diff options
| author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2015-05-20 09:56:07 +0000 |
|---|---|---|
| committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2015-05-20 09:56:07 +0000 |
| commit | 41386e9cb918eed93b3f13648cbef387e371e451 (patch) | |
| tree | a97f9d7bd1d9d091833286085f72da9d83fd0606 /Source/JavaScriptCore/bytecode | |
| parent | e15dd966d523731101f70ccf768bba12435a0208 (diff) | |
| download | WebKitGtk-tarball-41386e9cb918eed93b3f13648cbef387e371e451.tar.gz | |
webkitgtk-2.4.9webkitgtk-2.4.9
Diffstat (limited to 'Source/JavaScriptCore/bytecode')
119 files changed, 3367 insertions, 9480 deletions
diff --git a/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.cpp b/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.cpp index 905b5bd3c..4a008e083 100644 --- a/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.cpp +++ b/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.cpp @@ -26,7 +26,7 @@ #include "config.h" #include "ArrayAllocationProfile.h" -#include "JSCInlines.h" +#include "Operations.h" namespace JSC { @@ -49,7 +49,7 @@ void ArrayAllocationProfile::updateIndexingType() JSArray* lastArray = m_lastArray; if (!lastArray) return; - m_currentIndexingType = leastUpperBoundOfIndexingTypes(m_currentIndexingType, lastArray->indexingType()); + m_currentIndexingType = leastUpperBoundOfIndexingTypes(m_currentIndexingType, lastArray->structure()->indexingType()); m_lastArray = 0; } diff --git a/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.h b/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.h index f03763f70..f77b92a2f 100644 --- a/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.h +++ b/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.h @@ -42,7 +42,7 @@ public: IndexingType selectIndexingType() { JSArray* lastArray = m_lastArray; - if (lastArray && UNLIKELY(lastArray->indexingType() != m_currentIndexingType)) + if (lastArray && UNLIKELY(lastArray->structure()->indexingType() != m_currentIndexingType)) updateIndexingType(); return m_currentIndexingType; } diff --git a/Source/JavaScriptCore/bytecode/ArrayProfile.cpp b/Source/JavaScriptCore/bytecode/ArrayProfile.cpp index b8ade2223..4c055fea5 100644 --- a/Source/JavaScriptCore/bytecode/ArrayProfile.cpp +++ b/Source/JavaScriptCore/bytecode/ArrayProfile.cpp @@ -27,7 +27,6 @@ #include "ArrayProfile.h" #include "CodeBlock.h" -#include "JSCInlines.h" #include <wtf/CommaPrinter.h> #include <wtf/StringExtras.h> #include <wtf/StringPrintStream.h> @@ -73,53 +72,28 @@ void dumpArrayModes(PrintStream& out, ArrayModes arrayModes) out.print(comma, "ArrayWithArrayStorage"); if (arrayModes & asArrayModes(ArrayWithSlowPutArrayStorage)) out.print(comma, "ArrayWithSlowPutArrayStorage"); - - if (arrayModes & Int8ArrayMode) - out.print(comma, "Int8ArrayMode"); - if (arrayModes & Int16ArrayMode) - out.print(comma, "Int16ArrayMode"); - if (arrayModes & Int32ArrayMode) - out.print(comma, "Int32ArrayMode"); - if (arrayModes & Uint8ArrayMode) - out.print(comma, "Uint8ArrayMode"); - if (arrayModes & Uint8ClampedArrayMode) - out.print(comma, "Uint8ClampedArrayMode"); - if (arrayModes & Uint16ArrayMode) - out.print(comma, "Uint16ArrayMode"); - if (arrayModes & Uint32ArrayMode) - out.print(comma, "Uint32ArrayMode"); - if (arrayModes & Float32ArrayMode) - out.print(comma, "Float32ArrayMode"); - if (arrayModes & Float64ArrayMode) - out.print(comma, "Float64ArrayMode"); } -void ArrayProfile::computeUpdatedPrediction(const ConcurrentJITLocker& locker, CodeBlock* codeBlock) +void ArrayProfile::computeUpdatedPrediction(const ConcurrentJITLocker&, CodeBlock* codeBlock) { - if (!m_lastSeenStructureID) + if (!m_lastSeenStructure) return; - Structure* lastSeenStructure = codeBlock->heap()->structureIDTable().get(m_lastSeenStructureID); - computeUpdatedPrediction(locker, codeBlock, lastSeenStructure); - m_lastSeenStructureID = 0; -} - -void ArrayProfile::computeUpdatedPrediction(const ConcurrentJITLocker&, CodeBlock* codeBlock, Structure* lastSeenStructure) -{ - m_observedArrayModes |= arrayModeFromStructure(lastSeenStructure); + m_observedArrayModes |= arrayModeFromStructure(m_lastSeenStructure); if (!m_didPerformFirstRunPruning && hasTwoOrMoreBitsSet(m_observedArrayModes)) { - m_observedArrayModes = arrayModeFromStructure(lastSeenStructure); + m_observedArrayModes = arrayModeFromStructure(m_lastSeenStructure); m_didPerformFirstRunPruning = true; } m_mayInterceptIndexedAccesses |= - lastSeenStructure->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero(); + m_lastSeenStructure->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero(); JSGlobalObject* globalObject = codeBlock->globalObject(); - if (!globalObject->isOriginalArrayStructure(lastSeenStructure) - && !globalObject->isOriginalTypedArrayStructure(lastSeenStructure)) + if (!globalObject->isOriginalArrayStructure(m_lastSeenStructure) + && !globalObject->isOriginalTypedArrayStructure(m_lastSeenStructure)) m_usesOriginalArrayStructures = false; + m_lastSeenStructure = 0; } CString ArrayProfile::briefDescription(const ConcurrentJITLocker& locker, CodeBlock* codeBlock) diff --git a/Source/JavaScriptCore/bytecode/ArrayProfile.h b/Source/JavaScriptCore/bytecode/ArrayProfile.h index 66b295da7..c23230e06 100644 --- a/Source/JavaScriptCore/bytecode/ArrayProfile.h +++ b/Source/JavaScriptCore/bytecode/ArrayProfile.h @@ -37,44 +37,20 @@ namespace JSC { class CodeBlock; class LLIntOffsetsExtractor; -// This is a bitfield where each bit represents an type of array access that we have seen. -// There are 16 indexing types that use the lower bits. -// There are 9 typed array types taking the bits 16 to 25. +// This is a bitfield where each bit represents an IndexingType that we have seen. +// There are 32 indexing types, so an unsigned is enough. typedef unsigned ArrayModes; -const ArrayModes Int8ArrayMode = 1 << 16; -const ArrayModes Int16ArrayMode = 1 << 17; -const ArrayModes Int32ArrayMode = 1 << 18; -const ArrayModes Uint8ArrayMode = 1 << 19; -const ArrayModes Uint8ClampedArrayMode = 1 << 20; -const ArrayModes Uint16ArrayMode = 1 << 21; -const ArrayModes Uint32ArrayMode = 1 << 22; -const ArrayModes Float32ArrayMode = 1 << 23; -const ArrayModes Float64ArrayMode = 1 << 24; - #define asArrayModes(type) \ (static_cast<unsigned>(1) << static_cast<unsigned>(type)) -#define ALL_TYPED_ARRAY_MODES \ - (Int8ArrayMode \ - | Int16ArrayMode \ - | Int32ArrayMode \ - | Uint8ArrayMode \ - | Uint8ClampedArrayMode \ - | Uint16ArrayMode \ - | Uint32ArrayMode \ - | Float32ArrayMode \ - | Float64ArrayMode \ - ) - #define ALL_NON_ARRAY_ARRAY_MODES \ (asArrayModes(NonArray) \ | asArrayModes(NonArrayWithInt32) \ | asArrayModes(NonArrayWithDouble) \ | asArrayModes(NonArrayWithContiguous) \ | asArrayModes(NonArrayWithArrayStorage) \ - | asArrayModes(NonArrayWithSlowPutArrayStorage) \ - | ALL_TYPED_ARRAY_MODES) + | asArrayModes(NonArrayWithSlowPutArrayStorage)) #define ALL_ARRAY_ARRAY_MODES \ (asArrayModes(ArrayClass) \ @@ -89,29 +65,6 @@ const ArrayModes Float64ArrayMode = 1 << 24; inline ArrayModes arrayModeFromStructure(Structure* structure) { - switch (structure->classInfo()->typedArrayStorageType) { - case TypeInt8: - return Int8ArrayMode; - case TypeUint8: - return Uint8ArrayMode; - case TypeUint8Clamped: - return Uint8ClampedArrayMode; - case TypeInt16: - return Int16ArrayMode; - case TypeUint16: - return Uint16ArrayMode; - case TypeInt32: - return Int32ArrayMode; - case TypeUint32: - return Uint32ArrayMode; - case TypeFloat32: - return Float32ArrayMode; - case TypeFloat64: - return Float64ArrayMode; - case TypeDataView: - case NotTypedArray: - break; - } return asArrayModes(structure->indexingType()); } @@ -182,7 +135,7 @@ class ArrayProfile { public: ArrayProfile() : m_bytecodeOffset(std::numeric_limits<unsigned>::max()) - , m_lastSeenStructureID(0) + , m_lastSeenStructure(0) , m_mayStoreToHole(false) , m_outOfBounds(false) , m_mayInterceptIndexedAccesses(false) @@ -194,7 +147,7 @@ public: ArrayProfile(unsigned bytecodeOffset) : m_bytecodeOffset(bytecodeOffset) - , m_lastSeenStructureID(0) + , m_lastSeenStructure(0) , m_mayStoreToHole(false) , m_outOfBounds(false) , m_mayInterceptIndexedAccesses(false) @@ -206,20 +159,17 @@ public: unsigned bytecodeOffset() const { return m_bytecodeOffset; } - StructureID* addressOfLastSeenStructureID() { return &m_lastSeenStructureID; } + Structure** addressOfLastSeenStructure() { return &m_lastSeenStructure; } ArrayModes* addressOfArrayModes() { return &m_observedArrayModes; } bool* addressOfMayStoreToHole() { return &m_mayStoreToHole; } - - void setOutOfBounds() { m_outOfBounds = true; } bool* addressOfOutOfBounds() { return &m_outOfBounds; } void observeStructure(Structure* structure) { - m_lastSeenStructureID = structure->id(); + m_lastSeenStructure = structure; } void computeUpdatedPrediction(const ConcurrentJITLocker&, CodeBlock*); - void computeUpdatedPrediction(const ConcurrentJITLocker&, CodeBlock*, Structure* lastSeenStructure); ArrayModes observedArrayModes(const ConcurrentJITLocker&) const { return m_observedArrayModes; } bool mayInterceptIndexedAccesses(const ConcurrentJITLocker&) const { return m_mayInterceptIndexedAccesses; } @@ -238,7 +188,7 @@ private: static Structure* polymorphicStructure() { return static_cast<Structure*>(reinterpret_cast<void*>(1)); } unsigned m_bytecodeOffset; - StructureID m_lastSeenStructureID; + Structure* m_lastSeenStructure; bool m_mayStoreToHole; // This flag may become overloaded to indicate other special cases that were encountered during array access, as it depends on indexing type. Since we currently have basically just one indexing type (two variants of ArrayStorage), this flag for now just means exactly what its name implies. bool m_outOfBounds; bool m_mayInterceptIndexedAccesses : 1; @@ -247,7 +197,7 @@ private: ArrayModes m_observedArrayModes; }; -typedef SegmentedVector<ArrayProfile, 4> ArrayProfileVector; +typedef SegmentedVector<ArrayProfile, 4, 0> ArrayProfileVector; } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/ByValInfo.h b/Source/JavaScriptCore/bytecode/ByValInfo.h index b46a40d07..35fae0c60 100644 --- a/Source/JavaScriptCore/bytecode/ByValInfo.h +++ b/Source/JavaScriptCore/bytecode/ByValInfo.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2012 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,25 +26,23 @@ #ifndef ByValInfo_h #define ByValInfo_h +#include <wtf/Platform.h> + +#if ENABLE(JIT) + #include "ClassInfo.h" #include "CodeLocation.h" -#include "CodeOrigin.h" #include "IndexingType.h" #include "JITStubRoutine.h" #include "Structure.h" -#include "StructureStubInfo.h" namespace JSC { -#if ENABLE(JIT) - enum JITArrayMode { JITInt32, JITDouble, JITContiguous, JITArrayStorage, - JITDirectArguments, - JITScopedArguments, JITInt8Array, JITInt16Array, JITInt32Array, @@ -69,17 +67,6 @@ inline bool isOptimizableIndexingType(IndexingType indexingType) } } -inline bool hasOptimizableIndexingForJSType(JSType type) -{ - switch (type) { - case DirectArgumentsType: - case ScopedArgumentsType: - return true; - default: - return false; - } -} - inline bool hasOptimizableIndexingForClassInfo(const ClassInfo* classInfo) { return isTypedView(classInfo->typedArrayStorageType); @@ -88,7 +75,6 @@ inline bool hasOptimizableIndexingForClassInfo(const ClassInfo* classInfo) inline bool hasOptimizableIndexing(Structure* structure) { return isOptimizableIndexingType(structure->indexingType()) - || hasOptimizableIndexingForJSType(structure->typeInfo().type()) || hasOptimizableIndexingForClassInfo(structure->classInfo()); } @@ -109,19 +95,6 @@ inline JITArrayMode jitArrayModeForIndexingType(IndexingType indexingType) } } -inline JITArrayMode jitArrayModeForJSType(JSType type) -{ - switch (type) { - case DirectArgumentsType: - return JITDirectArguments; - case ScopedArgumentsType: - return JITScopedArguments; - default: - RELEASE_ASSERT_NOT_REACHED(); - return JITContiguous; - } -} - inline JITArrayMode jitArrayModeForClassInfo(const ClassInfo* classInfo) { switch (classInfo->typedArrayStorageType) { @@ -149,19 +122,6 @@ inline JITArrayMode jitArrayModeForClassInfo(const ClassInfo* classInfo) } } -inline bool jitArrayModePermitsPut(JITArrayMode mode) -{ - switch (mode) { - case JITDirectArguments: - case JITScopedArguments: - // We could support put_by_val on these at some point, but it's just not that profitable - // at the moment. - return false; - default: - return true; - } -} - inline TypedArrayType typedArrayTypeForJITArrayMode(JITArrayMode mode) { switch (mode) { @@ -194,44 +154,30 @@ inline JITArrayMode jitArrayModeForStructure(Structure* structure) if (isOptimizableIndexingType(structure->indexingType())) return jitArrayModeForIndexingType(structure->indexingType()); - if (hasOptimizableIndexingForJSType(structure->typeInfo().type())) - return jitArrayModeForJSType(structure->typeInfo().type()); - ASSERT(hasOptimizableIndexingForClassInfo(structure->classInfo())); return jitArrayModeForClassInfo(structure->classInfo()); } struct ByValInfo { ByValInfo() { } - - ByValInfo(unsigned bytecodeIndex, CodeLocationJump notIndexJump, CodeLocationJump badTypeJump, JITArrayMode arrayMode, ArrayProfile* arrayProfile, int16_t badTypeJumpToDone, int16_t badTypeJumpToNextHotPath, int16_t returnAddressToSlowPath) + + ByValInfo(unsigned bytecodeIndex, CodeLocationJump badTypeJump, JITArrayMode arrayMode, int16_t badTypeJumpToDone, int16_t returnAddressToSlowPath) : bytecodeIndex(bytecodeIndex) - , notIndexJump(notIndexJump) , badTypeJump(badTypeJump) , arrayMode(arrayMode) - , arrayProfile(arrayProfile) , badTypeJumpToDone(badTypeJumpToDone) - , badTypeJumpToNextHotPath(badTypeJumpToNextHotPath) , returnAddressToSlowPath(returnAddressToSlowPath) , slowPathCount(0) - , stubInfo(nullptr) - , tookSlowPath(false) { } - + unsigned bytecodeIndex; - CodeLocationJump notIndexJump; CodeLocationJump badTypeJump; JITArrayMode arrayMode; // The array mode that was baked into the inline JIT code. - ArrayProfile* arrayProfile; int16_t badTypeJumpToDone; - int16_t badTypeJumpToNextHotPath; int16_t returnAddressToSlowPath; unsigned slowPathCount; RefPtr<JITStubRoutine> stubRoutine; - Identifier cachedId; - StructureStubInfo* stubInfo; - bool tookSlowPath; }; inline unsigned getByValInfoBytecodeIndex(ByValInfo* info) @@ -239,15 +185,9 @@ inline unsigned getByValInfoBytecodeIndex(ByValInfo* info) return info->bytecodeIndex; } -typedef HashMap<CodeOrigin, ByValInfo*, CodeOriginApproximateHash> ByValInfoMap; - -#else // ENABLE(JIT) - -typedef HashMap<int, void*> ByValInfoMap; +} // namespace JSC #endif // ENABLE(JIT) -} // namespace JSC - #endif // ByValInfo_h diff --git a/Source/JavaScriptCore/bytecode/BytecodeBasicBlock.cpp b/Source/JavaScriptCore/bytecode/BytecodeBasicBlock.cpp index 54dfd168c..d7489d31a 100644 --- a/Source/JavaScriptCore/bytecode/BytecodeBasicBlock.cpp +++ b/Source/JavaScriptCore/bytecode/BytecodeBasicBlock.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,17 +27,10 @@ #include "BytecodeBasicBlock.h" #include "CodeBlock.h" -#include "JSCInlines.h" #include "PreciseJumpTargets.h" namespace JSC { -void BytecodeBasicBlock::shrinkToFit() -{ - m_bytecodeOffsets.shrinkToFit(); - m_successors.shrinkToFit(); -} - static bool isBranch(OpcodeID opcodeID) { switch (opcodeID) { @@ -58,6 +51,8 @@ static bool isBranch(OpcodeID opcodeID) case op_switch_imm: case op_switch_char: case op_switch_string: + case op_get_pnames: + case op_next_pname: case op_check_has_instance: return true; default: @@ -79,6 +74,7 @@ static bool isTerminal(OpcodeID opcodeID) { switch (opcodeID) { case op_ret: + case op_ret_object_or_this: case op_end: return true; default: @@ -97,36 +93,38 @@ static bool isThrow(OpcodeID opcodeID) } } -static bool isJumpTarget(OpcodeID opcodeID, const Vector<unsigned, 32>& jumpTargets, unsigned bytecodeOffset) +static bool isJumpTarget(OpcodeID opcodeID, Vector<unsigned, 32>& jumpTargets, unsigned bytecodeOffset) { if (opcodeID == op_catch) return true; - return std::binary_search(jumpTargets.begin(), jumpTargets.end(), bytecodeOffset); + for (unsigned i = 0; i < jumpTargets.size(); i++) { + if (bytecodeOffset == jumpTargets[i]) + return true; + } + return false; } static void linkBlocks(BytecodeBasicBlock* predecessor, BytecodeBasicBlock* successor) { predecessor->addSuccessor(successor); + successor->addPredecessor(predecessor); } -void computeBytecodeBasicBlocks(CodeBlock* codeBlock, Vector<std::unique_ptr<BytecodeBasicBlock>>& basicBlocks) +void computeBytecodeBasicBlocks(CodeBlock* codeBlock, Vector<RefPtr<BytecodeBasicBlock> >& basicBlocks) { Vector<unsigned, 32> jumpTargets; computePreciseJumpTargets(codeBlock, jumpTargets); // Create the entry and exit basic blocks. - basicBlocks.reserveCapacity(jumpTargets.size() + 2); - - auto entry = std::make_unique<BytecodeBasicBlock>(BytecodeBasicBlock::EntryBlock); - auto firstBlock = std::make_unique<BytecodeBasicBlock>(0, 0); - linkBlocks(entry.get(), firstBlock.get()); - - basicBlocks.append(WTF::move(entry)); - BytecodeBasicBlock* current = firstBlock.get(); - basicBlocks.append(WTF::move(firstBlock)); + BytecodeBasicBlock* entry = new BytecodeBasicBlock(BytecodeBasicBlock::EntryBlock); + basicBlocks.append(adoptRef(entry)); + BytecodeBasicBlock* exit = new BytecodeBasicBlock(BytecodeBasicBlock::ExitBlock); - auto exit = std::make_unique<BytecodeBasicBlock>(BytecodeBasicBlock::ExitBlock); + // Find basic block boundaries. + BytecodeBasicBlock* current = new BytecodeBasicBlock(0, 0); + linkBlocks(entry, current); + basicBlocks.append(adoptRef(current)); bool nextInstructionIsLeader = false; @@ -140,9 +138,9 @@ void computeBytecodeBasicBlocks(CodeBlock* codeBlock, Vector<std::unique_ptr<Byt bool createdBlock = false; // If the current bytecode is a jump target, then it's the leader of its own basic block. if (isJumpTarget(opcodeID, jumpTargets, bytecodeOffset) || nextInstructionIsLeader) { - auto newBlock = std::make_unique<BytecodeBasicBlock>(bytecodeOffset, opcodeLength); - current = newBlock.get(); - basicBlocks.append(WTF::move(newBlock)); + BytecodeBasicBlock* block = new BytecodeBasicBlock(bytecodeOffset, opcodeLength); + basicBlocks.append(adoptRef(block)); + current = block; createdBlock = true; nextInstructionIsLeader = false; bytecodeOffset += opcodeLength; @@ -175,7 +173,7 @@ void computeBytecodeBasicBlocks(CodeBlock* codeBlock, Vector<std::unique_ptr<Byt // If we found a terminal bytecode, link to the exit block. if (isTerminal(opcodeID)) { ASSERT(bytecodeOffset + opcodeLength == block->leaderBytecodeOffset() + block->totalBytecodeLength()); - linkBlocks(block, exit.get()); + linkBlocks(block, exit); fallsThrough = false; break; } @@ -188,7 +186,7 @@ void computeBytecodeBasicBlocks(CodeBlock* codeBlock, Vector<std::unique_ptr<Byt HandlerInfo* handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset); fallsThrough = false; if (!handler) { - linkBlocks(block, exit.get()); + linkBlocks(block, exit); break; } for (unsigned i = 0; i < basicBlocks.size(); i++) { @@ -229,10 +227,7 @@ void computeBytecodeBasicBlocks(CodeBlock* codeBlock, Vector<std::unique_ptr<Byt } } - basicBlocks.append(WTF::move(exit)); - - for (auto& basicBlock : basicBlocks) - basicBlock->shrinkToFit(); + basicBlocks.append(adoptRef(exit)); } } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/BytecodeBasicBlock.h b/Source/JavaScriptCore/bytecode/BytecodeBasicBlock.h index bd7d3ae9b..736ba8540 100644 --- a/Source/JavaScriptCore/bytecode/BytecodeBasicBlock.h +++ b/Source/JavaScriptCore/bytecode/BytecodeBasicBlock.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -36,13 +36,11 @@ namespace JSC { class CodeBlock; -class BytecodeBasicBlock { - WTF_MAKE_FAST_ALLOCATED; +class BytecodeBasicBlock : public RefCounted<BytecodeBasicBlock> { public: enum SpecialBlockType { EntryBlock, ExitBlock }; BytecodeBasicBlock(unsigned start, unsigned length); BytecodeBasicBlock(SpecialBlockType); - void shrinkToFit(); bool isEntryBlock() { return !m_leaderBytecodeOffset && !m_totalBytecodeLength; } bool isExitBlock() { return m_leaderBytecodeOffset == UINT_MAX && m_totalBytecodeLength == UINT_MAX; } @@ -53,9 +51,12 @@ public: Vector<unsigned>& bytecodeOffsets() { return m_bytecodeOffsets; } void addBytecodeLength(unsigned); - Vector<BytecodeBasicBlock*>& successors() { return m_successors; } + void addPredecessor(BytecodeBasicBlock* block) { m_predecessors.append(block); } void addSuccessor(BytecodeBasicBlock* block) { m_successors.append(block); } + Vector<BytecodeBasicBlock*>& predecessors() { return m_predecessors; } + Vector<BytecodeBasicBlock*>& successors() { return m_successors; } + FastBitVector& in() { return m_in; } FastBitVector& out() { return m_out; } @@ -64,13 +65,15 @@ private: unsigned m_totalBytecodeLength; Vector<unsigned> m_bytecodeOffsets; + + Vector<BytecodeBasicBlock*> m_predecessors; Vector<BytecodeBasicBlock*> m_successors; FastBitVector m_in; FastBitVector m_out; }; -void computeBytecodeBasicBlocks(CodeBlock*, Vector<std::unique_ptr<BytecodeBasicBlock>>&); +void computeBytecodeBasicBlocks(CodeBlock*, Vector<RefPtr<BytecodeBasicBlock> >&); inline BytecodeBasicBlock::BytecodeBasicBlock(unsigned start, unsigned length) : m_leaderBytecodeOffset(start) diff --git a/Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.cpp b/Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.cpp deleted file mode 100644 index 83f93c6dc..000000000 --- a/Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. - * - * 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. - */ - -#include "config.h" -#include "BytecodeIntrinsicRegistry.h" - -#include "CommonIdentifiers.h" -#include "Nodes.h" - -namespace JSC { - -#define INITIALISE_BYTECODE_INTRINSIC_NAMES_TO_SET(name) m_bytecodeIntrinsicMap.add(propertyNames.name##PrivateName.impl(), &BytecodeIntrinsicNode::emit_intrinsic_##name); - -BytecodeIntrinsicRegistry::BytecodeIntrinsicRegistry(const CommonIdentifiers& propertyNames) - : m_propertyNames(propertyNames) - , m_bytecodeIntrinsicMap() -{ - JSC_COMMON_BYTECODE_INTRINSICS_EACH_NAME(INITIALISE_BYTECODE_INTRINSIC_NAMES_TO_SET) -} - -BytecodeIntrinsicNode::EmitterType BytecodeIntrinsicRegistry::lookup(const Identifier& ident) const -{ - if (!m_propertyNames.isPrivateName(ident)) - return nullptr; - auto iterator = m_bytecodeIntrinsicMap.find(ident.impl()); - if (iterator == m_bytecodeIntrinsicMap.end()) - return nullptr; - return iterator->value; -} - -} // namespace JSC - diff --git a/Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h b/Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h deleted file mode 100644 index 87a578c16..000000000 --- a/Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. - * - * 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 BytecodeIntrinsicRegistry_h -#define BytecodeIntrinsicRegistry_h - -#include "Identifier.h" -#include <wtf/HashTable.h> -#include <wtf/Noncopyable.h> - -namespace JSC { - -class CommonIdentifiers; -class BytecodeGenerator; -class BytecodeIntrinsicNode; -class RegisterID; - -class BytecodeIntrinsicRegistry { - WTF_MAKE_NONCOPYABLE(BytecodeIntrinsicRegistry); -public: - explicit BytecodeIntrinsicRegistry(const CommonIdentifiers&); - - typedef RegisterID* (BytecodeIntrinsicNode::* EmitterType)(BytecodeGenerator&, RegisterID*); - - EmitterType lookup(const Identifier&) const; - -private: - const CommonIdentifiers& m_propertyNames; - HashMap<RefPtr<UniquedStringImpl>, EmitterType, IdentifierRepHash> m_bytecodeIntrinsicMap; -}; - -} // namespace JSC - -#endif // BytecodeIntrinsicRegistry_h diff --git a/Source/JavaScriptCore/bytecode/BytecodeKills.h b/Source/JavaScriptCore/bytecode/BytecodeKills.h deleted file mode 100644 index d073ded25..000000000 --- a/Source/JavaScriptCore/bytecode/BytecodeKills.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2015 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. 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 INC. 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 BytecodeKills_h -#define BytecodeKills_h - -#include "CodeBlock.h" -#include <wtf/FastBitVector.h> - -namespace JSC { - -class BytecodeLivenessAnalysis; - -class BytecodeKills { - WTF_MAKE_FAST_ALLOCATED; -public: - BytecodeKills() - : m_codeBlock(nullptr) - { - } - - // By convention, we say that non-local operands are never killed. - bool operandIsKilled(unsigned bytecodeIndex, int operand) const - { - ASSERT_WITH_SECURITY_IMPLICATION(bytecodeIndex < m_codeBlock->instructions().size()); - VirtualRegister reg(operand); - if (reg.isLocal()) - return m_killSets[bytecodeIndex].contains(operand); - return false; - } - - bool operandIsKilled(Instruction* instruction, int operand) const - { - return operandIsKilled(instruction - m_codeBlock->instructions().begin(), operand); - } - - template<typename Functor> - void forEachOperandKilledAt(unsigned bytecodeIndex, const Functor& functor) const - { - ASSERT_WITH_SECURITY_IMPLICATION(bytecodeIndex < m_codeBlock->instructions().size()); - m_killSets[bytecodeIndex].forEachLocal( - [&] (unsigned local) { - functor(virtualRegisterForLocal(local)); - }); - } - - template<typename Functor> - void forEachOperandKilledAt(Instruction* pc, const Functor& functor) const - { - forEachOperandKilledAt(pc - m_codeBlock->instructions().begin(), functor); - } - -private: - friend class BytecodeLivenessAnalysis; - - class KillSet { - public: - KillSet() - : m_word(0) - { - } - - ~KillSet() - { - if (hasVector()) - delete vector(); - } - - void add(unsigned local) - { - if (isEmpty()) { - setOneItem(local); - return; - } - if (hasOneItem()) { - ASSERT(oneItem() != local); - Vector<unsigned>* vector = new Vector<unsigned>(); - vector->append(oneItem()); - vector->append(local); - setVector(vector); - return; - } - ASSERT(!vector()->contains(local)); - vector()->append(local); - } - - template<typename Functor> - void forEachLocal(const Functor& functor) - { - if (isEmpty()) - return; - if (hasOneItem()) { - functor(oneItem()); - return; - } - for (unsigned local : *vector()) - functor(local); - } - - bool contains(unsigned expectedLocal) - { - if (isEmpty()) - return false; - if (hasOneItem()) - return oneItem() == expectedLocal; - for (unsigned local : *vector()) { - if (local == expectedLocal) - return true; - } - return false; - } - - private: - bool isEmpty() const - { - return !m_word; - } - - bool hasOneItem() const - { - return m_word & 1; - } - - unsigned oneItem() const - { - return m_word >> 1; - } - - void setOneItem(unsigned value) - { - m_word = (value << 1) | 1; - } - - bool hasVector() const - { - return !isEmpty() && !hasOneItem(); - } - - Vector<unsigned>* vector() - { - return bitwise_cast<Vector<unsigned>*>(m_word); - } - - void setVector(Vector<unsigned>* value) - { - m_word = bitwise_cast<uintptr_t>(value); - } - - uintptr_t m_word; - }; - - CodeBlock* m_codeBlock; - std::unique_ptr<KillSet[]> m_killSets; -}; - -} // namespace JSC - -#endif // BytecodeKills_h - diff --git a/Source/JavaScriptCore/bytecode/BytecodeList.json b/Source/JavaScriptCore/bytecode/BytecodeList.json deleted file mode 100644 index 2cf753bb8..000000000 --- a/Source/JavaScriptCore/bytecode/BytecodeList.json +++ /dev/null @@ -1,170 +0,0 @@ -[ - { - "section" : "Bytecodes", "emitInHFile" : true, "emitInASMFile" : true, - "macroNameComponent" : "BYTECODE", "asmPrefix" : "llint_", - "bytecodes" : [ - { "name" : "op_enter", "length" : 1 }, - { "name" : "op_get_scope", "length" : 2 }, - { "name" : "op_create_direct_arguments", "length" : 2 }, - { "name" : "op_create_scoped_arguments", "length" : 3 }, - { "name" : "op_create_out_of_band_arguments", "length" : 2 }, - { "name" : "op_create_this", "length" : 5 }, - { "name" : "op_to_this", "length" : 4 }, - { "name" : "op_check_tdz", "length" : 2 }, - { "name" : "op_new_object", "length" : 4 }, - { "name" : "op_new_array", "length" : 5 }, - { "name" : "op_new_array_with_size", "length" : 4 }, - { "name" : "op_new_array_buffer", "length" : 5 }, - { "name" : "op_new_regexp", "length" : 3 }, - { "name" : "op_mov", "length" : 3 }, - { "name" : "op_not", "length" : 3 }, - { "name" : "op_eq", "length" : 4 }, - { "name" : "op_eq_null", "length" : 3 }, - { "name" : "op_neq", "length" : 4 }, - { "name" : "op_neq_null", "length" : 3 }, - { "name" : "op_stricteq", "length" : 4 }, - { "name" : "op_nstricteq", "length" : 4 }, - { "name" : "op_less", "length" : 4 }, - { "name" : "op_lesseq", "length" : 4 }, - { "name" : "op_greater", "length" : 4 }, - { "name" : "op_greatereq", "length" : 4 }, - { "name" : "op_inc", "length" : 2 }, - { "name" : "op_dec", "length" : 2 }, - { "name" : "op_to_number", "length" : 3 }, - { "name" : "op_to_string", "length" : 3 }, - { "name" : "op_negate", "length" : 3 }, - { "name" : "op_add", "length" : 5 }, - { "name" : "op_mul", "length" : 5 }, - { "name" : "op_div", "length" : 5 }, - { "name" : "op_mod", "length" : 4 }, - { "name" : "op_sub", "length" : 5 }, - { "name" : "op_lshift", "length" : 4 }, - { "name" : "op_rshift", "length" : 4 }, - { "name" : "op_urshift", "length" : 4 }, - { "name" : "op_unsigned", "length" : 3 }, - { "name" : "op_bitand", "length" : 5 }, - { "name" : "op_bitxor", "length" : 5 }, - { "name" : "op_bitor", "length" : 5 }, - { "name" : "op_check_has_instance", "length" : 5 }, - { "name" : "op_instanceof", "length" : 4 }, - { "name" : "op_typeof", "length" : 3 }, - { "name" : "op_is_undefined", "length" : 3 }, - { "name" : "op_is_boolean", "length" : 3 }, - { "name" : "op_is_number", "length" : 3 }, - { "name" : "op_is_string", "length" : 3 }, - { "name" : "op_is_object", "length" : 3 }, - { "name" : "op_is_object_or_null", "length" : 3 }, - { "name" : "op_is_function", "length" : 3 }, - { "name" : "op_in", "length" : 4 }, - { "name" : "op_get_by_id", "length" : 9 }, - { "name" : "op_get_by_id_out_of_line", "length" : 9 }, - { "name" : "op_get_array_length", "length" : 9 }, - { "name" : "op_put_by_id", "length" : 9 }, - { "name" : "op_put_by_id_out_of_line", "length" : 9 }, - { "name" : "op_put_by_id_transition_direct", "length" : 9 }, - { "name" : "op_put_by_id_transition_direct_out_of_line", "length" : 9 }, - { "name" : "op_put_by_id_transition_normal", "length" : 9 }, - { "name" : "op_put_by_id_transition_normal_out_of_line", "length" : 9 }, - { "name" : "op_del_by_id", "length" : 4 }, - { "name" : "op_get_by_val", "length" : 6 }, - { "name" : "op_put_by_val", "length" : 5 }, - { "name" : "op_put_by_val_direct", "length" : 5 }, - { "name" : "op_del_by_val", "length" : 4 }, - { "name" : "op_put_by_index", "length" : 4 }, - { "name" : "op_put_getter_by_id", "length" : 4 }, - { "name" : "op_put_setter_by_id", "length" : 4 }, - { "name" : "op_put_getter_setter", "length" : 5 }, - { "name" : "op_jmp", "length" : 2 }, - { "name" : "op_jtrue", "length" : 3 }, - { "name" : "op_jfalse", "length" : 3 }, - { "name" : "op_jeq_null", "length" : 3 }, - { "name" : "op_jneq_null", "length" : 3 }, - { "name" : "op_jneq_ptr", "length" : 4 }, - { "name" : "op_jless", "length" : 4 }, - { "name" : "op_jlesseq", "length" : 4 }, - { "name" : "op_jgreater", "length" : 4 }, - { "name" : "op_jgreatereq", "length" : 4 }, - { "name" : "op_jnless", "length" : 4 }, - { "name" : "op_jnlesseq", "length" : 4 }, - { "name" : "op_jngreater", "length" : 4 }, - { "name" : "op_jngreatereq", "length" : 4 }, - { "name" : "op_loop_hint", "length" : 1 }, - { "name" : "op_switch_imm", "length" : 4 }, - { "name" : "op_switch_char", "length" : 4 }, - { "name" : "op_switch_string", "length" : 4 }, - { "name" : "op_new_func", "length" : 4 }, - { "name" : "op_new_func_exp", "length" : 4 }, - { "name" : "op_call", "length" : 9 }, - { "name" : "op_call_eval", "length" : 9 }, - { "name" : "op_call_varargs", "length" : 9 }, - { "name" : "op_ret", "length" : 2 }, - { "name" : "op_construct", "length" : 9 }, - { "name" : "op_construct_varargs", "length" : 9 }, - { "name" : "op_strcat", "length" : 4 }, - { "name" : "op_to_primitive", "length" : 3 }, - { "name" : "op_resolve_scope", "length" : 7 }, - { "name" : "op_get_from_scope", "length" : 8 }, - { "name" : "op_put_to_scope", "length" : 7 }, - { "name" : "op_get_from_arguments", "length" : 5 }, - { "name" : "op_put_to_arguments", "length" : 4 }, - { "name" : "op_push_with_scope", "length" : 4 }, - { "name" : "op_create_lexical_environment", "length" : 5 }, - { "name" : "op_get_parent_scope", "length" : 3 }, - { "name" : "op_catch", "length" : 3 }, - { "name" : "op_throw", "length" : 2 }, - { "name" : "op_throw_static_error", "length" : 3 }, - { "name" : "op_debug", "length" : 3 }, - { "name" : "op_profile_will_call", "length" : 2 }, - { "name" : "op_profile_did_call", "length" : 2 }, - { "name" : "op_end", "length" : 2 }, - { "name" : "op_profile_type", "length" : 6 }, - { "name" : "op_profile_control_flow", "length" : 2 }, - { "name" : "op_get_enumerable_length", "length" : 3 }, - { "name" : "op_has_indexed_property", "length" : 5 }, - { "name" : "op_has_structure_property", "length" : 5 }, - { "name" : "op_has_generic_property", "length" : 4 }, - { "name" : "op_get_direct_pname", "length" : 7 }, - { "name" : "op_get_property_enumerator", "length" : 3 }, - { "name" : "op_enumerator_structure_pname", "length" : 4 }, - { "name" : "op_enumerator_generic_pname", "length" : 4 }, - { "name" : "op_to_index_string", "length" : 3 } - ] - }, - { - "section" : "CLoopHelpers", "emitInHFile" : true, "emitInASMFile" : false, "defaultLength" : 1, - "macroNameComponent" : "CLOOP_BYTECODE_HELPER", - "bytecodes" : [ - { "name" : "llint_entry" }, - { "name" : "getHostCallReturnValue" }, - { "name" : "llint_return_to_host" }, - { "name" : "llint_vm_entry_to_javascript" }, - { "name" : "llint_vm_entry_to_native" }, - { "name" : "llint_cloop_did_return_from_js_1" }, - { "name" : "llint_cloop_did_return_from_js_2" }, - { "name" : "llint_cloop_did_return_from_js_3" }, - { "name" : "llint_cloop_did_return_from_js_4" }, - { "name" : "llint_cloop_did_return_from_js_5" }, - { "name" : "llint_cloop_did_return_from_js_6" }, - { "name" : "llint_cloop_did_return_from_js_7" }, - { "name" : "llint_cloop_did_return_from_js_8" } - ] - }, - { - "section" : "NativeHelpers", "emitInHFile" : true, "emitInASMFile" : true, "defaultLength" : 1, - "macroNameComponent" : "BYTECODE_HELPER", - "bytecodes" : [ - { "name" : "llint_program_prologue" }, - { "name" : "llint_eval_prologue" }, - { "name" : "llint_function_for_call_prologue" }, - { "name" : "llint_function_for_construct_prologue" }, - { "name" : "llint_function_for_call_arity_check" }, - { "name" : "llint_function_for_construct_arity_check" }, - { "name" : "llint_generic_return_point" }, - { "name" : "llint_throw_from_slow_path_trampoline" }, - { "name" : "llint_throw_during_call_trampoline" }, - { "name" : "llint_native_call_trampoline" }, - { "name" : "llint_native_construct_trampoline" }, - { "name" : "handleUncaughtException" } - ] - } -] diff --git a/Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysis.cpp b/Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysis.cpp index c77abeaa2..926334c44 100644 --- a/Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysis.cpp +++ b/Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysis.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,7 +26,6 @@ #include "config.h" #include "BytecodeLivenessAnalysis.h" -#include "BytecodeKills.h" #include "BytecodeLivenessAnalysisInlines.h" #include "BytecodeUseDef.h" #include "CodeBlock.h" @@ -48,17 +47,56 @@ static bool isValidRegisterForLiveness(CodeBlock* codeBlock, int operand) return false; VirtualRegister virtualReg(operand); - return virtualReg.isLocal(); + if (!virtualReg.isLocal()) + return false; + + if (codeBlock->captureCount() + && operand <= codeBlock->captureStart() + && operand > codeBlock->captureEnd()) + return false; + + return true; } -static unsigned getLeaderOffsetForBasicBlock(std::unique_ptr<BytecodeBasicBlock>* basicBlock) +static void setForOperand(CodeBlock* codeBlock, FastBitVector& bits, int operand) +{ + ASSERT(isValidRegisterForLiveness(codeBlock, operand)); + VirtualRegister virtualReg(operand); + if (virtualReg.offset() > codeBlock->captureStart()) + bits.set(virtualReg.toLocal()); + else + bits.set(virtualReg.toLocal() - codeBlock->captureCount()); +} + +namespace { + +class SetBit { +public: + SetBit(FastBitVector& bits) + : m_bits(bits) + { + } + + void operator()(CodeBlock* codeBlock, Instruction*, OpcodeID, int operand) + { + if (isValidRegisterForLiveness(codeBlock, operand)) + setForOperand(codeBlock, m_bits, operand); + } + +private: + FastBitVector& m_bits; +}; + +} // anonymous namespace + +static unsigned getLeaderOffsetForBasicBlock(RefPtr<BytecodeBasicBlock>* basicBlock) { return (*basicBlock)->leaderBytecodeOffset(); } -static BytecodeBasicBlock* findBasicBlockWithLeaderOffset(Vector<std::unique_ptr<BytecodeBasicBlock>>& basicBlocks, unsigned leaderOffset) +static BytecodeBasicBlock* findBasicBlockWithLeaderOffset(Vector<RefPtr<BytecodeBasicBlock> >& basicBlocks, unsigned leaderOffset) { - return (*tryBinarySearch<std::unique_ptr<BytecodeBasicBlock>, unsigned>(basicBlocks, basicBlocks.size(), leaderOffset, getLeaderOffsetForBasicBlock)).get(); + return (*tryBinarySearch<RefPtr<BytecodeBasicBlock>, unsigned>(basicBlocks, basicBlocks.size(), leaderOffset, getLeaderOffsetForBasicBlock)).get(); } static bool blockContainsBytecodeOffset(BytecodeBasicBlock* block, unsigned bytecodeOffset) @@ -67,7 +105,7 @@ static bool blockContainsBytecodeOffset(BytecodeBasicBlock* block, unsigned byte return bytecodeOffset >= leaderOffset && bytecodeOffset < leaderOffset + block->totalBytecodeLength(); } -static BytecodeBasicBlock* findBasicBlockForBytecodeOffset(Vector<std::unique_ptr<BytecodeBasicBlock>>& basicBlocks, unsigned bytecodeOffset) +static BytecodeBasicBlock* findBasicBlockForBytecodeOffset(Vector<RefPtr<BytecodeBasicBlock> >& basicBlocks, unsigned bytecodeOffset) { /* for (unsigned i = 0; i < basicBlocks.size(); i++) { @@ -76,7 +114,7 @@ static BytecodeBasicBlock* findBasicBlockForBytecodeOffset(Vector<std::unique_pt } return 0; */ - std::unique_ptr<BytecodeBasicBlock>* basicBlock = approximateBinarySearch<std::unique_ptr<BytecodeBasicBlock>, unsigned>( + RefPtr<BytecodeBasicBlock>* basicBlock = approximateBinarySearch<RefPtr<BytecodeBasicBlock>, unsigned>( basicBlocks, basicBlocks.size(), bytecodeOffset, getLeaderOffsetForBasicBlock); // We found the block we were looking for. if (blockContainsBytecodeOffset((*basicBlock).get(), bytecodeOffset)) @@ -95,82 +133,52 @@ static BytecodeBasicBlock* findBasicBlockForBytecodeOffset(Vector<std::unique_pt return basicBlock[1].get(); } -// Simplified interface to bytecode use/def, which determines defs first and then uses, and includes -// exception handlers in the uses. -template<typename UseFunctor, typename DefFunctor> -static void stepOverInstruction(CodeBlock* codeBlock, Vector<std::unique_ptr<BytecodeBasicBlock>>& basicBlocks, unsigned bytecodeOffset, const UseFunctor& use, const DefFunctor& def) +static void stepOverInstruction(CodeBlock* codeBlock, Vector<RefPtr<BytecodeBasicBlock>>& basicBlocks, unsigned bytecodeOffset, FastBitVector& uses, FastBitVector& defs, FastBitVector& out) { - // This abstractly execute the instruction in reverse. Instructions logically first use operands and - // then define operands. This logical ordering is necessary for operations that use and def the same - // operand, like: - // - // op_add loc1, loc1, loc2 - // - // The use of loc1 happens before the def of loc1. That's a semantic requirement since the add - // operation cannot travel forward in time to read the value that it will produce after reading that - // value. Since we are executing in reverse, this means that we must do defs before uses (reverse of - // uses before defs). - // - // Since this is a liveness analysis, this ordering ends up being particularly important: if we did - // uses before defs, then the add operation above would appear to not have loc1 live, since we'd - // first add it to the out set (the use), and then we'd remove it (the def). + uses.clearAll(); + defs.clearAll(); + + SetBit setUses(uses); + SetBit setDefs(defs); + computeUsesForBytecodeOffset(codeBlock, bytecodeOffset, setUses); + computeDefsForBytecodeOffset(codeBlock, bytecodeOffset, setDefs); + + out.exclude(defs); + out.merge(uses); - computeDefsForBytecodeOffset( - codeBlock, bytecodeOffset, - [&] (CodeBlock* codeBlock, Instruction*, OpcodeID, int operand) { - if (isValidRegisterForLiveness(codeBlock, operand)) - def(VirtualRegister(operand).toLocal()); - }); - - computeUsesForBytecodeOffset( - codeBlock, bytecodeOffset, - [&] (CodeBlock* codeBlock, Instruction*, OpcodeID, int operand) { - if (isValidRegisterForLiveness(codeBlock, operand)) - use(VirtualRegister(operand).toLocal()); - }); - // If we have an exception handler, we want the live-in variables of the // exception handler block to be included in the live-in of this particular bytecode. if (HandlerInfo* handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset)) { BytecodeBasicBlock* handlerBlock = findBasicBlockWithLeaderOffset(basicBlocks, handler->target); ASSERT(handlerBlock); - handlerBlock->in().forEachSetBit(use); + out.merge(handlerBlock->in()); } } -static void stepOverInstruction(CodeBlock* codeBlock, Vector<std::unique_ptr<BytecodeBasicBlock>>& basicBlocks, unsigned bytecodeOffset, FastBitVector& out) -{ - stepOverInstruction( - codeBlock, basicBlocks, bytecodeOffset, - [&] (unsigned bitIndex) { - // This is the use functor, so we set the bit. - out.set(bitIndex); - }, - [&] (unsigned bitIndex) { - // This is the def functor, so we clear the bit. - out.clear(bitIndex); - }); -} - -static void computeLocalLivenessForBytecodeOffset(CodeBlock* codeBlock, BytecodeBasicBlock* block, Vector<std::unique_ptr<BytecodeBasicBlock>>& basicBlocks, unsigned targetOffset, FastBitVector& result) +static void computeLocalLivenessForBytecodeOffset(CodeBlock* codeBlock, BytecodeBasicBlock* block, Vector<RefPtr<BytecodeBasicBlock> >& basicBlocks, unsigned targetOffset, FastBitVector& result) { ASSERT(!block->isExitBlock()); ASSERT(!block->isEntryBlock()); FastBitVector out = block->out(); + FastBitVector uses; + FastBitVector defs; + uses.resize(out.numBits()); + defs.resize(out.numBits()); + for (int i = block->bytecodeOffsets().size() - 1; i >= 0; i--) { unsigned bytecodeOffset = block->bytecodeOffsets()[i]; if (targetOffset > bytecodeOffset) break; - stepOverInstruction(codeBlock, basicBlocks, bytecodeOffset, out); + stepOverInstruction(codeBlock, basicBlocks, bytecodeOffset, uses, defs, out); } result.set(out); } -static void computeLocalLivenessForBlock(CodeBlock* codeBlock, BytecodeBasicBlock* block, Vector<std::unique_ptr<BytecodeBasicBlock>>& basicBlocks) +static void computeLocalLivenessForBlock(CodeBlock* codeBlock, BytecodeBasicBlock* block, Vector<RefPtr<BytecodeBasicBlock> >& basicBlocks) { if (block->isExitBlock() || block->isEntryBlock()) return; @@ -180,7 +188,8 @@ static void computeLocalLivenessForBlock(CodeBlock* codeBlock, BytecodeBasicBloc void BytecodeLivenessAnalysis::runLivenessFixpoint() { UnlinkedCodeBlock* unlinkedCodeBlock = m_codeBlock->unlinkedCodeBlock(); - unsigned numberOfVariables = unlinkedCodeBlock->m_numCalleeRegisters; + unsigned numberOfVariables = + unlinkedCodeBlock->m_numCalleeRegisters - m_codeBlock->captureCount(); for (unsigned i = 0; i < m_basicBlocks.size(); i++) { BytecodeBasicBlock* block = m_basicBlocks[i].get(); @@ -195,7 +204,7 @@ void BytecodeLivenessAnalysis::runLivenessFixpoint() newOut.resize(m_basicBlocks.last()->out().numBits()); do { changed = false; - for (unsigned i = m_basicBlocks.size() - 1; i--;) { + for (int i = m_basicBlocks.size() - 2; i >= 0; i--) { BytecodeBasicBlock* block = m_basicBlocks[i].get(); newOut.clearAll(); for (unsigned j = 0; j < block->successors().size(); j++) @@ -207,7 +216,7 @@ void BytecodeLivenessAnalysis::runLivenessFixpoint() } while (changed); } -void BytecodeLivenessAnalysis::getLivenessInfoAtBytecodeOffset(unsigned bytecodeOffset, FastBitVector& result) +void BytecodeLivenessAnalysis::getLivenessInfoForNonCapturedVarsAtBytecodeOffset(unsigned bytecodeOffset, FastBitVector& result) { BytecodeBasicBlock* block = findBasicBlockForBytecodeOffset(m_basicBlocks, bytecodeOffset); ASSERT(block); @@ -219,47 +228,60 @@ void BytecodeLivenessAnalysis::getLivenessInfoAtBytecodeOffset(unsigned bytecode bool BytecodeLivenessAnalysis::operandIsLiveAtBytecodeOffset(int operand, unsigned bytecodeOffset) { - if (operandIsAlwaysLive(operand)) + if (operandIsAlwaysLive(m_codeBlock, operand)) return true; FastBitVector result; - getLivenessInfoAtBytecodeOffset(bytecodeOffset, result); - return operandThatIsNotAlwaysLiveIsLive(result, operand); + getLivenessInfoForNonCapturedVarsAtBytecodeOffset(bytecodeOffset, result); + return operandThatIsNotAlwaysLiveIsLive(m_codeBlock, result, operand); } -FastBitVector BytecodeLivenessAnalysis::getLivenessInfoAtBytecodeOffset(unsigned bytecodeOffset) +FastBitVector getLivenessInfo(CodeBlock* codeBlock, const FastBitVector& out) { - FastBitVector out; - getLivenessInfoAtBytecodeOffset(bytecodeOffset, out); - return out; -} + FastBitVector result; -void BytecodeLivenessAnalysis::computeFullLiveness(FullBytecodeLiveness& result) -{ - FastBitVector out; - - result.m_map.resize(m_codeBlock->instructions().size()); - - for (unsigned i = m_basicBlocks.size(); i--;) { - BytecodeBasicBlock* block = m_basicBlocks[i].get(); - if (block->isEntryBlock() || block->isExitBlock()) + unsigned numCapturedVars = codeBlock->captureCount(); + if (numCapturedVars) { + int firstCapturedLocal = VirtualRegister(codeBlock->captureStart()).toLocal(); + result.resize(out.numBits() + numCapturedVars); + for (unsigned i = 0; i < numCapturedVars; ++i) + result.set(firstCapturedLocal + i); + } else + result.resize(out.numBits()); + + int outLength = out.numBits(); + ASSERT(outLength >= 0); + for (int i = 0; i < outLength; i++) { + if (!out.get(i)) + continue; + + if (!numCapturedVars) { + result.set(i); continue; - - out = block->out(); - - for (unsigned i = block->bytecodeOffsets().size(); i--;) { - unsigned bytecodeOffset = block->bytecodeOffsets()[i]; - stepOverInstruction(m_codeBlock, m_basicBlocks, bytecodeOffset, out); - result.m_map[bytecodeOffset] = out; } + + if (virtualRegisterForLocal(i).offset() > codeBlock->captureStart()) + result.set(i); + else + result.set(numCapturedVars + i); } + return result; +} + +FastBitVector BytecodeLivenessAnalysis::getLivenessInfoAtBytecodeOffset(unsigned bytecodeOffset) +{ + FastBitVector out; + getLivenessInfoForNonCapturedVarsAtBytecodeOffset(bytecodeOffset, out); + return getLivenessInfo(m_codeBlock, out); } -void BytecodeLivenessAnalysis::computeKills(BytecodeKills& result) +void BytecodeLivenessAnalysis::computeFullLiveness(FullBytecodeLiveness& result) { FastBitVector out; + FastBitVector uses; + FastBitVector defs; result.m_codeBlock = m_codeBlock; - result.m_killSets = std::make_unique<BytecodeKills::KillSet[]>(m_codeBlock->instructions().size()); + result.m_map.clear(); for (unsigned i = m_basicBlocks.size(); i--;) { BytecodeBasicBlock* block = m_basicBlocks[i].get(); @@ -267,22 +289,13 @@ void BytecodeLivenessAnalysis::computeKills(BytecodeKills& result) continue; out = block->out(); + uses.resize(out.numBits()); + defs.resize(out.numBits()); for (unsigned i = block->bytecodeOffsets().size(); i--;) { unsigned bytecodeOffset = block->bytecodeOffsets()[i]; - stepOverInstruction( - m_codeBlock, m_basicBlocks, bytecodeOffset, - [&] (unsigned index) { - // This is for uses. - if (out.get(index)) - return; - result.m_killSets[bytecodeOffset].add(index); - out.set(index); - }, - [&] (unsigned index) { - // This is for defs. - out.clear(index); - }); + stepOverInstruction(m_codeBlock, m_basicBlocks, bytecodeOffset, uses, defs, out); + result.m_map.add(bytecodeOffset, out); } } } @@ -294,6 +307,12 @@ void BytecodeLivenessAnalysis::dumpResults() for (unsigned i = 0; i < m_basicBlocks.size(); i++) { BytecodeBasicBlock* block = m_basicBlocks[i].get(); dataLogF("\nBytecode basic block %u: %p (offset: %u, length: %u)\n", i, block, block->leaderBytecodeOffset(), block->totalBytecodeLength()); + dataLogF("Predecessors: "); + for (unsigned j = 0; j < block->predecessors().size(); j++) { + BytecodeBasicBlock* predecessor = block->predecessors()[j]; + dataLogF("%p ", predecessor); + } + dataLogF("\n"); dataLogF("Successors: "); for (unsigned j = 0; j < block->successors().size(); j++) { BytecodeBasicBlock* successor = block->successors()[j]; diff --git a/Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysis.h b/Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysis.h index ece16f21f..349912175 100644 --- a/Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysis.h +++ b/Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysis.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,13 +33,10 @@ namespace JSC { -class BytecodeKills; class CodeBlock; class FullBytecodeLiveness; class BytecodeLivenessAnalysis { - WTF_MAKE_FAST_ALLOCATED; - WTF_MAKE_NONCOPYABLE(BytecodeLivenessAnalysis); public: BytecodeLivenessAnalysis(CodeBlock*); @@ -47,22 +44,23 @@ public: FastBitVector getLivenessInfoAtBytecodeOffset(unsigned bytecodeOffset); void computeFullLiveness(FullBytecodeLiveness& result); - void computeKills(BytecodeKills& result); private: void compute(); void runLivenessFixpoint(); void dumpResults(); - void getLivenessInfoAtBytecodeOffset(unsigned bytecodeOffset, FastBitVector&); + void getLivenessInfoForNonCapturedVarsAtBytecodeOffset(unsigned bytecodeOffset, FastBitVector&); CodeBlock* m_codeBlock; - Vector<std::unique_ptr<BytecodeBasicBlock>> m_basicBlocks; + Vector<RefPtr<BytecodeBasicBlock> > m_basicBlocks; }; -inline bool operandIsAlwaysLive(int operand); -inline bool operandThatIsNotAlwaysLiveIsLive(const FastBitVector& out, int operand); -inline bool operandIsLive(const FastBitVector& out, int operand); +inline bool operandIsAlwaysLive(CodeBlock*, int operand); +inline bool operandThatIsNotAlwaysLiveIsLive(CodeBlock*, const FastBitVector& out, int operand); +inline bool operandIsLive(CodeBlock*, const FastBitVector& out, int operand); + +FastBitVector getLivenessInfo(CodeBlock*, const FastBitVector& out); } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysisInlines.h b/Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysisInlines.h index 9b5c755fc..8824bd85c 100644 --- a/Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysisInlines.h +++ b/Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysisInlines.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,26 +28,30 @@ #include "BytecodeLivenessAnalysis.h" #include "CodeBlock.h" -#include "Operations.h" namespace JSC { -inline bool operandIsAlwaysLive(int operand) +inline bool operandIsAlwaysLive(CodeBlock* codeBlock, int operand) { - return !VirtualRegister(operand).isLocal(); + if (VirtualRegister(operand).isArgument()) + return true; + return operand <= codeBlock->captureStart() && operand > codeBlock->captureEnd(); } -inline bool operandThatIsNotAlwaysLiveIsLive(const FastBitVector& out, int operand) +inline bool operandThatIsNotAlwaysLiveIsLive(CodeBlock* codeBlock, const FastBitVector& out, int operand) { - unsigned local = VirtualRegister(operand).toLocal(); - if (local >= out.numBits()) + VirtualRegister virtualReg(operand); + if (virtualReg.offset() > codeBlock->captureStart()) + return out.get(virtualReg.toLocal()); + size_t index = virtualReg.toLocal() - codeBlock->captureCount(); + if (index >= out.numBits()) return false; - return out.get(local); + return out.get(index); } -inline bool operandIsLive(const FastBitVector& out, int operand) +inline bool operandIsLive(CodeBlock* codeBlock, const FastBitVector& out, int operand) { - return operandIsAlwaysLive(operand) || operandThatIsNotAlwaysLiveIsLive(out, operand); + return operandIsAlwaysLive(codeBlock, operand) || operandThatIsNotAlwaysLiveIsLive(codeBlock, out, operand); } } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/BytecodeUseDef.h b/Source/JavaScriptCore/bytecode/BytecodeUseDef.h index 0383a0fbd..45cb91a1c 100644 --- a/Source/JavaScriptCore/bytecode/BytecodeUseDef.h +++ b/Source/JavaScriptCore/bytecode/BytecodeUseDef.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,7 +32,7 @@ namespace JSC { template<typename Functor> void computeUsesForBytecodeOffset( - CodeBlock* codeBlock, unsigned bytecodeOffset, const Functor& functor) + CodeBlock* codeBlock, unsigned bytecodeOffset, Functor& functor) { Interpreter* interpreter = codeBlock->vm()->interpreter; Instruction* instructionsBegin = codeBlock->instructions().begin(); @@ -44,23 +44,29 @@ void computeUsesForBytecodeOffset( case op_new_array_buffer: case op_throw_static_error: case op_debug: + case op_resolve_scope: + case op_pop_scope: case op_jneq_ptr: + case op_new_func_exp: case op_loop_hint: case op_jmp: case op_new_object: + case op_init_lazy_reg: + case op_get_callee: case op_enter: case op_catch: - case op_profile_control_flow: - case op_create_direct_arguments: - case op_create_out_of_band_arguments: + case op_touch_entry: return; - case op_get_scope: + case op_new_func: + case op_new_captured_func: + case op_create_activation: + case op_create_arguments: case op_to_this: - case op_check_tdz: + case op_tear_off_activation: case op_profile_will_call: case op_profile_did_call: - case op_profile_type: case op_throw: + case op_push_with_scope: case op_end: case op_ret: case op_jtrue: @@ -72,6 +78,7 @@ void computeUsesForBytecodeOffset( functor(codeBlock, instruction, opcodeID, instruction[1].u.operand); return; } + case op_ret_object_or_this: case op_jlesseq: case op_jgreater: case op_jgreatereq: @@ -92,16 +99,16 @@ void computeUsesForBytecodeOffset( return; } case op_put_by_index: + case op_put_by_id_replace: + case op_put_by_id_transition: case op_put_by_id_transition_direct: case op_put_by_id_transition_direct_out_of_line: case op_put_by_id_transition_normal: case op_put_by_id_transition_normal_out_of_line: + case op_put_by_id_generic: case op_put_by_id_out_of_line: case op_put_by_id: - case op_put_getter_by_id: - case op_put_setter_by_id: - case op_put_to_scope: - case op_put_to_arguments: { + case op_put_to_scope: { functor(codeBlock, instruction, opcodeID, instruction[1].u.operand); functor(codeBlock, instruction, opcodeID, instruction[3].u.operand); return; @@ -112,48 +119,50 @@ void computeUsesForBytecodeOffset( functor(codeBlock, instruction, opcodeID, instruction[4].u.operand); return; } - case op_get_property_enumerator: - case op_get_enumerable_length: - case op_new_func_exp: - case op_to_index_string: - case op_create_lexical_environment: - case op_resolve_scope: + case op_init_global_const_nop: + case op_init_global_const: + case op_push_name_scope: case op_get_from_scope: case op_to_primitive: case op_get_by_id: case op_get_by_id_out_of_line: + case op_get_by_id_self: + case op_get_by_id_proto: + case op_get_by_id_chain: + case op_get_by_id_getter_self: + case op_get_by_id_getter_proto: + case op_get_by_id_getter_chain: + case op_get_by_id_custom_self: + case op_get_by_id_custom_proto: + case op_get_by_id_custom_chain: + case op_get_by_id_generic: case op_get_array_length: + case op_get_string_length: + case op_get_arguments_length: case op_typeof: case op_is_undefined: case op_is_boolean: case op_is_number: case op_is_string: case op_is_object: - case op_is_object_or_null: case op_is_function: case op_to_number: - case op_to_string: case op_negate: case op_neq_null: case op_eq_null: case op_not: case op_mov: + case op_captured_mov: case op_new_array_with_size: case op_create_this: + case op_get_pnames: case op_del_by_id: - case op_unsigned: - case op_new_func: - case op_get_parent_scope: - case op_create_scoped_arguments: - case op_get_from_arguments: { + case op_unsigned: { functor(codeBlock, instruction, opcodeID, instruction[2].u.operand); return; } - case op_has_generic_property: - case op_has_indexed_property: - case op_enumerator_structure_pname: - case op_enumerator_generic_pname: case op_get_by_val: + case op_get_argument_by_val: case op_in: case op_instanceof: case op_check_has_instance: @@ -176,27 +185,32 @@ void computeUsesForBytecodeOffset( case op_stricteq: case op_neq: case op_eq: - case op_push_with_scope: case op_del_by_val: { functor(codeBlock, instruction, opcodeID, instruction[2].u.operand); functor(codeBlock, instruction, opcodeID, instruction[3].u.operand); return; } - case op_has_structure_property: - case op_construct_varargs: case op_call_varargs: { functor(codeBlock, instruction, opcodeID, instruction[2].u.operand); functor(codeBlock, instruction, opcodeID, instruction[3].u.operand); functor(codeBlock, instruction, opcodeID, instruction[4].u.operand); return; } - case op_get_direct_pname: { + case op_next_pname: { functor(codeBlock, instruction, opcodeID, instruction[2].u.operand); functor(codeBlock, instruction, opcodeID, instruction[3].u.operand); functor(codeBlock, instruction, opcodeID, instruction[4].u.operand); functor(codeBlock, instruction, opcodeID, instruction[5].u.operand); return; } + case op_get_by_pname: { + functor(codeBlock, instruction, opcodeID, instruction[2].u.operand); + functor(codeBlock, instruction, opcodeID, instruction[3].u.operand); + functor(codeBlock, instruction, opcodeID, instruction[4].u.operand); + functor(codeBlock, instruction, opcodeID, instruction[5].u.operand); + functor(codeBlock, instruction, opcodeID, instruction[6].u.operand); + return; + } case op_switch_string: case op_switch_char: case op_switch_imm: { @@ -218,10 +232,16 @@ void computeUsesForBytecodeOffset( int argCount = instruction[3].u.operand; int registerOffset = -instruction[4].u.operand; int lastArg = registerOffset + CallFrame::thisArgumentOffset(); - for (int i = 0; i < argCount; i++) + for (int i = opcodeID == op_construct ? 1 : 0; i < argCount; i++) functor(codeBlock, instruction, opcodeID, lastArg + i); return; } + case op_tear_off_arguments: { + functor(codeBlock, instruction, opcodeID, instruction[1].u.operand); + functor(codeBlock, instruction, opcodeID, unmodifiedArgumentsRegister(VirtualRegister(instruction[1].u.operand)).offset()); + functor(codeBlock, instruction, opcodeID, instruction[2].u.operand); + return; + } default: RELEASE_ASSERT_NOT_REACHED(); break; @@ -229,7 +249,7 @@ void computeUsesForBytecodeOffset( } template<typename Functor> -void computeDefsForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecodeOffset, const Functor& functor) +void computeDefsForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecodeOffset, Functor& functor) { Interpreter* interpreter = codeBlock->vm()->interpreter; Instruction* instructionsBegin = codeBlock->instructions().begin(); @@ -237,7 +257,12 @@ void computeDefsForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecodeOffset, OpcodeID opcodeID = interpreter->getOpcodeID(instruction->u.opcode); switch (opcodeID) { // These don't define anything. + case op_init_global_const: + case op_init_global_const_nop: + case op_push_name_scope: + case op_push_with_scope: case op_put_to_scope: + case op_pop_scope: case op_end: case op_profile_will_call: case op_profile_did_call: @@ -245,6 +270,7 @@ void computeDefsForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecodeOffset, case op_throw_static_error: case op_debug: case op_ret: + case op_ret_object_or_this: case op_jmp: case op_jtrue: case op_jfalse: @@ -265,69 +291,72 @@ void computeDefsForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecodeOffset, case op_switch_string: case op_put_by_id: case op_put_by_id_out_of_line: + case op_put_by_id_replace: + case op_put_by_id_transition: case op_put_by_id_transition_direct: case op_put_by_id_transition_direct_out_of_line: case op_put_by_id_transition_normal: case op_put_by_id_transition_normal_out_of_line: - case op_put_getter_by_id: - case op_put_setter_by_id: + case op_put_by_id_generic: case op_put_getter_setter: case op_put_by_val: case op_put_by_val_direct: case op_put_by_index: - case op_profile_type: - case op_profile_control_flow: - case op_put_to_arguments: + case op_tear_off_arguments: + case op_touch_entry: #define LLINT_HELPER_OPCODES(opcode, length) case opcode: FOR_EACH_LLINT_OPCODE_EXTENSION(LLINT_HELPER_OPCODES); #undef LLINT_HELPER_OPCODES return; // These all have a single destination for the first argument. - case op_to_index_string: - case op_get_enumerable_length: - case op_has_indexed_property: - case op_has_structure_property: - case op_has_generic_property: - case op_get_direct_pname: - case op_get_property_enumerator: - case op_enumerator_structure_pname: - case op_enumerator_generic_pname: - case op_get_parent_scope: - case op_push_with_scope: - case op_create_lexical_environment: + case op_next_pname: case op_resolve_scope: case op_strcat: + case op_tear_off_activation: case op_to_primitive: + case op_catch: case op_create_this: case op_new_array: case op_new_array_buffer: case op_new_array_with_size: case op_new_regexp: case op_new_func: + case op_new_captured_func: case op_new_func_exp: case op_call_varargs: - case op_construct_varargs: case op_get_from_scope: case op_call: case op_call_eval: case op_construct: case op_get_by_id: case op_get_by_id_out_of_line: + case op_get_by_id_self: + case op_get_by_id_proto: + case op_get_by_id_chain: + case op_get_by_id_getter_self: + case op_get_by_id_getter_proto: + case op_get_by_id_getter_chain: + case op_get_by_id_custom_self: + case op_get_by_id_custom_proto: + case op_get_by_id_custom_chain: + case op_get_by_id_generic: case op_get_array_length: + case op_get_string_length: case op_check_has_instance: case op_instanceof: case op_get_by_val: + case op_get_argument_by_val: + case op_get_by_pname: + case op_get_arguments_length: case op_typeof: case op_is_undefined: case op_is_boolean: case op_is_number: case op_is_string: case op_is_object: - case op_is_object_or_null: case op_is_function: case op_in: case op_to_number: - case op_to_string: case op_negate: case op_add: case op_mul: @@ -354,23 +383,23 @@ void computeDefsForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecodeOffset, case op_eq_null: case op_not: case op_mov: + case op_captured_mov: case op_new_object: case op_to_this: - case op_check_tdz: - case op_get_scope: - case op_create_direct_arguments: - case op_create_scoped_arguments: - case op_create_out_of_band_arguments: + case op_get_callee: + case op_init_lazy_reg: + case op_create_activation: + case op_create_arguments: case op_del_by_id: case op_del_by_val: - case op_unsigned: - case op_get_from_arguments: { + case op_unsigned: { functor(codeBlock, instruction, opcodeID, instruction[1].u.operand); return; } - case op_catch: { + case op_get_pnames: { functor(codeBlock, instruction, opcodeID, instruction[1].u.operand); - functor(codeBlock, instruction, opcodeID, instruction[2].u.operand); + functor(codeBlock, instruction, opcodeID, instruction[3].u.operand); + functor(codeBlock, instruction, opcodeID, instruction[4].u.operand); return; } case op_enter: { diff --git a/Source/JavaScriptCore/bytecode/CallEdge.cpp b/Source/JavaScriptCore/bytecode/CallEdge.cpp deleted file mode 100644 index dffff6dfd..000000000 --- a/Source/JavaScriptCore/bytecode/CallEdge.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2014 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. - */ - -#include "config.h" -#include "CallEdge.h" - -namespace JSC { - -void CallEdge::dump(PrintStream& out) const -{ - out.print("<", m_callee, ", count: ", m_count, ">"); -} - -} // namespace JSC - diff --git a/Source/JavaScriptCore/bytecode/CallEdge.h b/Source/JavaScriptCore/bytecode/CallEdge.h deleted file mode 100644 index 304520951..000000000 --- a/Source/JavaScriptCore/bytecode/CallEdge.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2014, 2015 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 CallEdge_h -#define CallEdge_h - -#include "CallVariant.h" - -namespace JSC { - -class CallEdge { -public: - CallEdge(); - CallEdge(CallVariant, uint32_t); - - bool operator!() const { return !m_callee; } - - CallVariant callee() const { return m_callee; } - uint32_t count() const { return m_count; } - - CallEdge despecifiedClosure() const - { - return CallEdge(m_callee.despecifiedClosure(), m_count); - } - - void dump(PrintStream&) const; - -private: - CallVariant m_callee; - uint32_t m_count; -}; - -inline CallEdge::CallEdge(CallVariant callee, uint32_t count) - : m_callee(callee) - , m_count(count) -{ -} - -inline CallEdge::CallEdge() - : CallEdge(CallVariant(), 0) -{ -} - -typedef Vector<CallEdge, 1> CallEdgeList; - -} // namespace JSC - -#endif // CallEdge_h - diff --git a/Source/JavaScriptCore/bytecode/CallLinkInfo.cpp b/Source/JavaScriptCore/bytecode/CallLinkInfo.cpp index 7292f7364..a4baa6100 100644 --- a/Source/JavaScriptCore/bytecode/CallLinkInfo.cpp +++ b/Source/JavaScriptCore/bytecode/CallLinkInfo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2013, 2014 Apple Inc. All rights reserved. + * Copyright (C) 2012 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,79 +28,33 @@ #include "DFGOperations.h" #include "DFGThunks.h" -#include "JSCInlines.h" -#include "Repatch.h" #include "RepatchBuffer.h" -#include <wtf/ListDump.h> -#include <wtf/NeverDestroyed.h> #if ENABLE(JIT) namespace JSC { -void CallLinkInfo::clearStub() +void CallLinkInfo::unlink(VM& vm, RepatchBuffer& repatchBuffer) { - if (!stub()) - return; - - m_stub->clearCallNodesFor(this); - m_stub = nullptr; -} - -void CallLinkInfo::unlink(RepatchBuffer& repatchBuffer) -{ - if (!isLinked()) { - // We could be called even if we're not linked anymore because of how polymorphic calls - // work. Each callsite within the polymorphic call stub may separately ask us to unlink(). - RELEASE_ASSERT(!isOnList()); - return; - } + ASSERT(isLinked()); - unlinkFor(repatchBuffer, *this); + repatchBuffer.revertJumpReplacementToBranchPtrWithPatch(RepatchBuffer::startOfBranchPtrWithPatchOnRegister(hotPathBegin), static_cast<MacroAssembler::RegisterID>(calleeGPR), 0); + if (isDFG) { +#if ENABLE(DFG_JIT) + repatchBuffer.relink(callReturnLocation, (callType == Construct ? vm.getCTIStub(linkConstructThunkGenerator) : vm.getCTIStub(linkCallThunkGenerator)).code()); +#else + RELEASE_ASSERT_NOT_REACHED(); +#endif + } else + repatchBuffer.relink(callReturnLocation, callType == Construct ? vm.getCTIStub(linkConstructThunkGenerator).code() : vm.getCTIStub(linkCallThunkGenerator).code()); + hasSeenShouldRepatch = false; + callee.clear(); + stub.clear(); // It will be on a list if the callee has a code block. if (isOnList()) remove(); } -void CallLinkInfo::visitWeak(RepatchBuffer& repatchBuffer) -{ - auto handleSpecificCallee = [&] (JSFunction* callee) { - if (Heap::isMarked(callee->executable())) - m_hasSeenClosure = true; - else - m_clearedByGC = true; - }; - - if (isLinked()) { - if (stub()) { - if (!stub()->visitWeak(repatchBuffer)) { - if (Options::verboseOSR()) { - dataLog( - "Clearing closure call from ", *repatchBuffer.codeBlock(), " to ", - listDump(stub()->variants()), ", stub routine ", RawPointer(stub()), - ".\n"); - } - unlink(repatchBuffer); - m_clearedByGC = true; - } - } else if (!Heap::isMarked(m_callee.get())) { - if (Options::verboseOSR()) { - dataLog( - "Clearing call from ", *repatchBuffer.codeBlock(), " to ", - RawPointer(m_callee.get()), " (", - m_callee.get()->executable()->hashFor(specializationKind()), - ").\n"); - } - handleSpecificCallee(m_callee.get()); - unlink(repatchBuffer); - } - } - if (haveLastSeenCallee() && !Heap::isMarked(lastSeenCallee())) { - handleSpecificCallee(lastSeenCallee()); - clearLastSeenCallee(); - } -} - } // namespace JSC #endif // ENABLE(JIT) diff --git a/Source/JavaScriptCore/bytecode/CallLinkInfo.h b/Source/JavaScriptCore/bytecode/CallLinkInfo.h index 061c6f83e..0244497df 100644 --- a/Source/JavaScriptCore/bytecode/CallLinkInfo.h +++ b/Source/JavaScriptCore/bytecode/CallLinkInfo.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2014, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2012 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,13 +26,14 @@ #ifndef CallLinkInfo_h #define CallLinkInfo_h +#include "ClosureCallStubRoutine.h" #include "CodeLocation.h" #include "CodeSpecializationKind.h" #include "JITWriteBarrier.h" #include "JSFunction.h" #include "Opcode.h" -#include "PolymorphicCallStubRoutine.h" #include "WriteBarrier.h" +#include <wtf/Platform.h> #include <wtf/SentinelLinkedList.h> namespace JSC { @@ -41,275 +42,73 @@ namespace JSC { class RepatchBuffer; -class CallLinkInfo : public BasicRawSentinelNode<CallLinkInfo> { -public: - enum CallType { None, Call, CallVarargs, Construct, ConstructVarargs }; +struct CallLinkInfo : public BasicRawSentinelNode<CallLinkInfo> { + enum CallType { None, Call, CallVarargs, Construct }; static CallType callTypeFor(OpcodeID opcodeID) { if (opcodeID == op_call || opcodeID == op_call_eval) return Call; if (opcodeID == op_construct) return Construct; - if (opcodeID == op_construct_varargs) - return ConstructVarargs; ASSERT(opcodeID == op_call_varargs); return CallVarargs; } - + CallLinkInfo() - : m_registerPreservationMode(static_cast<unsigned>(RegisterPreservationNotRequired)) - , m_hasSeenShouldRepatch(false) - , m_hasSeenClosure(false) - , m_clearedByGC(false) - , m_callType(None) - , m_maxNumArguments(0) - , m_slowPathCount(0) + : hasSeenShouldRepatch(false) + , isDFG(false) + , hasSeenClosure(false) + , callType(None) { } ~CallLinkInfo() { - clearStub(); - if (isOnList()) remove(); } - static CodeSpecializationKind specializationKindFor(CallType callType) - { - return specializationFromIsConstruct(callType == Construct || callType == ConstructVarargs); - } CodeSpecializationKind specializationKind() const { - return specializationKindFor(static_cast<CallType>(m_callType)); - } - - RegisterPreservationMode registerPreservationMode() const - { - return static_cast<RegisterPreservationMode>(m_registerPreservationMode); - } - - bool isLinked() { return m_stub || m_callee; } - void unlink(RepatchBuffer&); - - void setUpCall(CallType callType, CodeOrigin codeOrigin, unsigned calleeGPR) - { - m_callType = callType; - m_codeOrigin = codeOrigin; - m_calleeGPR = calleeGPR; - } - - void setCallLocations(CodeLocationNearCall callReturnLocation, CodeLocationDataLabelPtr hotPathBegin, - CodeLocationNearCall hotPathOther) - { - m_callReturnLocation = callReturnLocation; - m_hotPathBegin = hotPathBegin; - m_hotPathOther = hotPathOther; - } - - void setUpCallFromFTL(CallType callType, CodeOrigin codeOrigin, - CodeLocationNearCall callReturnLocation, CodeLocationDataLabelPtr hotPathBegin, - CodeLocationNearCall hotPathOther, unsigned calleeGPR) - { - m_registerPreservationMode = static_cast<unsigned>(MustPreserveRegisters); - m_callType = callType; - m_codeOrigin = codeOrigin; - m_callReturnLocation = callReturnLocation; - m_hotPathBegin = hotPathBegin; - m_hotPathOther = hotPathOther; - m_calleeGPR = calleeGPR; - } - - CodeLocationNearCall callReturnLocation() - { - return m_callReturnLocation; - } - - CodeLocationDataLabelPtr hotPathBegin() - { - return m_hotPathBegin; - } - - CodeLocationNearCall hotPathOther() - { - return m_hotPathOther; - } - - void setCallee(VM& vm, CodeLocationDataLabelPtr location, JSCell* owner, JSFunction* callee) - { - m_callee.set(vm, location, owner, callee); - } - - void clearCallee() - { - m_callee.clear(); - } - - JSFunction* callee() - { - return m_callee.get(); - } - - void setLastSeenCallee(VM& vm, const JSCell* owner, JSFunction* callee) - { - m_lastSeenCallee.set(vm, owner, callee); - } - - void clearLastSeenCallee() - { - m_lastSeenCallee.clear(); + return specializationFromIsConstruct(callType == Construct); } - JSFunction* lastSeenCallee() - { - return m_lastSeenCallee.get(); - } - - bool haveLastSeenCallee() - { - return !!m_lastSeenCallee; - } - - void setStub(PassRefPtr<PolymorphicCallStubRoutine> newStub) - { - clearStub(); - m_stub = newStub; - } - - void clearStub(); + CodeLocationNearCall callReturnLocation; + CodeLocationDataLabelPtr hotPathBegin; + CodeLocationNearCall hotPathOther; + JITWriteBarrier<JSFunction> callee; + WriteBarrier<JSFunction> lastSeenCallee; + RefPtr<ClosureCallStubRoutine> stub; + bool hasSeenShouldRepatch : 1; + bool isDFG : 1; + bool hasSeenClosure : 1; + unsigned callType : 5; // CallType + unsigned calleeGPR : 8; + CodeOrigin codeOrigin; - PolymorphicCallStubRoutine* stub() - { - return m_stub.get(); - } - - void setSlowStub(PassRefPtr<JITStubRoutine> newSlowStub) - { - m_slowStub = newSlowStub; - } - - void clearSlowStub() - { - m_slowStub = nullptr; - } - - JITStubRoutine* slowStub() - { - return m_slowStub.get(); - } + bool isLinked() { return stub || callee; } + void unlink(VM&, RepatchBuffer&); bool seenOnce() { - return m_hasSeenShouldRepatch; - } - - void clearSeen() - { - m_hasSeenShouldRepatch = false; + return hasSeenShouldRepatch; } void setSeen() { - m_hasSeenShouldRepatch = true; - } - - bool hasSeenClosure() - { - return m_hasSeenClosure; - } - - void setHasSeenClosure() - { - m_hasSeenClosure = true; - } - - bool clearedByGC() - { - return m_clearedByGC; - } - - void setCallType(CallType callType) - { - m_callType = callType; + hasSeenShouldRepatch = true; } - - CallType callType() - { - return static_cast<CallType>(m_callType); - } - - uint8_t* addressOfMaxNumArguments() - { - return &m_maxNumArguments; - } - - uint8_t maxNumArguments() - { - return m_maxNumArguments; - } - - static ptrdiff_t offsetOfSlowPathCount() - { - return OBJECT_OFFSETOF(CallLinkInfo, m_slowPathCount); - } - - void setCalleeGPR(unsigned calleeGPR) - { - m_calleeGPR = calleeGPR; - } - - unsigned calleeGPR() - { - return m_calleeGPR; - } - - uint32_t slowPathCount() - { - return m_slowPathCount; - } - - void setCodeOrigin(CodeOrigin codeOrigin) - { - m_codeOrigin = codeOrigin; - } - - CodeOrigin codeOrigin() - { - return m_codeOrigin; - } - - void visitWeak(RepatchBuffer&); - -private: - CodeLocationNearCall m_callReturnLocation; - CodeLocationDataLabelPtr m_hotPathBegin; - CodeLocationNearCall m_hotPathOther; - JITWriteBarrier<JSFunction> m_callee; - WriteBarrier<JSFunction> m_lastSeenCallee; - RefPtr<PolymorphicCallStubRoutine> m_stub; - RefPtr<JITStubRoutine> m_slowStub; - unsigned m_registerPreservationMode : 1; // Real type is RegisterPreservationMode - bool m_hasSeenShouldRepatch : 1; - bool m_hasSeenClosure : 1; - bool m_clearedByGC : 1; - unsigned m_callType : 4; // CallType - unsigned m_calleeGPR : 8; - uint8_t m_maxNumArguments; // Only used for varargs calls. - uint32_t m_slowPathCount; - CodeOrigin m_codeOrigin; }; -inline CodeOrigin getCallLinkInfoCodeOrigin(CallLinkInfo& callLinkInfo) +inline void* getCallLinkInfoReturnLocation(CallLinkInfo* callLinkInfo) { - return callLinkInfo.codeOrigin(); + return callLinkInfo->callReturnLocation.executableAddress(); } -typedef HashMap<CodeOrigin, CallLinkInfo*, CodeOriginApproximateHash> CallLinkInfoMap; - -#else // ENABLE(JIT) - -typedef HashMap<int, void*> CallLinkInfoMap; - +inline unsigned getCallLinkInfoBytecodeIndex(CallLinkInfo* callLinkInfo) +{ + return callLinkInfo->codeOrigin.bytecodeIndex; +} #endif // ENABLE(JIT) } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/CallLinkStatus.cpp b/Source/JavaScriptCore/bytecode/CallLinkStatus.cpp index 103a7f2b5..b64c967e9 100644 --- a/Source/JavaScriptCore/bytecode/CallLinkStatus.cpp +++ b/Source/JavaScriptCore/bytecode/CallLinkStatus.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2015 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,294 +26,103 @@ #include "config.h" #include "CallLinkStatus.h" -#include "CallLinkInfo.h" #include "CodeBlock.h" -#include "DFGJITCode.h" #include "LLIntCallLinkInfo.h" -#include "JSCInlines.h" +#include "Operations.h" #include <wtf/CommaPrinter.h> -#include <wtf/ListDump.h> namespace JSC { -static const bool verbose = false; - CallLinkStatus::CallLinkStatus(JSValue value) - : m_couldTakeSlowPath(false) + : m_callTarget(value) + , m_executable(0) + , m_structure(0) + , m_couldTakeSlowPath(false) , m_isProved(false) { - if (!value || !value.isCell()) { - m_couldTakeSlowPath = true; + if (!value || !value.isCell()) + return; + + m_structure = value.asCell()->structure(); + + if (!value.asCell()->inherits(JSFunction::info())) return; - } - m_variants.append(CallVariant(value.asCell())); + m_executable = jsCast<JSFunction*>(value.asCell())->executable(); } -CallLinkStatus CallLinkStatus::computeFromLLInt(const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, unsigned bytecodeIndex) +JSFunction* CallLinkStatus::function() const { - UNUSED_PARAM(profiledBlock); - UNUSED_PARAM(bytecodeIndex); -#if ENABLE(DFG_JIT) - if (profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCell))) { - // We could force this to be a closure call, but instead we'll just assume that it - // takes slow path. - return takesSlowPath(); - } -#else - UNUSED_PARAM(locker); -#endif - - VM& vm = *profiledBlock->vm(); + if (!m_callTarget || !m_callTarget.isCell()) + return 0; - Instruction* instruction = profiledBlock->instructions().begin() + bytecodeIndex; - OpcodeID op = vm.interpreter->getOpcodeID(instruction[0].u.opcode); - if (op != op_call && op != op_construct) - return CallLinkStatus(); - - LLIntCallLinkInfo* callLinkInfo = instruction[5].u.callLinkInfo; + if (!m_callTarget.asCell()->inherits(JSFunction::info())) + return 0; - return CallLinkStatus(callLinkInfo->lastSeenCallee.get()); + return jsCast<JSFunction*>(m_callTarget.asCell()); } -CallLinkStatus CallLinkStatus::computeFor( - CodeBlock* profiledBlock, unsigned bytecodeIndex, const CallLinkInfoMap& map) +InternalFunction* CallLinkStatus::internalFunction() const { - ConcurrentJITLocker locker(profiledBlock->m_lock); - - UNUSED_PARAM(profiledBlock); - UNUSED_PARAM(bytecodeIndex); - UNUSED_PARAM(map); -#if ENABLE(DFG_JIT) - ExitSiteData exitSiteData = computeExitSiteData(locker, profiledBlock, bytecodeIndex); + if (!m_callTarget || !m_callTarget.isCell()) + return 0; - CallLinkInfo* callLinkInfo = map.get(CodeOrigin(bytecodeIndex)); - if (!callLinkInfo) { - if (exitSiteData.m_takesSlowPath) - return takesSlowPath(); - return computeFromLLInt(locker, profiledBlock, bytecodeIndex); - } + if (!m_callTarget.asCell()->inherits(InternalFunction::info())) + return 0; - return computeFor(locker, profiledBlock, *callLinkInfo, exitSiteData); -#else - return CallLinkStatus(); -#endif + return jsCast<InternalFunction*>(m_callTarget.asCell()); } -CallLinkStatus::ExitSiteData CallLinkStatus::computeExitSiteData( - const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, unsigned bytecodeIndex) +Intrinsic CallLinkStatus::intrinsicFor(CodeSpecializationKind kind) const { - ExitSiteData exitSiteData; - -#if ENABLE(DFG_JIT) - exitSiteData.m_takesSlowPath = - profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadType)) - || profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadExecutable)); - exitSiteData.m_badFunction = - profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCell)); -#else - UNUSED_PARAM(locker); - UNUSED_PARAM(profiledBlock); - UNUSED_PARAM(bytecodeIndex); -#endif + if (!m_executable) + return NoIntrinsic; - return exitSiteData; + return m_executable->intrinsicFor(kind); } -#if ENABLE(JIT) -CallLinkStatus CallLinkStatus::computeFor( - const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, CallLinkInfo& callLinkInfo) +CallLinkStatus CallLinkStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned bytecodeIndex) { - // We don't really need this, but anytime we have to debug this code, it becomes indispensable. UNUSED_PARAM(profiledBlock); + UNUSED_PARAM(bytecodeIndex); +#if ENABLE(LLINT) + Instruction* instruction = profiledBlock->instructions().begin() + bytecodeIndex; + LLIntCallLinkInfo* callLinkInfo = instruction[5].u.callLinkInfo; - CallLinkStatus result = computeFromCallLinkInfo(locker, callLinkInfo); - result.m_maxNumArguments = callLinkInfo.maxNumArguments(); - return result; + return CallLinkStatus(callLinkInfo->lastSeenCallee.get()); +#else + return CallLinkStatus(); +#endif } -CallLinkStatus CallLinkStatus::computeFromCallLinkInfo( - const ConcurrentJITLocker&, CallLinkInfo& callLinkInfo) +CallLinkStatus CallLinkStatus::computeFor(CodeBlock* profiledBlock, unsigned bytecodeIndex) { - if (callLinkInfo.clearedByGC()) - return takesSlowPath(); - - // Note that despite requiring that the locker is held, this code is racy with respect - // to the CallLinkInfo: it may get cleared while this code runs! This is because - // CallLinkInfo::unlink() may be called from a different CodeBlock than the one that owns - // the CallLinkInfo and currently we save space by not having CallLinkInfos know who owns - // them. So, there is no way for either the caller of CallLinkInfo::unlock() or unlock() - // itself to figure out which lock to lock. - // - // Fortunately, that doesn't matter. The only things we ask of CallLinkInfo - the slow - // path count, the stub, and the target - can all be asked racily. Stubs and targets can - // only be deleted at next GC, so if we load a non-null one, then it must contain data - // that is still marginally valid (i.e. the pointers ain't stale). This kind of raciness - // is probably OK for now. + ConcurrentJITLocker locker(profiledBlock->m_lock); - // PolymorphicCallStubRoutine is a GCAwareJITStubRoutine, so if non-null, it will stay alive - // until next GC even if the CallLinkInfo is concurrently cleared. Also, the variants list is - // never mutated after the PolymorphicCallStubRoutine is instantiated. We have some conservative - // fencing in place to make sure that we see the variants list after construction. - if (PolymorphicCallStubRoutine* stub = callLinkInfo.stub()) { - WTF::loadLoadFence(); - - CallEdgeList edges = stub->edges(); - - // Now that we've loaded the edges list, there are no further concurrency concerns. We will - // just manipulate and prune this list to our liking - mostly removing entries that are too - // infrequent and ensuring that it's sorted in descending order of frequency. - - RELEASE_ASSERT(edges.size()); - - std::sort( - edges.begin(), edges.end(), - [] (CallEdge a, CallEdge b) { - return a.count() > b.count(); - }); - RELEASE_ASSERT(edges.first().count() >= edges.last().count()); - - double totalCallsToKnown = 0; - double totalCallsToUnknown = callLinkInfo.slowPathCount(); - CallVariantList variants; - for (size_t i = 0; i < edges.size(); ++i) { - CallEdge edge = edges[i]; - // If the call is at the tail of the distribution, then we don't optimize it and we - // treat it as if it was a call to something unknown. We define the tail as being either - // a call that doesn't belong to the N most frequent callees (N = - // maxPolymorphicCallVariantsForInlining) or that has a total call count that is too - // small. - if (i >= Options::maxPolymorphicCallVariantsForInlining() - || edge.count() < Options::frequentCallThreshold()) - totalCallsToUnknown += edge.count(); - else { - totalCallsToKnown += edge.count(); - variants.append(edge.callee()); - } - } - - // Bail if we didn't find any calls that qualified. - RELEASE_ASSERT(!!totalCallsToKnown == !!variants.size()); - if (variants.isEmpty()) - return takesSlowPath(); - - // We require that the distribution of callees is skewed towards a handful of common ones. - if (totalCallsToKnown / totalCallsToUnknown < Options::minimumCallToKnownRate()) - return takesSlowPath(); - - RELEASE_ASSERT(totalCallsToKnown); - RELEASE_ASSERT(variants.size()); - - CallLinkStatus result; - result.m_variants = variants; - result.m_couldTakeSlowPath = !!totalCallsToUnknown; - return result; - } + UNUSED_PARAM(profiledBlock); + UNUSED_PARAM(bytecodeIndex); +#if ENABLE(JIT) + if (!profiledBlock->hasBaselineJITProfiling()) + return computeFromLLInt(profiledBlock, bytecodeIndex); - CallLinkStatus result; + if (profiledBlock->couldTakeSlowCase(bytecodeIndex)) + return CallLinkStatus::takesSlowPath(); - if (JSFunction* target = callLinkInfo.lastSeenCallee()) { - CallVariant variant(target); - if (callLinkInfo.hasSeenClosure()) - variant = variant.despecifiedClosure(); - result.m_variants.append(variant); - } + CallLinkInfo& callLinkInfo = profiledBlock->getCallLinkInfo(bytecodeIndex); + if (callLinkInfo.stub) + return CallLinkStatus(callLinkInfo.stub->executable(), callLinkInfo.stub->structure()); - result.m_couldTakeSlowPath = !!callLinkInfo.slowPathCount(); - - return result; -} - -CallLinkStatus CallLinkStatus::computeFor( - const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, CallLinkInfo& callLinkInfo, - ExitSiteData exitSiteData) -{ - CallLinkStatus result = computeFor(locker, profiledBlock, callLinkInfo); - if (exitSiteData.m_badFunction) - result.makeClosureCall(); - if (exitSiteData.m_takesSlowPath) - result.m_couldTakeSlowPath = true; + JSFunction* target = callLinkInfo.lastSeenCallee.get(); + if (!target) + return computeFromLLInt(profiledBlock, bytecodeIndex); - return result; -} -#endif + if (callLinkInfo.hasSeenClosure) + return CallLinkStatus(target->executable(), target->structure()); -void CallLinkStatus::computeDFGStatuses( - CodeBlock* dfgCodeBlock, CallLinkStatus::ContextMap& map) -{ -#if ENABLE(DFG_JIT) - RELEASE_ASSERT(dfgCodeBlock->jitType() == JITCode::DFGJIT); - CodeBlock* baselineCodeBlock = dfgCodeBlock->alternative(); - for (auto iter = dfgCodeBlock->callLinkInfosBegin(); !!iter; ++iter) { - CallLinkInfo& info = **iter; - CodeOrigin codeOrigin = info.codeOrigin(); - - // Check if we had already previously made a terrible mistake in the FTL for this - // code origin. Note that this is approximate because we could have a monovariant - // inline in the FTL that ended up failing. We should fix that at some point by - // having data structures to track the context of frequent exits. This is currently - // challenging because it would require creating a CodeOrigin-based database in - // baseline CodeBlocks, but those CodeBlocks don't really have a place to put the - // InlineCallFrames. - CodeBlock* currentBaseline = - baselineCodeBlockForOriginAndBaselineCodeBlock(codeOrigin, baselineCodeBlock); - ExitSiteData exitSiteData; - { - ConcurrentJITLocker locker(currentBaseline->m_lock); - exitSiteData = computeExitSiteData( - locker, currentBaseline, codeOrigin.bytecodeIndex); - } - - { - ConcurrentJITLocker locker(dfgCodeBlock->m_lock); - map.add(info.codeOrigin(), computeFor(locker, dfgCodeBlock, info, exitSiteData)); - } - } + return CallLinkStatus(target); #else - UNUSED_PARAM(dfgCodeBlock); -#endif // ENABLE(DFG_JIT) - - if (verbose) { - dataLog("Context map:\n"); - ContextMap::iterator iter = map.begin(); - ContextMap::iterator end = map.end(); - for (; iter != end; ++iter) { - dataLog(" ", iter->key, ":\n"); - dataLog(" ", iter->value, "\n"); - } - } -} - -CallLinkStatus CallLinkStatus::computeFor( - CodeBlock* profiledBlock, CodeOrigin codeOrigin, - const CallLinkInfoMap& baselineMap, const CallLinkStatus::ContextMap& dfgMap) -{ - auto iter = dfgMap.find(codeOrigin); - if (iter != dfgMap.end()) - return iter->value; - - return computeFor(profiledBlock, codeOrigin.bytecodeIndex, baselineMap); -} - -void CallLinkStatus::setProvenConstantCallee(CallVariant variant) -{ - m_variants = CallVariantList{ variant }; - m_couldTakeSlowPath = false; - m_isProved = true; -} - -bool CallLinkStatus::isClosureCall() const -{ - for (unsigned i = m_variants.size(); i--;) { - if (m_variants[i].isClosureCall()) - return true; - } - return false; -} - -void CallLinkStatus::makeClosureCall() -{ - m_variants = despecifiedVariantList(m_variants); + return CallLinkStatus(); +#endif } void CallLinkStatus::dump(PrintStream& out) const @@ -331,11 +140,17 @@ void CallLinkStatus::dump(PrintStream& out) const if (m_couldTakeSlowPath) out.print(comma, "Could Take Slow Path"); - if (!m_variants.isEmpty()) - out.print(comma, listDump(m_variants)); + if (m_callTarget) + out.print(comma, "Known target: ", m_callTarget); + + if (m_executable) { + out.print(comma, "Executable/CallHash: ", RawPointer(m_executable)); + if (!isCompilationThread()) + out.print("/", m_executable->hashFor(CodeForCall)); + } - if (m_maxNumArguments) - out.print(comma, "maxNumArguments = ", m_maxNumArguments); + if (m_structure) + out.print(comma, "Structure: ", RawPointer(m_structure)); } } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/CallLinkStatus.h b/Source/JavaScriptCore/bytecode/CallLinkStatus.h index 271d8b6e1..51965fe4a 100644 --- a/Source/JavaScriptCore/bytecode/CallLinkStatus.h +++ b/Source/JavaScriptCore/bytecode/CallLinkStatus.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2015 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,12 +26,7 @@ #ifndef CallLinkStatus_h #define CallLinkStatus_h -#include "CallLinkInfo.h" -#include "CallVariant.h" -#include "CodeOrigin.h" #include "CodeSpecializationKind.h" -#include "ConcurrentJITLock.h" -#include "ExitingJITType.h" #include "Intrinsic.h" #include "JSCJSValue.h" @@ -42,13 +37,13 @@ class ExecutableBase; class InternalFunction; class JSFunction; class Structure; -class CallLinkInfo; class CallLinkStatus { - WTF_MAKE_FAST_ALLOCATED; public: CallLinkStatus() - : m_couldTakeSlowPath(false) + : m_executable(0) + , m_structure(0) + , m_couldTakeSlowPath(false) , m_isProved(false) { } @@ -62,81 +57,75 @@ public: explicit CallLinkStatus(JSValue); - CallLinkStatus(CallVariant variant) - : m_variants(1, variant) + CallLinkStatus(ExecutableBase* executable, Structure* structure) + : m_executable(executable) + , m_structure(structure) , m_couldTakeSlowPath(false) , m_isProved(false) { + ASSERT(!!executable == !!structure); } - static CallLinkStatus computeFor( - CodeBlock*, unsigned bytecodeIndex, const CallLinkInfoMap&); - - struct ExitSiteData { - ExitSiteData() - : m_takesSlowPath(false) - , m_badFunction(false) - { - } - - bool m_takesSlowPath; - bool m_badFunction; - }; - static ExitSiteData computeExitSiteData(const ConcurrentJITLocker&, CodeBlock*, unsigned bytecodeIndex); - -#if ENABLE(JIT) - // Computes the status assuming that we never took slow path and never previously - // exited. - static CallLinkStatus computeFor(const ConcurrentJITLocker&, CodeBlock*, CallLinkInfo&); - static CallLinkStatus computeFor( - const ConcurrentJITLocker&, CodeBlock*, CallLinkInfo&, ExitSiteData); -#endif + CallLinkStatus& setIsProved(bool isProved) + { + m_isProved = isProved; + return *this; + } - typedef HashMap<CodeOrigin, CallLinkStatus, CodeOriginApproximateHash> ContextMap; + static CallLinkStatus computeFor(CodeBlock*, unsigned bytecodeIndex); - // Computes all of the statuses of the DFG code block. Doesn't include statuses that had - // no information. Currently we use this when compiling FTL code, to enable polyvariant - // inlining. - static void computeDFGStatuses(CodeBlock* dfgCodeBlock, ContextMap&); + CallLinkStatus& setHasBadFunctionExitSite(bool didHaveExitSite) + { + ASSERT(!m_isProved); + if (didHaveExitSite) { + // Turn this into a closure call. + m_callTarget = JSValue(); + } + return *this; + } - // Helper that first consults the ContextMap and then does computeFor(). - static CallLinkStatus computeFor( - CodeBlock*, CodeOrigin, const CallLinkInfoMap&, const ContextMap&); + CallLinkStatus& setHasBadCacheExitSite(bool didHaveExitSite) + { + ASSERT(!m_isProved); + if (didHaveExitSite) + *this = takesSlowPath(); + return *this; + } - void setProvenConstantCallee(CallVariant); + CallLinkStatus& setHasBadExecutableExitSite(bool didHaveExitSite) + { + ASSERT(!m_isProved); + if (didHaveExitSite) + *this = takesSlowPath(); + return *this; + } - bool isSet() const { return !m_variants.isEmpty() || m_couldTakeSlowPath; } + bool isSet() const { return m_callTarget || m_executable || m_couldTakeSlowPath; } bool operator!() const { return !isSet(); } bool couldTakeSlowPath() const { return m_couldTakeSlowPath; } - - CallVariantList variants() const { return m_variants; } - unsigned size() const { return m_variants.size(); } - CallVariant at(unsigned i) const { return m_variants[i]; } - CallVariant operator[](unsigned i) const { return at(i); } + bool isClosureCall() const { return m_executable && !m_callTarget; } + + JSValue callTarget() const { return m_callTarget; } + JSFunction* function() const; + InternalFunction* internalFunction() const; + Intrinsic intrinsicFor(CodeSpecializationKind) const; + ExecutableBase* executable() const { return m_executable; } + Structure* structure() const { return m_structure; } bool isProved() const { return m_isProved; } - bool canOptimize() const { return !m_variants.isEmpty(); } - - bool isClosureCall() const; // Returns true if any callee is a closure call. - - unsigned maxNumArguments() const { return m_maxNumArguments; } + bool canOptimize() const { return (m_callTarget || m_executable) && !m_couldTakeSlowPath; } void dump(PrintStream&) const; private: - void makeClosureCall(); - - static CallLinkStatus computeFromLLInt(const ConcurrentJITLocker&, CodeBlock*, unsigned bytecodeIndex); -#if ENABLE(JIT) - static CallLinkStatus computeFromCallLinkInfo( - const ConcurrentJITLocker&, CallLinkInfo&); -#endif + static CallLinkStatus computeFromLLInt(CodeBlock*, unsigned bytecodeIndex); - CallVariantList m_variants; + JSValue m_callTarget; + ExecutableBase* m_executable; + Structure* m_structure; bool m_couldTakeSlowPath; bool m_isProved; - unsigned m_maxNumArguments; }; } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/CallReturnOffsetToBytecodeOffset.h b/Source/JavaScriptCore/bytecode/CallReturnOffsetToBytecodeOffset.h index 496738f09..3a7448efd 100644 --- a/Source/JavaScriptCore/bytecode/CallReturnOffsetToBytecodeOffset.h +++ b/Source/JavaScriptCore/bytecode/CallReturnOffsetToBytecodeOffset.h @@ -26,6 +26,8 @@ #ifndef CallReturnOffsetToBytecodeOffset_h #define CallReturnOffsetToBytecodeOffset_h +#include <wtf/Platform.h> + namespace JSC { #if ENABLE(JIT) diff --git a/Source/JavaScriptCore/bytecode/CallVariant.cpp b/Source/JavaScriptCore/bytecode/CallVariant.cpp deleted file mode 100644 index 9745dde2b..000000000 --- a/Source/JavaScriptCore/bytecode/CallVariant.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2014, 2015 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. - */ - -#include "config.h" -#include "CallVariant.h" - -#include "JSCInlines.h" -#include <wtf/ListDump.h> - -namespace JSC { - -void CallVariant::dump(PrintStream& out) const -{ - if (!*this) { - out.print("null"); - return; - } - - if (InternalFunction* internalFunction = this->internalFunction()) { - out.print("InternalFunction: ", JSValue(internalFunction)); - return; - } - - if (JSFunction* function = this->function()) { - out.print("(Function: ", JSValue(function), "; Executable: ", *executable(), ")"); - return; - } - - out.print("Executable: ", *executable()); -} - -CallVariantList variantListWithVariant(const CallVariantList& list, CallVariant variantToAdd) -{ - ASSERT(variantToAdd); - CallVariantList result; - for (CallVariant variant : list) { - ASSERT(variant); - if (!!variantToAdd) { - if (variant == variantToAdd) - variantToAdd = CallVariant(); - else if (variant.despecifiedClosure() == variantToAdd.despecifiedClosure()) { - variant = variant.despecifiedClosure(); - variantToAdd = CallVariant(); - } - } - result.append(variant); - } - if (!!variantToAdd) - result.append(variantToAdd); - - if (!ASSERT_DISABLED) { - for (unsigned i = 0; i < result.size(); ++i) { - for (unsigned j = i + 1; j < result.size(); ++j) { - if (result[i] != result[j]) - continue; - - dataLog("variantListWithVariant(", listDump(list), ", ", variantToAdd, ") failed: got duplicates in result: ", listDump(result), "\n"); - RELEASE_ASSERT_NOT_REACHED(); - } - } - } - - return result; -} - -CallVariantList despecifiedVariantList(const CallVariantList& list) -{ - CallVariantList result; - for (CallVariant variant : list) - result = variantListWithVariant(result, variant.despecifiedClosure()); - return result; -} - -} // namespace JSC - diff --git a/Source/JavaScriptCore/bytecode/CallVariant.h b/Source/JavaScriptCore/bytecode/CallVariant.h deleted file mode 100644 index 2514f72b8..000000000 --- a/Source/JavaScriptCore/bytecode/CallVariant.h +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (C) 2014, 2015 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 CallVariant_h -#define CallVariant_h - -#include "Executable.h" -#include "JSCell.h" -#include "JSFunction.h" - -namespace JSC { - -// The CallVariant class is meant to encapsulate a callee in a way that is useful for call linking -// and inlining. Because JavaScript has closures, and because JSC implements the notion of internal -// non-function objects that nevertheless provide call traps, the call machinery wants to see a -// callee in one of the following four forms: -// -// JSFunction callee: This means that we expect the callsite to always call a particular function -// instance, that is associated with a particular lexical environment. This pinpoints not -// just the code that will be called (i.e. the executable) but also the scope within which -// the code runs. -// -// Executable callee: This corresponds to a call to a closure. In this case, we know that the -// callsite will call a JSFunction, but we do not know which particular JSFunction. We do know -// what code will be called - i.e. we know the executable. -// -// InternalFunction callee: JSC supports a special kind of native functions that support bizarre -// semantics. These are always singletons. If we know that the callee is an InternalFunction -// then we know both the code that will be called and the scope; in fact the "scope" is really -// just the InternalFunction itself. -// -// Something else: It's possible call all manner of rubbish in JavaScript. This implicitly supports -// bizarre object callees, but it can't really tell you anything interesting about them other -// than the fact that they don't fall into any of the above categories. -// -// This class serves as a kind of union over these four things. It does so by just holding a -// JSCell*. We determine which of the modes its in by doing type checks on the cell. Note that we -// cannot use WriteBarrier<> here because this gets used inside the compiler. - -class CallVariant { -public: - explicit CallVariant(JSCell* callee = nullptr) - : m_callee(callee) - { - } - - CallVariant(WTF::HashTableDeletedValueType) - : m_callee(deletedToken()) - { - } - - bool operator!() const { return !m_callee; } - - // If this variant refers to a function, change it to refer to its executable. - ALWAYS_INLINE CallVariant despecifiedClosure() const - { - if (m_callee->type() == JSFunctionType) - return CallVariant(jsCast<JSFunction*>(m_callee)->executable()); - return *this; - } - - JSCell* rawCalleeCell() const { return m_callee; } - - InternalFunction* internalFunction() const - { - return jsDynamicCast<InternalFunction*>(m_callee); - } - - JSFunction* function() const - { - return jsDynamicCast<JSFunction*>(m_callee); - } - - bool isClosureCall() const { return !!jsDynamicCast<ExecutableBase*>(m_callee); } - - ExecutableBase* executable() const - { - if (JSFunction* function = this->function()) - return function->executable(); - return jsDynamicCast<ExecutableBase*>(m_callee); - } - - JSCell* nonExecutableCallee() const - { - RELEASE_ASSERT(!isClosureCall()); - return m_callee; - } - - Intrinsic intrinsicFor(CodeSpecializationKind kind) const - { - if (ExecutableBase* executable = this->executable()) - return executable->intrinsicFor(kind); - return NoIntrinsic; - } - - FunctionExecutable* functionExecutable() const - { - if (ExecutableBase* executable = this->executable()) - return jsDynamicCast<FunctionExecutable*>(executable); - return nullptr; - } - - void dump(PrintStream& out) const; - - bool isHashTableDeletedValue() const - { - return m_callee == deletedToken(); - } - - bool operator==(const CallVariant& other) const - { - return m_callee == other.m_callee; - } - - bool operator!=(const CallVariant& other) const - { - return !(*this == other); - } - - bool operator<(const CallVariant& other) const - { - return m_callee < other.m_callee; - } - - bool operator>(const CallVariant& other) const - { - return other < *this; - } - - bool operator<=(const CallVariant& other) const - { - return !(*this < other); - } - - bool operator>=(const CallVariant& other) const - { - return other <= *this; - } - - unsigned hash() const - { - return WTF::PtrHash<JSCell*>::hash(m_callee); - } - -private: - static JSCell* deletedToken() { return bitwise_cast<JSCell*>(static_cast<uintptr_t>(1)); } - - JSCell* m_callee; -}; - -struct CallVariantHash { - static unsigned hash(const CallVariant& key) { return key.hash(); } - static bool equal(const CallVariant& a, const CallVariant& b) { return a == b; } - static const bool safeToCompareToEmptyOrDeleted = true; -}; - -typedef Vector<CallVariant, 1> CallVariantList; - -// Returns a new variant list by attempting to either append the given variant or merge it with one -// of the variants we already have by despecifying closures. -CallVariantList variantListWithVariant(const CallVariantList&, CallVariant); - -// Returns a new list where every element is despecified, and the list is deduplicated. -CallVariantList despecifiedVariantList(const CallVariantList&); - -} // namespace JSC - -namespace WTF { - -template<typename T> struct DefaultHash; -template<> struct DefaultHash<JSC::CallVariant> { - typedef JSC::CallVariantHash Hash; -}; - -template<typename T> struct HashTraits; -template<> struct HashTraits<JSC::CallVariant> : SimpleClassHashTraits<JSC::CallVariant> { }; - -} // namespace WTF - -#endif // CallVariant_h - diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp index 394974eaa..eec5b7076 100644 --- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp +++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2010, 2012-2015 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009, 2010, 2012, 2013, 2014 Apple Inc. All rights reserved. * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca> * * Redistribution and use in source and binary forms, with or without @@ -11,7 +11,7 @@ * 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 + * 3. Neither the name of Apple Computer, 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. * @@ -30,42 +30,35 @@ #include "config.h" #include "CodeBlock.h" -#include "BasicBlockLocation.h" #include "BytecodeGenerator.h" #include "BytecodeUseDef.h" #include "CallLinkStatus.h" #include "DFGCapabilities.h" #include "DFGCommon.h" #include "DFGDriver.h" -#include "DFGJITCode.h" +#include "DFGNode.h" #include "DFGWorklist.h" #include "Debugger.h" -#include "FunctionExecutableDump.h" #include "Interpreter.h" #include "JIT.h" #include "JITStubs.h" +#include "JSActivation.h" #include "JSCJSValue.h" #include "JSFunction.h" -#include "JSLexicalEnvironment.h" +#include "JSNameScope.h" #include "LLIntEntrypoint.h" #include "LowLevelInterpreter.h" -#include "JSCInlines.h" -#include "PolymorphicGetByIdList.h" +#include "Operations.h" #include "PolymorphicPutByIdList.h" -#include "ProfilerDatabase.h" #include "ReduceWhitespace.h" #include "Repatch.h" #include "RepatchBuffer.h" #include "SlotVisitorInlines.h" -#include "StackVisitor.h" -#include "TypeLocationCache.h" -#include "TypeProfiler.h" #include "UnlinkedInstructionStream.h" #include <wtf/BagToHashMap.h> #include <wtf/CommaPrinter.h> #include <wtf/StringExtras.h> #include <wtf/StringPrintStream.h> -#include <wtf/text/UniquedStringImpl.h> #if ENABLE(DFG_JIT) #include "DFGOperations.h" @@ -134,36 +127,22 @@ CString CodeBlock::sourceCodeOnOneLine() const return reduceWhitespace(sourceCodeForTools()); } -CString CodeBlock::hashAsStringIfPossible() const -{ - if (hasHash() || isSafeToComputeHash()) - return toCString(hash()); - return "<no-hash>"; -} - void CodeBlock::dumpAssumingJITType(PrintStream& out, JITCode::JITType jitType) const { - out.print(inferredName(), "#", hashAsStringIfPossible()); - out.print(":[", RawPointer(this), "->"); - if (!!m_alternative) - out.print(RawPointer(m_alternative.get()), "->"); - out.print(RawPointer(ownerExecutable()), ", ", jitType, codeType()); + if (hasHash() || isSafeToComputeHash()) + out.print(inferredName(), "#", hash(), ":[", RawPointer(this), "->", RawPointer(ownerExecutable()), ", ", jitType, codeType()); + else + out.print(inferredName(), "#<no-hash>:[", RawPointer(this), "->", RawPointer(ownerExecutable()), ", ", jitType, codeType()); if (codeType() == FunctionCode) out.print(specializationKind()); out.print(", ", instructionCount()); if (this->jitType() == JITCode::BaselineJIT && m_shouldAlwaysBeInlined) - out.print(" (ShouldAlwaysBeInlined)"); + out.print(" (SABI)"); if (ownerExecutable()->neverInline()) out.print(" (NeverInline)"); - if (ownerExecutable()->didTryToEnterInLoop()) - out.print(" (DidTryToEnterInLoop)"); if (ownerExecutable()->isStrictMode()) out.print(" (StrictMode)"); - if (this->jitType() == JITCode::BaselineJIT && m_didFailFTLCompilation) - out.print(" (FTLFail)"); - if (this->jitType() == JITCode::BaselineJIT && m_hasBeenCompiledWithFTL) - out.print(" (HadFTLReplacement)"); out.print("]"); } @@ -172,6 +151,11 @@ void CodeBlock::dump(PrintStream& out) const dumpAssumingJITType(out, jitType()); } +static CString constantName(int k, JSValue value) +{ + return toCString(value, "(@k", k - FirstConstantRegisterIndex, ")"); +} + static CString idName(int id0, const Identifier& ident) { return toCString(ident.impl(), "(@id", id0, ")"); @@ -179,16 +163,19 @@ static CString idName(int id0, const Identifier& ident) CString CodeBlock::registerName(int r) const { + if (r == missingThisObjectMarker()) + return "<null>"; + if (isConstantRegisterIndex(r)) - return constantName(r); + return constantName(r, getConstant(r)); - return toCString(VirtualRegister(r)); -} + if (operandIsArgument(r)) { + if (!VirtualRegister(r).toArgument()) + return "this"; + return toCString("arg", VirtualRegister(r).toArgument()); + } -CString CodeBlock::constantName(int index) const -{ - JSValue value = getConstant(index); - return toCString(value, "(", VirtualRegister(index), ")"); + return toCString("loc", VirtualRegister(r).toLocal()); } static CString regexpToSourceString(RegExp* regExp) @@ -267,14 +254,45 @@ void CodeBlock::printGetByIdOp(PrintStream& out, ExecState* exec, int location, case op_get_by_id_out_of_line: op = "get_by_id_out_of_line"; break; + case op_get_by_id_self: + op = "get_by_id_self"; + break; + case op_get_by_id_proto: + op = "get_by_id_proto"; + break; + case op_get_by_id_chain: + op = "get_by_id_chain"; + break; + case op_get_by_id_getter_self: + op = "get_by_id_getter_self"; + break; + case op_get_by_id_getter_proto: + op = "get_by_id_getter_proto"; + break; + case op_get_by_id_getter_chain: + op = "get_by_id_getter_chain"; + break; + case op_get_by_id_custom_self: + op = "get_by_id_custom_self"; + break; + case op_get_by_id_custom_proto: + op = "get_by_id_custom_proto"; + break; + case op_get_by_id_custom_chain: + op = "get_by_id_custom_chain"; + break; + case op_get_by_id_generic: + op = "get_by_id_generic"; + break; case op_get_array_length: op = "array_length"; break; + case op_get_string_length: + op = "string_length"; + break; default: RELEASE_ASSERT_NOT_REACHED(); -#if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE) op = 0; -#endif } int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; @@ -284,19 +302,22 @@ void CodeBlock::printGetByIdOp(PrintStream& out, ExecState* exec, int location, it += 4; // Increment up to the value profiler. } -static void dumpStructure(PrintStream& out, const char* name, Structure* structure, const Identifier& ident) +#if ENABLE(JIT) || ENABLE(LLINT) // unused in some configurations +static void dumpStructure(PrintStream& out, const char* name, ExecState* exec, Structure* structure, const Identifier& ident) { if (!structure) return; out.printf("%s = %p", name, structure); - PropertyOffset offset = structure->getConcurrently(ident.impl()); + PropertyOffset offset = structure->getConcurrently(exec->vm(), ident.impl()); if (offset != invalidOffset) out.printf(" (offset = %d)", offset); } +#endif -static void dumpChain(PrintStream& out, StructureChain* chain, const Identifier& ident) +#if ENABLE(JIT) // unused when not ENABLE(JIT), leading to silly warnings +static void dumpChain(PrintStream& out, ExecState* exec, StructureChain* chain, const Identifier& ident) { out.printf("chain = %p: [", chain); bool first = true; @@ -307,10 +328,11 @@ static void dumpChain(PrintStream& out, StructureChain* chain, const Identifier& first = false; else out.printf(", "); - dumpStructure(out, "struct", currentStructure->get(), ident); + dumpStructure(out, "struct", exec, currentStructure->get(), ident); } out.printf("]"); } +#endif void CodeBlock::printGetByIdCacheStatus(PrintStream& out, ExecState* exec, int location, const StubInfoMap& map) { @@ -320,39 +342,65 @@ void CodeBlock::printGetByIdCacheStatus(PrintStream& out, ExecState* exec, int l UNUSED_PARAM(ident); // tell the compiler to shut up in certain platform configurations. +#if ENABLE(LLINT) if (exec->interpreter()->getOpcodeID(instruction[0].u.opcode) == op_get_array_length) out.printf(" llint(array_length)"); else if (Structure* structure = instruction[4].u.structure.get()) { out.printf(" llint("); - dumpStructure(out, "struct", structure, ident); + dumpStructure(out, "struct", exec, structure, ident); out.printf(")"); } +#endif #if ENABLE(JIT) if (StructureStubInfo* stubPtr = map.get(CodeOrigin(location))) { StructureStubInfo& stubInfo = *stubPtr; - if (stubInfo.resetByGC) - out.print(" (Reset By GC)"); - if (stubInfo.seen) { out.printf(" jit("); Structure* baseStructure = 0; Structure* prototypeStructure = 0; - PolymorphicGetByIdList* list = 0; + StructureChain* chain = 0; + PolymorphicAccessStructureList* structureList = 0; + int listSize = 0; switch (stubInfo.accessType) { case access_get_by_id_self: out.printf("self"); baseStructure = stubInfo.u.getByIdSelf.baseObjectStructure.get(); break; - case access_get_by_id_list: - out.printf("list"); - list = stubInfo.u.getByIdList.list; + case access_get_by_id_proto: + out.printf("proto"); + baseStructure = stubInfo.u.getByIdProto.baseObjectStructure.get(); + prototypeStructure = stubInfo.u.getByIdProto.prototypeStructure.get(); + break; + case access_get_by_id_chain: + out.printf("chain"); + baseStructure = stubInfo.u.getByIdChain.baseObjectStructure.get(); + chain = stubInfo.u.getByIdChain.chain.get(); + break; + case access_get_by_id_self_list: + out.printf("self_list"); + structureList = stubInfo.u.getByIdSelfList.structureList; + listSize = stubInfo.u.getByIdSelfList.listSize; + break; + case access_get_by_id_proto_list: + out.printf("proto_list"); + structureList = stubInfo.u.getByIdProtoList.structureList; + listSize = stubInfo.u.getByIdProtoList.listSize; break; case access_unset: out.printf("unset"); break; + case access_get_by_id_generic: + out.printf("generic"); + break; + case access_get_array_length: + out.printf("array_length"); + break; + case access_get_string_length: + out.printf("string_length"); + break; default: RELEASE_ASSERT_NOT_REACHED(); break; @@ -360,24 +408,36 @@ void CodeBlock::printGetByIdCacheStatus(PrintStream& out, ExecState* exec, int l if (baseStructure) { out.printf(", "); - dumpStructure(out, "struct", baseStructure, ident); + dumpStructure(out, "struct", exec, baseStructure, ident); } if (prototypeStructure) { out.printf(", "); - dumpStructure(out, "prototypeStruct", baseStructure, ident); + dumpStructure(out, "prototypeStruct", exec, baseStructure, ident); } - if (list) { - out.printf(", list = %p: [", list); - for (unsigned i = 0; i < list->size(); ++i) { + if (chain) { + out.printf(", "); + dumpChain(out, exec, chain, ident); + } + + if (structureList) { + out.printf(", list = %p: [", structureList); + for (int i = 0; i < listSize; ++i) { if (i) out.printf(", "); out.printf("("); - dumpStructure(out, "base", list->at(i).structure(), ident); - if (!list->at(i).conditionSet().isEmpty()) { - out.printf(", "); - out.print(list->at(i).conditionSet()); + dumpStructure(out, "base", exec, structureList->list[i].base.get(), ident); + if (structureList->list[i].isChain) { + if (structureList->list[i].u.chain.get()) { + out.printf(", "); + dumpChain(out, exec, structureList->list[i].u.chain.get(), ident); + } + } else { + if (structureList->list[i].u.proto.get()) { + out.printf(", "); + dumpStructure(out, "proto", exec, structureList->list[i].u.proto.get(), ident); + } } out.printf(")"); } @@ -391,115 +451,7 @@ void CodeBlock::printGetByIdCacheStatus(PrintStream& out, ExecState* exec, int l #endif } -void CodeBlock::printPutByIdCacheStatus(PrintStream& out, ExecState* exec, int location, const StubInfoMap& map) -{ - Instruction* instruction = instructions().begin() + location; - - const Identifier& ident = identifier(instruction[2].u.operand); - - UNUSED_PARAM(ident); // tell the compiler to shut up in certain platform configurations. - - if (Structure* structure = instruction[4].u.structure.get()) { - switch (exec->interpreter()->getOpcodeID(instruction[0].u.opcode)) { - case op_put_by_id: - case op_put_by_id_out_of_line: - out.print(" llint("); - dumpStructure(out, "struct", structure, ident); - out.print(")"); - break; - - case op_put_by_id_transition_direct: - case op_put_by_id_transition_normal: - case op_put_by_id_transition_direct_out_of_line: - case op_put_by_id_transition_normal_out_of_line: - out.print(" llint("); - dumpStructure(out, "prev", structure, ident); - out.print(", "); - dumpStructure(out, "next", instruction[6].u.structure.get(), ident); - if (StructureChain* chain = instruction[7].u.structureChain.get()) { - out.print(", "); - dumpChain(out, chain, ident); - } - out.print(")"); - break; - - default: - out.print(" llint(unknown)"); - break; - } - } - -#if ENABLE(JIT) - if (StructureStubInfo* stubPtr = map.get(CodeOrigin(location))) { - StructureStubInfo& stubInfo = *stubPtr; - if (stubInfo.resetByGC) - out.print(" (Reset By GC)"); - - if (stubInfo.seen) { - out.printf(" jit("); - - switch (stubInfo.accessType) { - case access_put_by_id_replace: - out.print("replace, "); - dumpStructure(out, "struct", stubInfo.u.putByIdReplace.baseObjectStructure.get(), ident); - break; - case access_put_by_id_transition_normal: - case access_put_by_id_transition_direct: - out.print("transition, "); - dumpStructure(out, "prev", stubInfo.u.putByIdTransition.previousStructure.get(), ident); - out.print(", "); - dumpStructure(out, "next", stubInfo.u.putByIdTransition.structure.get(), ident); - if (stubInfo.u.putByIdTransition.rawConditionSet) - out.print(", ", ObjectPropertyConditionSet::fromRawPointer(stubInfo.u.putByIdTransition.rawConditionSet)); - break; - case access_put_by_id_list: { - out.printf("list = ["); - PolymorphicPutByIdList* list = stubInfo.u.putByIdList.list; - CommaPrinter comma; - for (unsigned i = 0; i < list->size(); ++i) { - out.print(comma, "("); - const PutByIdAccess& access = list->at(i); - - if (access.isReplace()) { - out.print("replace, "); - dumpStructure(out, "struct", access.oldStructure(), ident); - } else if (access.isSetter()) { - out.print("setter, "); - dumpStructure(out, "struct", access.oldStructure(), ident); - } else if (access.isCustom()) { - out.print("custom, "); - dumpStructure(out, "struct", access.oldStructure(), ident); - } else if (access.isTransition()) { - out.print("transition, "); - dumpStructure(out, "prev", access.oldStructure(), ident); - out.print(", "); - dumpStructure(out, "next", access.newStructure(), ident); - if (!access.conditionSet().isEmpty()) - out.print(", ", access.conditionSet()); - } else - out.print("unknown"); - - out.print(")"); - } - out.print("]"); - break; - } - case access_unset: - out.printf("unset"); - break; - default: - RELEASE_ASSERT_NOT_REACHED(); - break; - } - out.printf(")"); - } - } -#else - UNUSED_PARAM(map); -#endif -} - -void CodeBlock::printCallOp(PrintStream& out, ExecState* exec, int location, const Instruction*& it, const char* op, CacheDumpMode cacheDumpMode, bool& hasPrintedProfiling, const CallLinkInfoMap& map) +void CodeBlock::printCallOp(PrintStream& out, ExecState* exec, int location, const Instruction*& it, const char* op, CacheDumpMode cacheDumpMode, bool& hasPrintedProfiling) { int dst = (++it)->u.operand; int func = (++it)->u.operand; @@ -508,6 +460,7 @@ void CodeBlock::printCallOp(PrintStream& out, ExecState* exec, int location, con printLocationAndOp(out, exec, location, it, op); out.printf("%s, %s, %d, %d", registerName(dst).data(), registerName(func).data(), argCount, registerOffset); if (cacheDumpMode == DumpCaches) { +#if ENABLE(LLINT) LLIntCallLinkInfo* callLinkInfo = it[1].u.callLinkInfo; if (callLinkInfo->lastSeenCallee) { out.printf( @@ -515,21 +468,17 @@ void CodeBlock::printCallOp(PrintStream& out, ExecState* exec, int location, con callLinkInfo->lastSeenCallee.get(), callLinkInfo->lastSeenCallee->executable()); } +#endif #if ENABLE(JIT) - if (CallLinkInfo* info = map.get(CodeOrigin(location))) { - JSFunction* target = info->lastSeenCallee(); + if (numberOfCallLinkInfos()) { + JSFunction* target = getCallLinkInfo(location).lastSeenCallee.get(); if (target) out.printf(" jit(%p, exec %p)", target, target->executable()); } - - if (jitType() != JITCode::FTLJIT) - out.print(" status(", CallLinkStatus::computeFor(this, location, map), ")"); -#else - UNUSED_PARAM(map); #endif + out.print(" status(", CallLinkStatus::computeFor(this, location), ")"); } ++it; - ++it; dumpArrayProfiling(out, it, hasPrintedProfiling); dumpValueProfiling(out, it, hasPrintedProfiling); } @@ -544,31 +493,6 @@ void CodeBlock::printPutByIdOp(PrintStream& out, ExecState* exec, int location, it += 5; } -void CodeBlock::dumpSource() -{ - dumpSource(WTF::dataFile()); -} - -void CodeBlock::dumpSource(PrintStream& out) -{ - ScriptExecutable* executable = ownerExecutable(); - if (executable->isFunctionExecutable()) { - FunctionExecutable* functionExecutable = reinterpret_cast<FunctionExecutable*>(executable); - String source = functionExecutable->source().provider()->getRange( - functionExecutable->parametersStartOffset(), - functionExecutable->typeProfilingEndOffset() + 1); // Type profiling end offset is the character before the '}'. - - out.print("function ", inferredName(), source); - return; - } - out.print(executable->source().toString()); -} - -void CodeBlock::dumpBytecode() -{ - dumpBytecode(WTF::dataFile()); -} - void CodeBlock::dumpBytecode(PrintStream& out) { // We only use the ExecState* for things that don't actually lead to JS execution, @@ -586,19 +510,33 @@ void CodeBlock::dumpBytecode(PrintStream& out) static_cast<unsigned long>(instructions().size()), static_cast<unsigned long>(instructions().size() * sizeof(Instruction)), m_numParameters, m_numCalleeRegisters, m_numVars); - if (needsActivation() && codeType() == FunctionCode) - out.printf("; lexical environment in r%d", activationRegister().offset()); + if (symbolTable() && symbolTable()->captureCount()) { + out.printf( + "; %d captured var(s) (from r%d to r%d, inclusive)", + symbolTable()->captureCount(), symbolTable()->captureStart(), symbolTable()->captureEnd() + 1); + } + if (usesArguments()) { + out.printf( + "; uses arguments, in r%d, r%d", + argumentsRegister().offset(), + unmodifiedArgumentsRegister(argumentsRegister()).offset()); + } + if (needsFullScopeChain() && codeType() == FunctionCode) + out.printf("; activation in r%d", activationRegister().offset()); out.printf("\n"); StubInfoMap stubInfos; - CallLinkInfoMap callLinkInfos; - getStubInfoMap(stubInfos); - getCallLinkInfoMap(callLinkInfos); +#if ENABLE(JIT) + { + ConcurrentJITLocker locker(m_lock); + getStubInfoMap(locker, stubInfos); + } +#endif const Instruction* begin = instructions().begin(); const Instruction* end = instructions().end(); for (const Instruction* it = begin; it != end; ++it) - dumpBytecode(out, exec, begin, it, stubInfos, callLinkInfos); + dumpBytecode(out, exec, begin, it, stubInfos); if (numberOfIdentifiers()) { out.printf("\nIdentifiers:\n"); @@ -613,19 +551,7 @@ void CodeBlock::dumpBytecode(PrintStream& out) out.printf("\nConstants:\n"); size_t i = 0; do { - const char* sourceCodeRepresentationDescription = nullptr; - switch (m_constantsSourceCodeRepresentation[i]) { - case SourceCodeRepresentation::Double: - sourceCodeRepresentationDescription = ": in source as double"; - break; - case SourceCodeRepresentation::Integer: - sourceCodeRepresentationDescription = ": in source as integer"; - break; - case SourceCodeRepresentation::Other: - sourceCodeRepresentationDescription = ""; - break; - } - out.printf(" k%u = %s%s\n", static_cast<unsigned>(i), toCString(m_constantRegisters[i].get()).data(), sourceCodeRepresentationDescription); + out.printf(" k%u = %s\n", static_cast<unsigned>(i), toCString(m_constantRegisters[i].get()).data()); ++i; } while (i < m_constantRegisters.size()); } @@ -643,9 +569,7 @@ void CodeBlock::dumpBytecode(PrintStream& out) out.printf("\nException Handlers:\n"); unsigned i = 0; do { - HandlerInfo& handler = m_rareData->m_exceptionHandlers[i]; - out.printf("\t %d: { start: [%4d] end: [%4d] target: [%4d] } %s\n", - i + 1, handler.start, handler.end, handler.target, handler.typeName()); + out.printf("\t %d: { start: [%4d] end: [%4d] target: [%4d] depth: [%4d] }\n", i + 1, m_rareData->m_exceptionHandlers[i].start, m_rareData->m_exceptionHandlers[i].end, m_rareData->m_exceptionHandlers[i].target, m_rareData->m_exceptionHandlers[i].scopeDepth); ++i; } while (i < m_rareData->m_exceptionHandlers.size()); } @@ -674,7 +598,7 @@ void CodeBlock::dumpBytecode(PrintStream& out) out.printf(" %1d = {\n", i); StringJumpTable::StringOffsetTable::const_iterator end = m_rareData->m_stringSwitchJumpTables[i].offsetTable.end(); for (StringJumpTable::StringOffsetTable::const_iterator iter = m_rareData->m_stringSwitchJumpTables[i].offsetTable.begin(); iter != end; ++iter) - out.printf("\t\t\"%s\" => %04d\n", iter->key->utf8().data(), iter->value.branchOffset); + out.printf("\t\t\"%s\" => %04d\n", String(iter->key).utf8().data(), iter->value.branchOffset); out.printf(" }\n"); ++i; } while (i < m_rareData->m_stringSwitchJumpTables.size()); @@ -729,74 +653,52 @@ void CodeBlock::dumpRareCaseProfile(PrintStream& out, const char* name, RareCase out.print(name, profile->m_counter); } -void CodeBlock::printLocationAndOp(PrintStream& out, ExecState*, int location, const Instruction*&, const char* op) -{ - out.printf("[%4d] %-17s ", location, op); -} - -void CodeBlock::printLocationOpAndRegisterOperand(PrintStream& out, ExecState* exec, int location, const Instruction*& it, const char* op, int operand) -{ - printLocationAndOp(out, exec, location, it, op); - out.printf("%s", registerName(operand).data()); -} - -void CodeBlock::dumpBytecode( - PrintStream& out, ExecState* exec, const Instruction* begin, const Instruction*& it, - const StubInfoMap& stubInfos, const CallLinkInfoMap& callLinkInfos) +void CodeBlock::dumpBytecode(PrintStream& out, ExecState* exec, const Instruction* begin, const Instruction*& it, const StubInfoMap& map) { int location = it - begin; bool hasPrintedProfiling = false; - OpcodeID opcode = exec->interpreter()->getOpcodeID(it->u.opcode); - switch (opcode) { + switch (exec->interpreter()->getOpcodeID(it->u.opcode)) { case op_enter: { printLocationAndOp(out, exec, location, it, "enter"); break; } - case op_get_scope: { + case op_touch_entry: { + printLocationAndOp(out, exec, location, it, "touch_entry"); + break; + } + case op_create_activation: { int r0 = (++it)->u.operand; - printLocationOpAndRegisterOperand(out, exec, location, it, "get_scope", r0); + printLocationOpAndRegisterOperand(out, exec, location, it, "create_activation", r0); break; } - case op_create_direct_arguments: { + case op_create_arguments: { int r0 = (++it)->u.operand; - printLocationAndOp(out, exec, location, it, "create_direct_arguments"); - out.printf("%s", registerName(r0).data()); + printLocationOpAndRegisterOperand(out, exec, location, it, "create_arguments", r0); break; } - case op_create_scoped_arguments: { + case op_init_lazy_reg: { int r0 = (++it)->u.operand; - int r1 = (++it)->u.operand; - printLocationAndOp(out, exec, location, it, "create_scoped_arguments"); - out.printf("%s, %s", registerName(r0).data(), registerName(r1).data()); + printLocationOpAndRegisterOperand(out, exec, location, it, "init_lazy_reg", r0); break; } - case op_create_out_of_band_arguments: { + case op_get_callee: { int r0 = (++it)->u.operand; - printLocationAndOp(out, exec, location, it, "create_out_of_band_arguments"); - out.printf("%s", registerName(r0).data()); + printLocationOpAndRegisterOperand(out, exec, location, it, "get_callee", r0); + ++it; break; } case op_create_this: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; unsigned inferredInlineCapacity = (++it)->u.operand; - unsigned cachedFunction = (++it)->u.operand; printLocationAndOp(out, exec, location, it, "create_this"); - out.printf("%s, %s, %u, %u", registerName(r0).data(), registerName(r1).data(), inferredInlineCapacity, cachedFunction); + out.printf("%s, %s, %u", registerName(r0).data(), registerName(r1).data(), inferredInlineCapacity); break; } case op_to_this: { int r0 = (++it)->u.operand; printLocationOpAndRegisterOperand(out, exec, location, it, "to_this", r0); - Structure* structure = (++it)->u.structure.get(); - if (structure) - out.print(", cache(struct = ", RawPointer(structure), ")"); - out.print(", ", (++it)->u.toThisStatus); - break; - } - case op_check_tdz: { - int r0 = (++it)->u.operand; - printLocationOpAndRegisterOperand(out, exec, location, it, "op_check_tdz", r0); + ++it; // Skip value profile. break; } case op_new_object: { @@ -851,20 +753,12 @@ void CodeBlock::dumpBytecode( out.printf("%s, %s", registerName(r0).data(), registerName(r1).data()); break; } - case op_profile_type: { + case op_captured_mov: { int r0 = (++it)->u.operand; + int r1 = (++it)->u.operand; + printLocationAndOp(out, exec, location, it, "captured_mov"); + out.printf("%s, %s", registerName(r0).data(), registerName(r1).data()); ++it; - ++it; - ++it; - ++it; - printLocationAndOp(out, exec, location, it, "op_profile_type"); - out.printf("%s", registerName(r0).data()); - break; - } - case op_profile_control_flow: { - BasicBlockLocation* basicBlockLocation = (++it)->u.basicBlockLocation; - printLocationAndOp(out, exec, location, it, "profile_control_flow"); - out.printf("[%d, %d]", basicBlockLocation->startOffset(), basicBlockLocation->endOffset()); break; } case op_not: { @@ -925,10 +819,6 @@ void CodeBlock::dumpBytecode( printUnaryOp(out, exec, location, it, "to_number"); break; } - case op_to_string: { - printUnaryOp(out, exec, location, it, "to_string"); - break; - } case op_negate: { printUnaryOp(out, exec, location, it, "negate"); break; @@ -1029,10 +919,6 @@ void CodeBlock::dumpBytecode( printUnaryOp(out, exec, location, it, "is_object"); break; } - case op_is_object_or_null: { - printUnaryOp(out, exec, location, it, "is_object_or_null"); - break; - } case op_is_function: { printUnaryOp(out, exec, location, it, "is_function"); break; @@ -1041,58 +927,81 @@ void CodeBlock::dumpBytecode( printBinaryOp(out, exec, location, it, "in"); break; } + case op_init_global_const_nop: { + printLocationAndOp(out, exec, location, it, "init_global_const_nop"); + it++; + it++; + it++; + it++; + break; + } + case op_init_global_const: { + WriteBarrier<Unknown>* registerPointer = (++it)->u.registerPointer; + int r0 = (++it)->u.operand; + printLocationAndOp(out, exec, location, it, "init_global_const"); + out.printf("g%d(%p), %s", m_globalObject->findRegisterIndex(registerPointer), registerPointer, registerName(r0).data()); + it++; + it++; + break; + } case op_get_by_id: case op_get_by_id_out_of_line: - case op_get_array_length: { + case op_get_by_id_self: + case op_get_by_id_proto: + case op_get_by_id_chain: + case op_get_by_id_getter_self: + case op_get_by_id_getter_proto: + case op_get_by_id_getter_chain: + case op_get_by_id_custom_self: + case op_get_by_id_custom_proto: + case op_get_by_id_custom_chain: + case op_get_by_id_generic: + case op_get_array_length: + case op_get_string_length: { printGetByIdOp(out, exec, location, it); - printGetByIdCacheStatus(out, exec, location, stubInfos); + printGetByIdCacheStatus(out, exec, location, map); dumpValueProfiling(out, it, hasPrintedProfiling); break; } + case op_get_arguments_length: { + printUnaryOp(out, exec, location, it, "get_arguments_length"); + it++; + break; + } case op_put_by_id: { printPutByIdOp(out, exec, location, it, "put_by_id"); - printPutByIdCacheStatus(out, exec, location, stubInfos); break; } case op_put_by_id_out_of_line: { printPutByIdOp(out, exec, location, it, "put_by_id_out_of_line"); - printPutByIdCacheStatus(out, exec, location, stubInfos); + break; + } + case op_put_by_id_replace: { + printPutByIdOp(out, exec, location, it, "put_by_id_replace"); + break; + } + case op_put_by_id_transition: { + printPutByIdOp(out, exec, location, it, "put_by_id_transition"); break; } case op_put_by_id_transition_direct: { printPutByIdOp(out, exec, location, it, "put_by_id_transition_direct"); - printPutByIdCacheStatus(out, exec, location, stubInfos); break; } case op_put_by_id_transition_direct_out_of_line: { printPutByIdOp(out, exec, location, it, "put_by_id_transition_direct_out_of_line"); - printPutByIdCacheStatus(out, exec, location, stubInfos); break; } case op_put_by_id_transition_normal: { printPutByIdOp(out, exec, location, it, "put_by_id_transition_normal"); - printPutByIdCacheStatus(out, exec, location, stubInfos); break; } case op_put_by_id_transition_normal_out_of_line: { printPutByIdOp(out, exec, location, it, "put_by_id_transition_normal_out_of_line"); - printPutByIdCacheStatus(out, exec, location, stubInfos); break; } - case op_put_getter_by_id: { - int r0 = (++it)->u.operand; - int id0 = (++it)->u.operand; - int r1 = (++it)->u.operand; - printLocationAndOp(out, exec, location, it, "put_getter_by_id"); - out.printf("%s, %s, %s", registerName(r0).data(), idName(id0, identifier(id0)).data(), registerName(r1).data()); - break; - } - case op_put_setter_by_id: { - int r0 = (++it)->u.operand; - int id0 = (++it)->u.operand; - int r1 = (++it)->u.operand; - printLocationAndOp(out, exec, location, it, "put_setter_by_id"); - out.printf("%s, %s, %s", registerName(r0).data(), idName(id0, identifier(id0)).data(), registerName(r1).data()); + case op_put_by_id_generic: { + printPutByIdOp(out, exec, location, it, "put_by_id_generic"); break; } case op_put_getter_setter: { @@ -1122,6 +1031,27 @@ void CodeBlock::dumpBytecode( dumpValueProfiling(out, it, hasPrintedProfiling); break; } + case op_get_argument_by_val: { + int r0 = (++it)->u.operand; + int r1 = (++it)->u.operand; + int r2 = (++it)->u.operand; + printLocationAndOp(out, exec, location, it, "get_argument_by_val"); + out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data()); + ++it; + dumpValueProfiling(out, it, hasPrintedProfiling); + break; + } + case op_get_by_pname: { + int r0 = (++it)->u.operand; + int r1 = (++it)->u.operand; + int r2 = (++it)->u.operand; + int r3 = (++it)->u.operand; + int r4 = (++it)->u.operand; + int r5 = (++it)->u.operand; + printLocationAndOp(out, exec, location, it, "get_by_pname"); + out.printf("%s, %s, %s, %s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data(), registerName(r3).data(), registerName(r4).data(), registerName(r5).data()); + break; + } case op_put_by_val: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; @@ -1280,51 +1210,73 @@ void CodeBlock::dumpBytecode( } case op_new_func: { int r0 = (++it)->u.operand; - int r1 = (++it)->u.operand; int f0 = (++it)->u.operand; + int shouldCheck = (++it)->u.operand; printLocationAndOp(out, exec, location, it, "new_func"); - out.printf("%s, %s, f%d", registerName(r0).data(), registerName(r1).data(), f0); + out.printf("%s, f%d, %s", registerName(r0).data(), f0, shouldCheck ? "<Checked>" : "<Unchecked>"); + break; + } + case op_new_captured_func: { + int r0 = (++it)->u.operand; + int f0 = (++it)->u.operand; + printLocationAndOp(out, exec, location, it, "new_captured_func"); + out.printf("%s, f%d", registerName(r0).data(), f0); + ++it; break; } case op_new_func_exp: { int r0 = (++it)->u.operand; - int r1 = (++it)->u.operand; int f0 = (++it)->u.operand; printLocationAndOp(out, exec, location, it, "new_func_exp"); - out.printf("%s, %s, f%d", registerName(r0).data(), registerName(r1).data(), f0); + out.printf("%s, f%d", registerName(r0).data(), f0); break; } case op_call: { - printCallOp(out, exec, location, it, "call", DumpCaches, hasPrintedProfiling, callLinkInfos); + printCallOp(out, exec, location, it, "call", DumpCaches, hasPrintedProfiling); break; } case op_call_eval: { - printCallOp(out, exec, location, it, "call_eval", DontDumpCaches, hasPrintedProfiling, callLinkInfos); + printCallOp(out, exec, location, it, "call_eval", DontDumpCaches, hasPrintedProfiling); break; } - - case op_construct_varargs: case op_call_varargs: { int result = (++it)->u.operand; int callee = (++it)->u.operand; int thisValue = (++it)->u.operand; int arguments = (++it)->u.operand; int firstFreeRegister = (++it)->u.operand; - int varArgOffset = (++it)->u.operand; ++it; - printLocationAndOp(out, exec, location, it, opcode == op_call_varargs ? "call_varargs" : "construct_varargs"); - out.printf("%s, %s, %s, %s, %d, %d", registerName(result).data(), registerName(callee).data(), registerName(thisValue).data(), registerName(arguments).data(), firstFreeRegister, varArgOffset); + printLocationAndOp(out, exec, location, it, "call_varargs"); + out.printf("%s, %s, %s, %s, %d", registerName(result).data(), registerName(callee).data(), registerName(thisValue).data(), registerName(arguments).data(), firstFreeRegister); dumpValueProfiling(out, it, hasPrintedProfiling); break; } - + case op_tear_off_activation: { + int r0 = (++it)->u.operand; + printLocationOpAndRegisterOperand(out, exec, location, it, "tear_off_activation", r0); + break; + } + case op_tear_off_arguments: { + int r0 = (++it)->u.operand; + int r1 = (++it)->u.operand; + printLocationAndOp(out, exec, location, it, "tear_off_arguments"); + out.printf("%s, %s", registerName(r0).data(), registerName(r1).data()); + break; + } case op_ret: { int r0 = (++it)->u.operand; printLocationOpAndRegisterOperand(out, exec, location, it, "ret", r0); break; } + case op_ret_object_or_this: { + int r0 = (++it)->u.operand; + int r1 = (++it)->u.operand; + printLocationAndOp(out, exec, location, it, "constructor_ret"); + out.printf("%s %s", registerName(r0).data(), registerName(r1).data()); + break; + } case op_construct: { - printCallOp(out, exec, location, it, "construct", DumpCaches, hasPrintedProfiling, callLinkInfos); + printCallOp(out, exec, location, it, "construct", DumpCaches, hasPrintedProfiling); break; } case op_strcat: { @@ -1342,120 +1294,49 @@ void CodeBlock::dumpBytecode( out.printf("%s, %s", registerName(r0).data(), registerName(r1).data()); break; } - case op_get_enumerable_length: { - int dst = it[1].u.operand; - int base = it[2].u.operand; - printLocationAndOp(out, exec, location, it, "op_get_enumerable_length"); - out.printf("%s, %s", registerName(dst).data(), registerName(base).data()); - it += OPCODE_LENGTH(op_get_enumerable_length) - 1; + case op_get_pnames: { + int r0 = it[1].u.operand; + int r1 = it[2].u.operand; + int r2 = it[3].u.operand; + int r3 = it[4].u.operand; + int offset = it[5].u.operand; + printLocationAndOp(out, exec, location, it, "get_pnames"); + out.printf("%s, %s, %s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), registerName(r2).data(), registerName(r3).data(), offset, location + offset); + it += OPCODE_LENGTH(op_get_pnames) - 1; break; } - case op_has_indexed_property: { - int dst = it[1].u.operand; + case op_next_pname: { + int dest = it[1].u.operand; int base = it[2].u.operand; - int propertyName = it[3].u.operand; - ArrayProfile* arrayProfile = it[4].u.arrayProfile; - printLocationAndOp(out, exec, location, it, "op_has_indexed_property"); - out.printf("%s, %s, %s, %p", registerName(dst).data(), registerName(base).data(), registerName(propertyName).data(), arrayProfile); - it += OPCODE_LENGTH(op_has_indexed_property) - 1; - break; - } - case op_has_structure_property: { - int dst = it[1].u.operand; - int base = it[2].u.operand; - int propertyName = it[3].u.operand; - int enumerator = it[4].u.operand; - printLocationAndOp(out, exec, location, it, "op_has_structure_property"); - out.printf("%s, %s, %s, %s", registerName(dst).data(), registerName(base).data(), registerName(propertyName).data(), registerName(enumerator).data()); - it += OPCODE_LENGTH(op_has_structure_property) - 1; - break; - } - case op_has_generic_property: { - int dst = it[1].u.operand; - int base = it[2].u.operand; - int propertyName = it[3].u.operand; - printLocationAndOp(out, exec, location, it, "op_has_generic_property"); - out.printf("%s, %s, %s", registerName(dst).data(), registerName(base).data(), registerName(propertyName).data()); - it += OPCODE_LENGTH(op_has_generic_property) - 1; - break; - } - case op_get_direct_pname: { - int dst = it[1].u.operand; - int base = it[2].u.operand; - int propertyName = it[3].u.operand; - int index = it[4].u.operand; - int enumerator = it[5].u.operand; - ValueProfile* profile = it[6].u.profile; - printLocationAndOp(out, exec, location, it, "op_get_direct_pname"); - out.printf("%s, %s, %s, %s, %s, %p", registerName(dst).data(), registerName(base).data(), registerName(propertyName).data(), registerName(index).data(), registerName(enumerator).data(), profile); - it += OPCODE_LENGTH(op_get_direct_pname) - 1; - break; - - } - case op_get_property_enumerator: { - int dst = it[1].u.operand; - int base = it[2].u.operand; - printLocationAndOp(out, exec, location, it, "op_get_property_enumerator"); - out.printf("%s, %s", registerName(dst).data(), registerName(base).data()); - it += OPCODE_LENGTH(op_get_property_enumerator) - 1; - break; - } - case op_enumerator_structure_pname: { - int dst = it[1].u.operand; - int enumerator = it[2].u.operand; - int index = it[3].u.operand; - printLocationAndOp(out, exec, location, it, "op_enumerator_structure_pname"); - out.printf("%s, %s, %s", registerName(dst).data(), registerName(enumerator).data(), registerName(index).data()); - it += OPCODE_LENGTH(op_enumerator_structure_pname) - 1; - break; - } - case op_enumerator_generic_pname: { - int dst = it[1].u.operand; - int enumerator = it[2].u.operand; - int index = it[3].u.operand; - printLocationAndOp(out, exec, location, it, "op_enumerator_generic_pname"); - out.printf("%s, %s, %s", registerName(dst).data(), registerName(enumerator).data(), registerName(index).data()); - it += OPCODE_LENGTH(op_enumerator_generic_pname) - 1; - break; - } - case op_to_index_string: { - int dst = it[1].u.operand; - int index = it[2].u.operand; - printLocationAndOp(out, exec, location, it, "op_to_index_string"); - out.printf("%s, %s", registerName(dst).data(), registerName(index).data()); - it += OPCODE_LENGTH(op_to_index_string) - 1; + int i = it[3].u.operand; + int size = it[4].u.operand; + int iter = it[5].u.operand; + int offset = it[6].u.operand; + printLocationAndOp(out, exec, location, it, "next_pname"); + out.printf("%s, %s, %s, %s, %s, %d(->%d)", registerName(dest).data(), registerName(base).data(), registerName(i).data(), registerName(size).data(), registerName(iter).data(), offset, location + offset); + it += OPCODE_LENGTH(op_next_pname) - 1; break; } case op_push_with_scope: { - int dst = (++it)->u.operand; - int newScope = (++it)->u.operand; - int currentScope = (++it)->u.operand; - printLocationAndOp(out, exec, location, it, "push_with_scope"); - out.printf("%s, %s, %s", registerName(dst).data(), registerName(newScope).data(), registerName(currentScope).data()); + int r0 = (++it)->u.operand; + printLocationOpAndRegisterOperand(out, exec, location, it, "push_with_scope", r0); break; } - case op_get_parent_scope: { - int dst = (++it)->u.operand; - int parentScope = (++it)->u.operand; - printLocationAndOp(out, exec, location, it, "get_parent_scope"); - out.printf("%s, %s", registerName(dst).data(), registerName(parentScope).data()); + case op_pop_scope: { + printLocationAndOp(out, exec, location, it, "pop_scope"); break; } - case op_create_lexical_environment: { - int dst = (++it)->u.operand; - int scope = (++it)->u.operand; - int symbolTable = (++it)->u.operand; - int initialValue = (++it)->u.operand; - printLocationAndOp(out, exec, location, it, "create_lexical_environment"); - out.printf("%s, %s, %s, %s", - registerName(dst).data(), registerName(scope).data(), registerName(symbolTable).data(), registerName(initialValue).data()); + case op_push_name_scope: { + int id0 = (++it)->u.operand; + int r1 = (++it)->u.operand; + unsigned attributes = (++it)->u.operand; + printLocationAndOp(out, exec, location, it, "push_name_scope"); + out.printf("%s, %s, %u", idName(id0, identifier(id0)).data(), registerName(r1).data(), attributes); break; } case op_catch: { int r0 = (++it)->u.operand; - int r1 = (++it)->u.operand; - printLocationAndOp(out, exec, location, it, "catch"); - out.printf("%s, %s", registerName(r0).data(), registerName(r1).data()); + printLocationOpAndRegisterOperand(out, exec, location, it, "catch", r0); break; } case op_throw: { @@ -1467,7 +1348,7 @@ void CodeBlock::dumpBytecode( int k0 = (++it)->u.operand; int k1 = (++it)->u.operand; printLocationAndOp(out, exec, location, it, "throw_static_error"); - out.printf("%s, %s", constantName(k0).data(), k1 ? "true" : "false"); + out.printf("%s, %s", constantName(k0, getConstant(k0)).data(), k1 ? "true" : "false"); break; } case op_debug: { @@ -1494,14 +1375,11 @@ void CodeBlock::dumpBytecode( } case op_resolve_scope: { int r0 = (++it)->u.operand; - int scope = (++it)->u.operand; int id0 = (++it)->u.operand; - ResolveModeAndType modeAndType = ResolveModeAndType((++it)->u.operand); - int depth = (++it)->u.operand; + int resolveModeAndType = (++it)->u.operand; + ++it; // depth printLocationAndOp(out, exec, location, it, "resolve_scope"); - out.printf("%s, %s, %s, %u<%s|%s>, %d", registerName(r0).data(), registerName(scope).data(), idName(id0, identifier(id0)).data(), - modeAndType.operand(), resolveModeName(modeAndType.mode()), resolveTypeName(modeAndType.type()), - depth); + out.printf("%s, %s, %d", registerName(r0).data(), idName(id0, identifier(id0)).data(), resolveModeAndType); ++it; break; } @@ -1509,54 +1387,29 @@ void CodeBlock::dumpBytecode( int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int id0 = (++it)->u.operand; - ResolveModeAndType modeAndType = ResolveModeAndType((++it)->u.operand); + int resolveModeAndType = (++it)->u.operand; ++it; // Structure - int operand = (++it)->u.operand; // Operand + ++it; // Operand + ++it; // Skip value profile. printLocationAndOp(out, exec, location, it, "get_from_scope"); - out.print(registerName(r0), ", ", registerName(r1)); - if (static_cast<unsigned>(id0) == UINT_MAX) - out.print(", anonymous"); - else - out.print(", ", idName(id0, identifier(id0))); - out.print(", ", modeAndType.operand(), "<", resolveModeName(modeAndType.mode()), "|", resolveTypeName(modeAndType.type()), ">, ", operand); - dumpValueProfiling(out, it, hasPrintedProfiling); + out.printf("%s, %s, %s, %d", registerName(r0).data(), registerName(r1).data(), idName(id0, identifier(id0)).data(), resolveModeAndType); break; } case op_put_to_scope: { int r0 = (++it)->u.operand; int id0 = (++it)->u.operand; int r1 = (++it)->u.operand; - ResolveModeAndType modeAndType = ResolveModeAndType((++it)->u.operand); + int resolveModeAndType = (++it)->u.operand; ++it; // Structure - int operand = (++it)->u.operand; // Operand + ++it; // Operand printLocationAndOp(out, exec, location, it, "put_to_scope"); - out.print(registerName(r0)); - if (static_cast<unsigned>(id0) == UINT_MAX) - out.print(", anonymous"); - else - out.print(", ", idName(id0, identifier(id0))); - out.print(", ", registerName(r1), ", ", modeAndType.operand(), "<", resolveModeName(modeAndType.mode()), "|", resolveTypeName(modeAndType.type()), ">, <structure>, ", operand); - break; - } - case op_get_from_arguments: { - int r0 = (++it)->u.operand; - int r1 = (++it)->u.operand; - int offset = (++it)->u.operand; - printLocationAndOp(out, exec, location, it, "get_from_arguments"); - out.printf("%s, %s, %d", registerName(r0).data(), registerName(r1).data(), offset); - dumpValueProfiling(out, it, hasPrintedProfiling); - break; - } - case op_put_to_arguments: { - int r0 = (++it)->u.operand; - int offset = (++it)->u.operand; - int r1 = (++it)->u.operand; - printLocationAndOp(out, exec, location, it, "put_to_arguments"); - out.printf("%s, %d, %s", registerName(r0).data(), offset, registerName(r1).data()); + out.printf("%s, %s, %s, %d", registerName(r0).data(), idName(id0, identifier(id0)).data(), registerName(r1).data(), resolveModeAndType); break; } +#if ENABLE(LLINT_C_LOOP) default: RELEASE_ASSERT_NOT_REACHED(); +#endif } dumpRareCaseProfile(out, "rare case: ", rareCaseProfileForBytecodeOffset(location), hasPrintedProfiling); @@ -1568,7 +1421,7 @@ void CodeBlock::dumpBytecode( out.print(" !! frequent exits: "); CommaPrinter comma; for (unsigned i = 0; i < exitSites.size(); ++i) - out.print(comma, exitSites[i].kind(), " ", exitSites[i].jitType()); + out.print(comma, exitSites[i].kind()); } #else // ENABLE(DFG_JIT) UNUSED_PARAM(location); @@ -1576,13 +1429,11 @@ void CodeBlock::dumpBytecode( out.print("\n"); } -void CodeBlock::dumpBytecode( - PrintStream& out, unsigned bytecodeOffset, - const StubInfoMap& stubInfos, const CallLinkInfoMap& callLinkInfos) +void CodeBlock::dumpBytecode(PrintStream& out, unsigned bytecodeOffset) { ExecState* exec = m_globalObject->globalExec(); const Instruction* it = instructions().begin() + bytecodeOffset; - dumpBytecode(out, exec, instructions().begin(), it, stubInfos, callLinkInfos); + dumpBytecode(out, exec, instructions().begin(), it); } #define FOR_EACH_MEMBER_VECTOR(macro) \ @@ -1610,28 +1461,6 @@ static size_t sizeInBytes(const Vector<T>& vector) return vector.capacity() * sizeof(T); } -namespace { - -class PutToScopeFireDetail : public FireDetail { -public: - PutToScopeFireDetail(CodeBlock* codeBlock, const Identifier& ident) - : m_codeBlock(codeBlock) - , m_ident(ident) - { - } - - virtual void dump(PrintStream& out) const override - { - out.print("Linking put_to_scope in ", FunctionExecutableDump(jsCast<FunctionExecutable*>(m_codeBlock->ownerExecutable())), " for ", m_ident); - } - -private: - CodeBlock* m_codeBlock; - const Identifier& m_ident; -}; - -} // anonymous namespace - CodeBlock::CodeBlock(CopyParsedBlockTag, CodeBlock& other) : m_globalObject(other.m_globalObject) , m_heap(other.m_heap) @@ -1640,26 +1469,22 @@ CodeBlock::CodeBlock(CopyParsedBlockTag, CodeBlock& other) , m_isConstructor(other.m_isConstructor) , m_shouldAlwaysBeInlined(true) , m_didFailFTLCompilation(false) - , m_hasBeenCompiledWithFTL(false) , m_unlinkedCode(*other.m_vm, other.m_ownerExecutable.get(), other.m_unlinkedCode.get()) - , m_hasDebuggerStatement(false) , m_steppingMode(SteppingModeDisabled) , m_numBreakpoints(0) , m_ownerExecutable(*other.m_vm, other.m_ownerExecutable.get(), other.m_ownerExecutable.get()) , m_vm(other.m_vm) , m_instructions(other.m_instructions) , m_thisRegister(other.m_thisRegister) - , m_scopeRegister(other.m_scopeRegister) - , m_lexicalEnvironmentRegister(other.m_lexicalEnvironmentRegister) + , m_argumentsRegister(other.m_argumentsRegister) + , m_activationRegister(other.m_activationRegister) , m_isStrictMode(other.m_isStrictMode) , m_needsActivation(other.m_needsActivation) - , m_mayBeExecuting(false) , m_source(other.m_source) , m_sourceOffset(other.m_sourceOffset) , m_firstLineColumnOffset(other.m_firstLineColumnOffset) , m_codeType(other.m_codeType) , m_constantRegisters(other.m_constantRegisters) - , m_constantsSourceCodeRepresentation(other.m_constantsSourceCodeRepresentation) , m_functionDecls(other.m_functionDecls) , m_functionExprs(other.m_functionExprs) , m_osrExitCounter(0) @@ -1670,11 +1495,11 @@ CodeBlock::CodeBlock(CopyParsedBlockTag, CodeBlock& other) , m_capabilityLevelState(DFG::CapabilityLevelNotSet) #endif { - m_visitAggregateHasBeenCalled.store(false, std::memory_order_relaxed); - ASSERT(m_heap->isDeferred()); - ASSERT(m_scopeRegister.isLocal()); - + + if (SymbolTable* symbolTable = other.symbolTable()) + m_symbolTable.set(*m_vm, m_ownerExecutable.get(), symbolTable); + setNumParameters(other.numParameters()); optimizeAfterWarmUp(); jitAfterWarmUp(); @@ -1689,7 +1514,7 @@ CodeBlock::CodeBlock(CopyParsedBlockTag, CodeBlock& other) } m_heap->m_codeBlocks.add(this); - m_heap->reportExtraMemoryAllocated(sizeof(CodeBlock)); + m_heap->reportExtraMemoryCost(sizeof(CodeBlock)); } CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlinkedCodeBlock, JSScope* scope, PassRefPtr<SourceProvider> sourceProvider, unsigned sourceOffset, unsigned firstLineColumnOffset) @@ -1700,19 +1525,16 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin , m_isConstructor(unlinkedCodeBlock->isConstructor()) , m_shouldAlwaysBeInlined(true) , m_didFailFTLCompilation(false) - , m_hasBeenCompiledWithFTL(false) , m_unlinkedCode(m_globalObject->vm(), ownerExecutable, unlinkedCodeBlock) - , m_hasDebuggerStatement(false) , m_steppingMode(SteppingModeDisabled) , m_numBreakpoints(0) , m_ownerExecutable(m_globalObject->vm(), ownerExecutable, ownerExecutable) , m_vm(unlinkedCodeBlock->vm()) , m_thisRegister(unlinkedCodeBlock->thisRegister()) - , m_scopeRegister(unlinkedCodeBlock->scopeRegister()) - , m_lexicalEnvironmentRegister(unlinkedCodeBlock->activationRegister()) + , m_argumentsRegister(unlinkedCodeBlock->argumentsRegister()) + , m_activationRegister(unlinkedCodeBlock->activationRegister()) , m_isStrictMode(unlinkedCodeBlock->isStrictMode()) - , m_needsActivation(unlinkedCodeBlock->hasActivationRegister() && unlinkedCodeBlock->codeType() == FunctionCode) - , m_mayBeExecuting(false) + , m_needsActivation(unlinkedCodeBlock->needsFullScopeChain() && unlinkedCodeBlock->codeType() == FunctionCode) , m_source(sourceProvider) , m_sourceOffset(sourceOffset) , m_firstLineColumnOffset(firstLineColumnOffset) @@ -1724,59 +1546,54 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin , m_capabilityLevelState(DFG::CapabilityLevelNotSet) #endif { - m_visitAggregateHasBeenCalled.store(false, std::memory_order_relaxed); - ASSERT(m_heap->isDeferred()); - ASSERT(m_scopeRegister.isLocal()); + bool didCloneSymbolTable = false; + + if (SymbolTable* symbolTable = unlinkedCodeBlock->symbolTable()) { + if (codeType() == FunctionCode && symbolTable->captureCount()) { + m_symbolTable.set(*m_vm, m_ownerExecutable.get(), symbolTable->clone(*m_vm)); + didCloneSymbolTable = true; + } else + m_symbolTable.set(*m_vm, m_ownerExecutable.get(), symbolTable); + } + ASSERT(m_source); setNumParameters(unlinkedCodeBlock->numParameters()); - if (vm()->typeProfiler() || vm()->controlFlowProfiler()) - vm()->functionHasExecutedCache()->removeUnexecutedRange(m_ownerExecutable->sourceID(), m_ownerExecutable->typeProfilingStartOffset(), m_ownerExecutable->typeProfilingEndOffset()); - - setConstantRegisters(unlinkedCodeBlock->constantRegisters(), unlinkedCodeBlock->constantsSourceCodeRepresentation()); + setConstantRegisters(unlinkedCodeBlock->constantRegisters()); if (unlinkedCodeBlock->usesGlobalObject()) - m_constantRegisters[unlinkedCodeBlock->globalObjectRegister().toConstantIndex()].set(*m_vm, ownerExecutable, m_globalObject.get()); - - for (unsigned i = 0; i < LinkTimeConstantCount; i++) { - LinkTimeConstant type = static_cast<LinkTimeConstant>(i); - if (unsigned registerIndex = unlinkedCodeBlock->registerIndexForLinkTimeConstant(type)) - m_constantRegisters[registerIndex].set(*m_vm, ownerExecutable, m_globalObject->jsCellForLinkTimeConstant(type)); - } - - HashSet<int, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> clonedConstantSymbolTables; - { - HashSet<SymbolTable*> clonedSymbolTables; - for (unsigned i = 0; i < m_constantRegisters.size(); i++) { - if (m_constantRegisters[i].get().isEmpty()) - continue; - if (SymbolTable* symbolTable = jsDynamicCast<SymbolTable*>(m_constantRegisters[i].get())) { - RELEASE_ASSERT(clonedSymbolTables.add(symbolTable).isNewEntry); - if (m_vm->typeProfiler()) { - ConcurrentJITLocker locker(symbolTable->m_lock); - symbolTable->prepareForTypeProfiling(locker); - } - m_constantRegisters[i].set(*m_vm, ownerExecutable, symbolTable->cloneScopePart(*m_vm)); - clonedConstantSymbolTables.add(i + FirstConstantRegisterIndex); - } - } - } - + m_constantRegisters[unlinkedCodeBlock->globalObjectRegister().offset()].set(*m_vm, ownerExecutable, m_globalObject.get()); m_functionDecls.resizeToFit(unlinkedCodeBlock->numberOfFunctionDecls()); for (size_t count = unlinkedCodeBlock->numberOfFunctionDecls(), i = 0; i < count; ++i) { UnlinkedFunctionExecutable* unlinkedExecutable = unlinkedCodeBlock->functionDecl(i); - if (vm()->typeProfiler() || vm()->controlFlowProfiler()) - vm()->functionHasExecutedCache()->insertUnexecutedRange(m_ownerExecutable->sourceID(), unlinkedExecutable->typeProfilingStartOffset(), unlinkedExecutable->typeProfilingEndOffset()); - m_functionDecls[i].set(*m_vm, ownerExecutable, unlinkedExecutable->link(*m_vm, ownerExecutable->source())); + unsigned lineCount = unlinkedExecutable->lineCount(); + unsigned firstLine = ownerExecutable->lineNo() + unlinkedExecutable->firstLineOffset(); + bool startColumnIsOnOwnerStartLine = !unlinkedExecutable->firstLineOffset(); + unsigned startColumn = unlinkedExecutable->unlinkedBodyStartColumn() + (startColumnIsOnOwnerStartLine ? ownerExecutable->startColumn() : 1); + bool endColumnIsOnStartLine = !lineCount; + unsigned endColumn = unlinkedExecutable->unlinkedBodyEndColumn() + (endColumnIsOnStartLine ? startColumn : 1); + unsigned startOffset = sourceOffset + unlinkedExecutable->startOffset(); + unsigned sourceLength = unlinkedExecutable->sourceLength(); + SourceCode code(m_source, startOffset, startOffset + sourceLength, firstLine, startColumn); + FunctionExecutable* executable = FunctionExecutable::create(*m_vm, code, unlinkedExecutable, firstLine, firstLine + lineCount, startColumn, endColumn); + m_functionDecls[i].set(*m_vm, ownerExecutable, executable); } m_functionExprs.resizeToFit(unlinkedCodeBlock->numberOfFunctionExprs()); for (size_t count = unlinkedCodeBlock->numberOfFunctionExprs(), i = 0; i < count; ++i) { UnlinkedFunctionExecutable* unlinkedExecutable = unlinkedCodeBlock->functionExpr(i); - if (vm()->typeProfiler() || vm()->controlFlowProfiler()) - vm()->functionHasExecutedCache()->insertUnexecutedRange(m_ownerExecutable->sourceID(), unlinkedExecutable->typeProfilingStartOffset(), unlinkedExecutable->typeProfilingEndOffset()); - m_functionExprs[i].set(*m_vm, ownerExecutable, unlinkedExecutable->link(*m_vm, ownerExecutable->source())); + unsigned lineCount = unlinkedExecutable->lineCount(); + unsigned firstLine = ownerExecutable->lineNo() + unlinkedExecutable->firstLineOffset(); + bool startColumnIsOnOwnerStartLine = !unlinkedExecutable->firstLineOffset(); + unsigned startColumn = unlinkedExecutable->unlinkedBodyStartColumn() + (startColumnIsOnOwnerStartLine ? ownerExecutable->startColumn() : 1); + bool endColumnIsOnStartLine = !lineCount; + unsigned endColumn = unlinkedExecutable->unlinkedBodyEndColumn() + (endColumnIsOnStartLine ? startColumn : 1); + unsigned startOffset = sourceOffset + unlinkedExecutable->startOffset(); + unsigned sourceLength = unlinkedExecutable->sourceLength(); + SourceCode code(m_source, startOffset, startOffset + sourceLength, firstLine, startColumn); + FunctionExecutable* executable = FunctionExecutable::create(*m_vm, code, unlinkedExecutable, firstLine, firstLine + lineCount, startColumn, endColumn); + m_functionExprs[i].set(*m_vm, ownerExecutable, executable); } if (unlinkedCodeBlock->hasRareData()) { @@ -1790,13 +1607,15 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin } if (size_t count = unlinkedCodeBlock->numberOfExceptionHandlers()) { m_rareData->m_exceptionHandlers.resizeToFit(count); + size_t nonLocalScopeDepth = scope->depth(); for (size_t i = 0; i < count; i++) { - const UnlinkedHandlerInfo& unlinkedHandler = unlinkedCodeBlock->exceptionHandler(i); - HandlerInfo& handler = m_rareData->m_exceptionHandlers[i]; -#if ENABLE(JIT) - handler.initialize(unlinkedHandler, CodeLocationLabel(MacroAssemblerCodePtr::createFromExecutableAddress(LLInt::getCodePtr(op_catch)))); -#else - handler.initialize(unlinkedHandler); + const UnlinkedHandlerInfo& handler = unlinkedCodeBlock->exceptionHandler(i); + m_rareData->m_exceptionHandlers[i].start = handler.start; + m_rareData->m_exceptionHandlers[i].end = handler.end; + m_rareData->m_exceptionHandlers[i].target = handler.target; + m_rareData->m_exceptionHandlers[i].scopeDepth = nonLocalScopeDepth + handler.scopeDepth; +#if ENABLE(JIT) && ENABLE(LLINT) + m_rareData->m_exceptionHandlers[i].nativeCode = CodeLocationLabel(MacroAssemblerCodePtr::createFromExecutableAddress(LLInt::getCodePtr(llint_op_catch))); #endif } } @@ -1826,8 +1645,10 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin } // Allocate metadata buffers for the bytecode +#if ENABLE(LLINT) if (size_t size = unlinkedCodeBlock->numberOfLLintCallLinkInfos()) m_llintCallLinkInfos.resizeToFit(size); +#endif if (size_t size = unlinkedCodeBlock->numberOfArrayProfiles()) m_arrayProfiles.grow(size); if (size_t size = unlinkedCodeBlock->numberOfArrayAllocationProfiles()) @@ -1841,8 +1662,7 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin unsigned instructionCount = unlinkedCodeBlock->instructions().count(); UnlinkedInstructionStream::Reader instructionReader(unlinkedCodeBlock->instructions()); - RefCountedArray<Instruction> instructions(instructionCount); - + Vector<Instruction, 0, UnsafeVectorOverflow> instructions(instructionCount); for (unsigned i = 0; !instructionReader.atEnd(); ) { const UnlinkedInstruction* pc = instructionReader.next(); @@ -1855,25 +1675,16 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin instructions[i + j].u.operand = pc[j].u.operand; } switch (pc[0].u.opcode) { - case op_has_indexed_property: { - int arrayProfileIndex = pc[opLength - 1].u.operand; - m_arrayProfiles[arrayProfileIndex] = ArrayProfile(i); - - instructions[i + opLength - 1] = &m_arrayProfiles[arrayProfileIndex]; - break; - } case op_call_varargs: - case op_construct_varargs: - case op_get_by_val: { + case op_get_by_val: + case op_get_argument_by_val: { int arrayProfileIndex = pc[opLength - 2].u.operand; m_arrayProfiles[arrayProfileIndex] = ArrayProfile(i); instructions[i + opLength - 2] = &m_arrayProfiles[arrayProfileIndex]; FALLTHROUGH; } - case op_get_direct_pname: - case op_get_by_id: - case op_get_from_arguments: { + case op_get_by_id: { ValueProfile* profile = &m_valueProfiles[pc[opLength - 1].u.operand]; ASSERT(profile->m_bytecodeOffset == -1); profile->m_bytecodeOffset = i; @@ -1920,11 +1731,15 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin int arrayProfileIndex = pc[opLength - 2].u.operand; m_arrayProfiles[arrayProfileIndex] = ArrayProfile(i); instructions[i + opLength - 2] = &m_arrayProfiles[arrayProfileIndex]; +#if ENABLE(LLINT) instructions[i + 5] = &m_llintCallLinkInfos[pc[5].u.operand]; +#endif break; } case op_construct: { +#if ENABLE(LLINT) instructions[i + 5] = &m_llintCallLinkInfos[pc[5].u.operand]; +#endif ValueProfile* profile = &m_valueProfiles[pc[opLength - 1].u.operand]; ASSERT(profile->m_bytecodeOffset == -1); profile->m_bytecodeOffset = i; @@ -1932,28 +1747,41 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin break; } case op_get_by_id_out_of_line: + case op_get_by_id_self: + case op_get_by_id_proto: + case op_get_by_id_chain: + case op_get_by_id_getter_self: + case op_get_by_id_getter_proto: + case op_get_by_id_getter_chain: + case op_get_by_id_custom_self: + case op_get_by_id_custom_proto: + case op_get_by_id_custom_chain: + case op_get_by_id_generic: case op_get_array_length: + case op_get_string_length: CRASH(); - case op_create_lexical_environment: { - int symbolTableIndex = pc[3].u.operand; - RELEASE_ASSERT(clonedConstantSymbolTables.contains(symbolTableIndex)); + case op_init_global_const_nop: { + ASSERT(codeType() == GlobalCode); + Identifier ident = identifier(pc[4].u.operand); + SymbolTableEntry entry = m_globalObject->symbolTable()->get(ident.impl()); + if (entry.isNull()) + break; + + instructions[i + 0] = vm()->interpreter->getOpcode(op_init_global_const); + instructions[i + 1] = &m_globalObject->registerAt(entry.getIndex()); break; } case op_resolve_scope: { - const Identifier& ident = identifier(pc[3].u.operand); - ResolveType type = static_cast<ResolveType>(pc[4].u.operand); - RELEASE_ASSERT(type != LocalClosureVar); - int localScopeDepth = pc[5].u.operand; - - ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), localScopeDepth, scope, ident, Get, type); - instructions[i + 4].u.operand = op.type; - instructions[i + 5].u.operand = op.depth; - if (op.lexicalEnvironment) - instructions[i + 6].u.symbolTable.set(*vm(), ownerExecutable, op.lexicalEnvironment->symbolTable()); - else - instructions[i + 6].u.pointer = nullptr; + const Identifier& ident = identifier(pc[2].u.operand); + ResolveType type = static_cast<ResolveType>(pc[3].u.operand); + + ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), scope, ident, Get, type); + instructions[i + 3].u.operand = op.type; + instructions[i + 4].u.operand = op.depth; + if (op.activation) + instructions[i + 5].u.activation.set(*vm(), ownerExecutable, op.activation); break; } @@ -1964,18 +1792,9 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin instructions[i + opLength - 1] = profile; // get_from_scope dst, scope, id, ResolveModeAndType, Structure, Operand - - int localScopeDepth = pc[5].u.operand; - instructions[i + 5].u.pointer = nullptr; - - ResolveModeAndType modeAndType = ResolveModeAndType(pc[4].u.operand); - if (modeAndType.type() == LocalClosureVar) { - instructions[i + 4] = ResolveModeAndType(modeAndType.mode(), ClosureVar).operand(); - break; - } - const Identifier& ident = identifier(pc[3].u.operand); - ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), localScopeDepth, scope, ident, Get, modeAndType.type()); + ResolveModeAndType modeAndType = ResolveModeAndType(pc[4].u.operand); + ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), scope, ident, Get, modeAndType.type()); instructions[i + 4].u.operand = ResolveModeAndType(modeAndType.mode(), op.type).operand(); if (op.type == GlobalVar || op.type == GlobalVarWithVarInjectionChecks) @@ -1988,131 +1807,35 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin case op_put_to_scope: { // put_to_scope scope, id, value, ResolveModeAndType, Structure, Operand - ResolveModeAndType modeAndType = ResolveModeAndType(pc[4].u.operand); - if (modeAndType.type() == LocalClosureVar) { - // Only do watching if the property we're putting to is not anonymous. - if (static_cast<unsigned>(pc[2].u.operand) != UINT_MAX) { - int symbolTableIndex = pc[5].u.operand; - RELEASE_ASSERT(clonedConstantSymbolTables.contains(symbolTableIndex)); - SymbolTable* symbolTable = jsCast<SymbolTable*>(getConstant(symbolTableIndex)); - const Identifier& ident = identifier(pc[2].u.operand); - ConcurrentJITLocker locker(symbolTable->m_lock); - auto iter = symbolTable->find(locker, ident.impl()); - RELEASE_ASSERT(iter != symbolTable->end(locker)); - iter->value.prepareToWatch(); - instructions[i + 5].u.watchpointSet = iter->value.watchpointSet(); - } else - instructions[i + 5].u.watchpointSet = nullptr; - break; - } - const Identifier& ident = identifier(pc[2].u.operand); - int localScopeDepth = pc[5].u.operand; - instructions[i + 5].u.pointer = nullptr; - ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), localScopeDepth, scope, ident, Put, modeAndType.type()); + ResolveModeAndType modeAndType = ResolveModeAndType(pc[4].u.operand); + ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), scope, ident, Put, modeAndType.type()); instructions[i + 4].u.operand = ResolveModeAndType(modeAndType.mode(), op.type).operand(); if (op.type == GlobalVar || op.type == GlobalVarWithVarInjectionChecks) instructions[i + 5].u.watchpointSet = op.watchpointSet; else if (op.type == ClosureVar || op.type == ClosureVarWithVarInjectionChecks) { if (op.watchpointSet) - op.watchpointSet->invalidate(PutToScopeFireDetail(this, ident)); + op.watchpointSet->invalidate(); } else if (op.structure) instructions[i + 5].u.structure.set(*vm(), ownerExecutable, op.structure); instructions[i + 6].u.pointer = reinterpret_cast<void*>(op.operand); - break; } - - case op_profile_type: { - RELEASE_ASSERT(vm()->typeProfiler()); - // The format of this instruction is: op_profile_type regToProfile, TypeLocation*, flag, identifier?, resolveType? - size_t instructionOffset = i + opLength - 1; - unsigned divotStart, divotEnd; - GlobalVariableID globalVariableID = 0; - RefPtr<TypeSet> globalTypeSet; - bool shouldAnalyze = m_unlinkedCode->typeProfilerExpressionInfoForBytecodeOffset(instructionOffset, divotStart, divotEnd); - VirtualRegister profileRegister(pc[1].u.operand); - ProfileTypeBytecodeFlag flag = static_cast<ProfileTypeBytecodeFlag>(pc[3].u.operand); - SymbolTable* symbolTable = nullptr; - - switch (flag) { - case ProfileTypeBytecodeClosureVar: { - const Identifier& ident = identifier(pc[4].u.operand); - int localScopeDepth = pc[2].u.operand; - ResolveType type = static_cast<ResolveType>(pc[5].u.operand); - // Even though type profiling may be profiling either a Get or a Put, we can always claim a Get because - // we're abstractly "read"ing from a JSScope. - ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), localScopeDepth, scope, ident, Get, type); - - if (op.type == ClosureVar) - symbolTable = op.lexicalEnvironment->symbolTable(); - else if (op.type == GlobalVar) - symbolTable = m_globalObject.get()->symbolTable(); - - if (symbolTable) { - ConcurrentJITLocker locker(symbolTable->m_lock); - // If our parent scope was created while profiling was disabled, it will not have prepared for profiling yet. - symbolTable->prepareForTypeProfiling(locker); - globalVariableID = symbolTable->uniqueIDForVariable(locker, ident.impl(), *vm()); - globalTypeSet = symbolTable->globalTypeSetForVariable(locker, ident.impl(), *vm()); - } else - globalVariableID = TypeProfilerNoGlobalIDExists; - - break; - } - case ProfileTypeBytecodeLocallyResolved: { - int symbolTableIndex = pc[2].u.operand; - RELEASE_ASSERT(clonedConstantSymbolTables.contains(symbolTableIndex)); - SymbolTable* symbolTable = jsCast<SymbolTable*>(getConstant(symbolTableIndex)); - const Identifier& ident = identifier(pc[4].u.operand); - ConcurrentJITLocker locker(symbolTable->m_lock); - // If our parent scope was created while profiling was disabled, it will not have prepared for profiling yet. - globalVariableID = symbolTable->uniqueIDForVariable(locker, ident.impl(), *vm()); - globalTypeSet = symbolTable->globalTypeSetForVariable(locker, ident.impl(), *vm()); - - break; - } - case ProfileTypeBytecodeDoesNotHaveGlobalID: - case ProfileTypeBytecodeFunctionArgument: { - globalVariableID = TypeProfilerNoGlobalIDExists; - break; - } - case ProfileTypeBytecodeFunctionReturnStatement: { - RELEASE_ASSERT(ownerExecutable->isFunctionExecutable()); - globalTypeSet = jsCast<FunctionExecutable*>(ownerExecutable)->returnStatementTypeSet(); - globalVariableID = TypeProfilerReturnStatement; - if (!shouldAnalyze) { - // Because a return statement can be added implicitly to return undefined at the end of a function, - // and these nodes don't emit expression ranges because they aren't in the actual source text of - // the user's program, give the type profiler some range to identify these return statements. - // Currently, the text offset that is used as identification is on the open brace of the function - // and is stored on TypeLocation's m_divotForFunctionOffsetIfReturnStatement member variable. - divotStart = divotEnd = m_sourceOffset; - shouldAnalyze = true; - } + + case op_captured_mov: + case op_new_captured_func: { + if (pc[3].u.index == UINT_MAX) { + instructions[i + 3].u.watchpointSet = 0; break; } - } - - std::pair<TypeLocation*, bool> locationPair = vm()->typeProfiler()->typeLocationCache()->getTypeLocation(globalVariableID, - m_ownerExecutable->sourceID(), divotStart, divotEnd, globalTypeSet, vm()); - TypeLocation* location = locationPair.first; - bool isNewLocation = locationPair.second; - - if (flag == ProfileTypeBytecodeFunctionReturnStatement) - location->m_divotForFunctionOffsetIfReturnStatement = m_sourceOffset; - - if (shouldAnalyze && isNewLocation) - vm()->typeProfiler()->insertNewLocation(location); - - instructions[i + 2].u.location = location; - break; - } - - case op_debug: { - if (pc[1].u.index == DidReachBreakpoint) - m_hasDebuggerStatement = true; + StringImpl* uid = identifier(pc[3].u.index).impl(); + RELEASE_ASSERT(didCloneSymbolTable); + ConcurrentJITLocker locker(m_symbolTable->m_lock); + SymbolTable::Map::iterator iter = m_symbolTable->find(locker, uid); + ASSERT(iter != m_symbolTable->end(locker)); + iter->value.prepareToWatch(); + instructions[i + 3].u.watchpointSet = iter->value.watchpointSet(); break; } @@ -2121,11 +1844,7 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin } i += opLength; } - - if (vm()->controlFlowProfiler()) - insertBasicBlockBoundariesForControlFlowProfiler(instructions); - - m_instructions = WTF::move(instructions); + m_instructions = WTF::RefCountedArray<Instruction>(instructions); // Set optimization thresholds only after m_instructions is initialized, since these // rely on the instruction count (and are in theory permitted to also inspect the @@ -2135,14 +1854,25 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin // If the concurrent thread will want the code block's hash, then compute it here // synchronously. - if (Options::alwaysComputeHash()) + if (Options::showDisassembly() + || Options::showDFGDisassembly() + || Options::dumpBytecodeAtDFGTime() + || Options::dumpGraphAtEachPhase() + || Options::verboseCompilation() + || Options::logCompilationChanges() + || Options::validateGraph() + || Options::validateGraphAtEachPhase() + || Options::verboseOSR() + || Options::verboseCompilationQueue() + || Options::reportCompileTimes() + || Options::verboseCFA()) hash(); if (Options::dumpGeneratedBytecodes()) dumpBytecode(); - + m_heap->m_codeBlocks.add(this); - m_heap->reportExtraMemoryAllocated(sizeof(CodeBlock) + m_instructions.size() * sizeof(Instruction)); + m_heap->reportExtraMemoryCost(sizeof(CodeBlock) + m_instructions.size() * sizeof(Instruction)); } CodeBlock::~CodeBlock() @@ -2153,8 +1883,11 @@ CodeBlock::~CodeBlock() #if ENABLE(VERBOSE_VALUE_PROFILE) dumpValueProfiles(); #endif + +#if ENABLE(LLINT) while (m_incomingLLIntCalls.begin() != m_incomingLLIntCalls.end()) m_incomingLLIntCalls.begin()->remove(); +#endif // ENABLE(LLINT) #if ENABLE(JIT) // We may be destroyed before any CodeBlocks that refer to us are destroyed. // Consider that two CodeBlocks become unreachable at the same time. There @@ -2164,8 +1897,6 @@ CodeBlock::~CodeBlock() // destructor will try to remove nodes from our (no longer valid) linked list. while (m_incomingCalls.begin() != m_incomingCalls.end()) m_incomingCalls.begin()->remove(); - while (m_incomingPolymorphicCalls.begin() != m_incomingPolymorphicCalls.end()) - m_incomingPolymorphicCalls.begin()->remove(); // Note that our outgoing calls will be removed from other CodeBlocks' // m_incomingCalls linked lists through the execution of the ~CallLinkInfo @@ -2206,11 +1937,26 @@ void CodeBlock::visitAggregate(SlotVisitor& visitor) { #if ENABLE(PARALLEL_GC) // I may be asked to scan myself more than once, and it may even happen concurrently. - // To this end, use an atomic operation to check (and set) if I've been called already. - // Only one thread may proceed past this point - whichever one wins the atomic set race. - bool setByMe = m_visitAggregateHasBeenCalled.compareExchangeStrong(false, true); - if (!setByMe) - return; + // To this end, use a CAS loop to check if I've been called already. Only one thread + // may proceed past this point - whichever one wins the CAS race. + unsigned oldValue; + do { + oldValue = m_visitAggregateHasBeenCalled; + if (oldValue) { + // Looks like someone else won! Return immediately to ensure that we don't + // trace the same CodeBlock concurrently. Doing so is hazardous since we will + // be mutating the state of ValueProfiles, which contain JSValues, which can + // have word-tearing on 32-bit, leading to awesome timing-dependent crashes + // that are nearly impossible to track down. + + // Also note that it must be safe to return early as soon as we see the + // value true (well, (unsigned)1), since once a GC thread is in this method + // and has won the CAS race (i.e. was responsible for setting the value true) + // it will definitely complete the rest of this method before declaring + // termination. + return; + } + } while (!WTF::weakCompareAndSwap(&m_visitAggregateHasBeenCalled, 0, 1)); #endif // ENABLE(PARALLEL_GC) if (!!m_alternative) @@ -2219,15 +1965,15 @@ void CodeBlock::visitAggregate(SlotVisitor& visitor) if (CodeBlock* otherBlock = specialOSREntryBlockOrNull()) otherBlock->visitAggregate(visitor); - visitor.reportExtraMemoryVisited(ownerExecutable(), sizeof(CodeBlock)); + visitor.reportExtraMemoryUsage(ownerExecutable(), sizeof(CodeBlock)); if (m_jitCode) - visitor.reportExtraMemoryVisited(ownerExecutable(), m_jitCode->size()); + visitor.reportExtraMemoryUsage(ownerExecutable(), m_jitCode->size()); if (m_instructions.size()) { // Divide by refCount() because m_instructions points to something that is shared // by multiple CodeBlocks, and we only want to count it towards the heap size once. // Having each CodeBlock report only its proportional share of the size is one way // of accomplishing this. - visitor.reportExtraMemoryVisited(ownerExecutable(), m_instructions.size() * sizeof(Instruction) / m_instructions.refCount()); + visitor.reportExtraMemoryUsage(ownerExecutable(), m_instructions.size() * sizeof(Instruction) / m_instructions.refCount()); } visitor.append(&m_unlinkedCode); @@ -2238,6 +1984,10 @@ void CodeBlock::visitAggregate(SlotVisitor& visitor) // and when it runs, it figures out whether it has any work to do. visitor.addUnconditionalFinalizer(this); + // There are two things that we use weak reference harvesters for: DFG fixpoint for + // jettisoning, and trying to find structures that would be live based on some + // inline cache. So it makes sense to register them regardless. + visitor.addWeakReferenceHarvester(this); m_allTransitionsHaveBeenMarked = false; if (shouldImmediatelyAssumeLivenessDuringScan()) { @@ -2248,11 +1998,6 @@ void CodeBlock::visitAggregate(SlotVisitor& visitor) return; } - // There are two things that we use weak reference harvesters for: DFG fixpoint for - // jettisoning, and trying to find structures that would be live based on some - // inline cache. So it makes sense to register them regardless. - visitor.addWeakReferenceHarvester(this); - #if ENABLE(DFG_JIT) // We get here if we're live in the sense that our owner executable is live, // but we're not yet live for sure in another sense: we may yet decide that this @@ -2272,62 +2017,6 @@ void CodeBlock::visitAggregate(SlotVisitor& visitor) #endif // ENABLE(DFG_JIT) } -bool CodeBlock::shouldImmediatelyAssumeLivenessDuringScan() -{ -#if ENABLE(DFG_JIT) - // Interpreter and Baseline JIT CodeBlocks don't need to be jettisoned when - // their weak references go stale. So if a basline JIT CodeBlock gets - // scanned, we can assume that this means that it's live. - if (!JITCode::isOptimizingJIT(jitType())) - return true; - - // For simplicity, we don't attempt to jettison code blocks during GC if - // they are executing. Instead we strongly mark their weak references to - // allow them to continue to execute soundly. - if (m_mayBeExecuting) - return true; - - if (Options::forceDFGCodeBlockLiveness()) - return true; - - return false; -#else - return true; -#endif -} - -bool CodeBlock::isKnownToBeLiveDuringGC() -{ -#if ENABLE(DFG_JIT) - // This should return true for: - // - Code blocks that behave like normal objects - i.e. if they are referenced then they - // are live. - // - Code blocks that were running on the stack. - // - Code blocks that survived the last GC if the current GC is an Eden GC. This is - // because either livenessHasBeenProved would have survived as true or m_mayBeExecuting - // would survive as true. - // - Code blocks that don't have any dead weak references. - - return shouldImmediatelyAssumeLivenessDuringScan() - || m_jitCode->dfgCommon()->livenessHasBeenProved; -#else - return true; -#endif -} - -#if ENABLE(DFG_JIT) -static bool shouldMarkTransition(DFG::WeakReferenceTransition& transition) -{ - if (transition.m_codeOrigin && !Heap::isMarked(transition.m_codeOrigin.get())) - return false; - - if (!Heap::isMarked(transition.m_from.get())) - return false; - - return true; -} -#endif // ENABLE(DFG_JIT) - void CodeBlock::propagateTransitions(SlotVisitor& visitor) { UNUSED_PARAM(visitor); @@ -2337,6 +2026,7 @@ void CodeBlock::propagateTransitions(SlotVisitor& visitor) bool allAreMarkedSoFar = true; +#if ENABLE(LLINT) Interpreter* interpreter = m_vm->interpreter; if (jitType() == JITCode::InterpreterThunk) { const Vector<unsigned>& propertyAccessInstructions = m_unlinkedCode->propertyAccessInstructions(); @@ -2358,6 +2048,7 @@ void CodeBlock::propagateTransitions(SlotVisitor& visitor) } } } +#endif // ENABLE(LLINT) #if ENABLE(JIT) if (JITCode::isJIT(jitType())) { @@ -2404,28 +2095,21 @@ void CodeBlock::propagateTransitions(SlotVisitor& visitor) #if ENABLE(DFG_JIT) if (JITCode::isOptimizingJIT(jitType())) { DFG::CommonData* dfgCommon = m_jitCode->dfgCommon(); - for (unsigned i = 0; i < dfgCommon->transitions.size(); ++i) { - if (shouldMarkTransition(dfgCommon->transitions[i])) { + if ((!dfgCommon->transitions[i].m_codeOrigin + || Heap::isMarked(dfgCommon->transitions[i].m_codeOrigin.get())) + && Heap::isMarked(dfgCommon->transitions[i].m_from.get())) { // If the following three things are live, then the target of the // transition is also live: - // // - This code block. We know it's live already because otherwise // we wouldn't be scanning ourselves. - // // - The code origin of the transition. Transitions may arise from // code that was inlined. They are not relevant if the user's // object that is required for the inlinee to run is no longer // live. - // // - The source of the transition. The transition checks if some // heap location holds the source, and if so, stores the target. // Hence the source must be live for the transition to be live. - // - // We also short-circuit the liveness if the structure is harmless - // to mark (i.e. its global object and prototype are both already - // live). - visitor.append(&dfgCommon->transitions[i].m_to); } else allAreMarkedSoFar = false; @@ -2460,14 +2144,6 @@ void CodeBlock::determineLiveness(SlotVisitor& visitor) break; } } - if (allAreLiveSoFar) { - for (unsigned i = 0; i < dfgCommon->weakStructureReferences.size(); ++i) { - if (!Heap::isMarked(dfgCommon->weakStructureReferences[i].get())) { - allAreLiveSoFar = false; - break; - } - } - } // If some weak references are dead, then this fixpoint iteration was // unsuccessful. @@ -2533,38 +2209,28 @@ void CodeBlock::finalizeUnconditionally() if (Options::verboseOSR()) dataLogF("Clearing LLInt to_this with structure %p.\n", curInstruction[2].u.structure.get()); curInstruction[2].u.structure.clear(); - curInstruction[3].u.toThisStatus = merge( - curInstruction[3].u.toThisStatus, ToThisClearedByGC); break; - case op_create_this: { - auto& cacheWriteBarrier = curInstruction[4].u.jsCell; - if (!cacheWriteBarrier || cacheWriteBarrier.unvalidatedGet() == JSCell::seenMultipleCalleeObjects()) - break; - JSCell* cachedFunction = cacheWriteBarrier.get(); - if (Heap::isMarked(cachedFunction)) + case op_get_callee: + if (!curInstruction[2].u.jsCell || Heap::isMarked(curInstruction[2].u.jsCell.get())) break; if (Options::verboseOSR()) - dataLogF("Clearing LLInt create_this with cached callee %p.\n", cachedFunction); - cacheWriteBarrier.clear(); + dataLogF("Clearing LLInt get callee with function %p.\n", curInstruction[2].u.jsCell.get()); + curInstruction[2].u.jsCell.clear(); break; - } case op_resolve_scope: { - // Right now this isn't strictly necessary. Any symbol tables that this will refer to - // are for outer functions, and we refer to those functions strongly, and they refer - // to the symbol table strongly. But it's nice to be on the safe side. - WriteBarrierBase<SymbolTable>& symbolTable = curInstruction[6].u.symbolTable; - if (!symbolTable || Heap::isMarked(symbolTable.get())) + WriteBarrierBase<JSActivation>& activation = curInstruction[5].u.activation; + if (!activation || Heap::isMarked(activation.get())) break; if (Options::verboseOSR()) - dataLogF("Clearing dead symbolTable %p.\n", symbolTable.get()); - symbolTable.clear(); + dataLogF("Clearing dead activation %p.\n", activation.get()); + activation.clear(); break; } case op_get_from_scope: case op_put_to_scope: { ResolveModeAndType modeAndType = ResolveModeAndType(curInstruction[4].u.operand); - if (modeAndType.type() == GlobalVar || modeAndType.type() == GlobalVarWithVarInjectionChecks || modeAndType.type() == LocalClosureVar) + if (modeAndType.type() == GlobalVar || modeAndType.type() == GlobalVarWithVarInjectionChecks) continue; WriteBarrierBase<Structure>& structure = curInstruction[5].u.structure; if (!structure || Heap::isMarked(structure.get())) @@ -2575,11 +2241,11 @@ void CodeBlock::finalizeUnconditionally() break; } default: - OpcodeID opcodeID = interpreter->getOpcodeID(curInstruction[0].u.opcode); - ASSERT_WITH_MESSAGE_UNUSED(opcodeID, false, "Unhandled opcode in CodeBlock::finalizeUnconditionally, %s(%d) at bc %u", opcodeNames[opcodeID], opcodeID, propertyAccessInstructions[i]); + RELEASE_ASSERT_NOT_REACHED(); } } +#if ENABLE(LLINT) for (unsigned i = 0; i < m_llintCallLinkInfos.size(); ++i) { if (m_llintCallLinkInfos[i].isLinked() && !Heap::isMarked(m_llintCallLinkInfos[i].callee.get())) { if (Options::verboseOSR()) @@ -2589,11 +2255,12 @@ void CodeBlock::finalizeUnconditionally() if (!!m_llintCallLinkInfos[i].lastSeenCallee && !Heap::isMarked(m_llintCallLinkInfos[i].lastSeenCallee.get())) m_llintCallLinkInfos[i].lastSeenCallee.clear(); } +#endif // ENABLE(LLINT) } #if ENABLE(DFG_JIT) // Check if we're not live. If we are, then jettison. - if (!isKnownToBeLiveDuringGC()) { + if (!(shouldImmediatelyAssumeLivenessDuringScan() || m_jitCode->dfgCommon()->livenessHasBeenProved)) { if (Options::verboseOSR()) dataLog(*this, " has dead weak references, jettisoning during GC.\n"); @@ -2617,7 +2284,7 @@ void CodeBlock::finalizeUnconditionally() } } - jettison(Profiler::JettisonDueToWeakReference); + jettison(); return; } #endif // ENABLE(DFG_JIT) @@ -2626,14 +2293,39 @@ void CodeBlock::finalizeUnconditionally() // Handle inline caches. if (!!jitCode()) { RepatchBuffer repatchBuffer(this); - - for (auto iter = callLinkInfosBegin(); !!iter; ++iter) - (*iter)->visitWeak(repatchBuffer); - + for (unsigned i = 0; i < numberOfCallLinkInfos(); ++i) { + if (callLinkInfo(i).isLinked()) { + if (ClosureCallStubRoutine* stub = callLinkInfo(i).stub.get()) { + if (!Heap::isMarked(stub->structure()) + || !Heap::isMarked(stub->executable())) { + if (Options::verboseOSR()) { + dataLog( + "Clearing closure call from ", *this, " to ", + stub->executable()->hashFor(callLinkInfo(i).specializationKind()), + ", stub routine ", RawPointer(stub), ".\n"); + } + callLinkInfo(i).unlink(*m_vm, repatchBuffer); + } + } else if (!Heap::isMarked(callLinkInfo(i).callee.get())) { + if (Options::verboseOSR()) { + dataLog( + "Clearing call from ", *this, " to ", + RawPointer(callLinkInfo(i).callee.get()), " (", + callLinkInfo(i).callee.get()->executable()->hashFor( + callLinkInfo(i).specializationKind()), + ").\n"); + } + callLinkInfo(i).unlink(*m_vm, repatchBuffer); + } + } + if (!!callLinkInfo(i).lastSeenCallee + && !Heap::isMarked(callLinkInfo(i).lastSeenCallee.get())) + callLinkInfo(i).lastSeenCallee.clear(); + } for (Bag<StructureStubInfo>::iterator iter = m_stubInfos.begin(); !!iter; ++iter) { StructureStubInfo& stubInfo = **iter; - if (stubInfo.visitWeakReferences(repatchBuffer)) + if (stubInfo.visitWeakReferences()) continue; resetStubDuringGCInternal(repatchBuffer, stubInfo); @@ -2642,52 +2334,6 @@ void CodeBlock::finalizeUnconditionally() #endif } -void CodeBlock::getStubInfoMap(const ConcurrentJITLocker&, StubInfoMap& result) -{ -#if ENABLE(JIT) - toHashMap(m_stubInfos, getStructureStubInfoCodeOrigin, result); -#else - UNUSED_PARAM(result); -#endif -} - -void CodeBlock::getStubInfoMap(StubInfoMap& result) -{ - ConcurrentJITLocker locker(m_lock); - getStubInfoMap(locker, result); -} - -void CodeBlock::getCallLinkInfoMap(const ConcurrentJITLocker&, CallLinkInfoMap& result) -{ -#if ENABLE(JIT) - toHashMap(m_callLinkInfos, getCallLinkInfoCodeOrigin, result); -#else - UNUSED_PARAM(result); -#endif -} - -void CodeBlock::getCallLinkInfoMap(CallLinkInfoMap& result) -{ - ConcurrentJITLocker locker(m_lock); - getCallLinkInfoMap(locker, result); -} - -void CodeBlock::getByValInfoMap(const ConcurrentJITLocker&, ByValInfoMap& result) -{ -#if ENABLE(JIT) - for (auto* byValInfo : m_byValInfos) - result.add(CodeOrigin(byValInfo->bytecodeIndex), byValInfo); -#else - UNUSED_PARAM(result); -#endif -} - -void CodeBlock::getByValInfoMap(ByValInfoMap& result) -{ - ConcurrentJITLocker locker(m_lock); - getByValInfoMap(locker, result); -} - #if ENABLE(JIT) StructureStubInfo* CodeBlock::addStubInfo() { @@ -2695,25 +2341,9 @@ StructureStubInfo* CodeBlock::addStubInfo() return m_stubInfos.add(); } -StructureStubInfo* CodeBlock::findStubInfo(CodeOrigin codeOrigin) -{ - for (StructureStubInfo* stubInfo : m_stubInfos) { - if (stubInfo->codeOrigin == codeOrigin) - return stubInfo; - } - return nullptr; -} - -ByValInfo* CodeBlock::addByValInfo() -{ - ConcurrentJITLocker locker(m_lock); - return m_byValInfos.add(); -} - -CallLinkInfo* CodeBlock::addCallLinkInfo() +void CodeBlock::getStubInfoMap(const ConcurrentJITLocker&, StubInfoMap& result) { - ConcurrentJITLocker locker(m_lock); - return m_callLinkInfos.add(); + toHashMap(m_stubInfos, getStructureStubInfoCodeOrigin, result); } void CodeBlock::resetStub(StructureStubInfo& stubInfo) @@ -2756,21 +2386,13 @@ void CodeBlock::resetStubDuringGCInternal(RepatchBuffer& repatchBuffer, Structur resetStubInternal(repatchBuffer, stubInfo); stubInfo.resetByGC = true; } - -CallLinkInfo* CodeBlock::getCallLinkInfoForBytecodeIndex(unsigned index) -{ - for (auto iter = m_callLinkInfos.begin(); !!iter; ++iter) { - if ((*iter)->codeOrigin() == CodeOrigin(index)) - return *iter; - } - return nullptr; -} #endif void CodeBlock::stronglyVisitStrongReferences(SlotVisitor& visitor) { visitor.append(&m_globalObject); visitor.append(&m_ownerExecutable); + visitor.append(&m_symbolTable); visitor.append(&m_unlinkedCode); if (m_rareData) m_rareData->m_evalCodeCache.visitAggregate(visitor); @@ -2782,21 +2404,6 @@ void CodeBlock::stronglyVisitStrongReferences(SlotVisitor& visitor) for (unsigned i = 0; i < m_objectAllocationProfiles.size(); ++i) m_objectAllocationProfiles[i].visitAggregate(visitor); -#if ENABLE(DFG_JIT) - if (JITCode::isOptimizingJIT(jitType())) { - // FIXME: This is an antipattern for two reasons. References introduced by the DFG - // that aren't in the original CodeBlock being compiled should be weakly referenced. - // Inline call frames aren't in the original CodeBlock, so they qualify as weak. Also, - // those weak references should already be tracked in the DFG as weak FrozenValues. So, - // there is probably no need for this. We already have assertions that this should be - // unnecessary. - // https://bugs.webkit.org/show_bug.cgi?id=146613 - DFG::CommonData* dfgCommon = m_jitCode->dfgCommon(); - if (dfgCommon->inlineCallFrames.get()) - dfgCommon->inlineCallFrames->visitAggregate(visitor); - } -#endif - updateAllPredictions(); } @@ -2819,9 +2426,6 @@ void CodeBlock::stronglyVisitWeakReferences(SlotVisitor& visitor) for (unsigned i = 0; i < dfgCommon->weakReferences.size(); ++i) visitor.append(&dfgCommon->weakReferences[i]); - - for (unsigned i = 0; i < dfgCommon->weakStructureReferences.size(); ++i) - visitor.append(&dfgCommon->weakStructureReferences[i]); #endif } @@ -2870,7 +2474,66 @@ bool CodeBlock::hasOptimizedReplacement() } #endif -HandlerInfo* CodeBlock::handlerForBytecodeOffset(unsigned bytecodeOffset, RequiredHandler requiredHandler) +bool CodeBlock::isCaptured(VirtualRegister operand, InlineCallFrame* inlineCallFrame) const +{ + if (operand.isArgument()) + return operand.toArgument() && usesArguments(); + + if (inlineCallFrame) + return inlineCallFrame->capturedVars.get(operand.toLocal()); + + // The activation object isn't in the captured region, but it's "captured" + // in the sense that stores to its location can be observed indirectly. + if (needsActivation() && operand == activationRegister()) + return true; + + // Ditto for the arguments object. + if (usesArguments() && operand == argumentsRegister()) + return true; + + // Ditto for the arguments object. + if (usesArguments() && operand == unmodifiedArgumentsRegister(argumentsRegister())) + return true; + + // We're in global code so there are no locals to capture + if (!symbolTable()) + return false; + + return symbolTable()->isCaptured(operand.offset()); +} + +int CodeBlock::framePointerOffsetToGetActivationRegisters(int machineCaptureStart) +{ + // We'll be adding this to the stack pointer to get a registers pointer that looks + // like it would have looked in the baseline engine. For example, if bytecode would + // have put the first captured variable at offset -5 but we put it at offset -1, then + // we'll have an offset of 4. + int32_t offset = 0; + + // Compute where we put the captured variables. This offset will point the registers + // pointer directly at the first captured var. + offset += machineCaptureStart; + + // Now compute the offset needed to make the runtime see the captured variables at the + // same offset that the bytecode would have used. + offset -= symbolTable()->captureStart(); + + return offset; +} + +int CodeBlock::framePointerOffsetToGetActivationRegisters() +{ + if (!JITCode::isOptimizingJIT(jitType())) + return 0; +#if ENABLE(DFG_JIT) + return framePointerOffsetToGetActivationRegisters(jitCode()->dfgCommon()->machineCaptureStart); +#else + RELEASE_ASSERT_NOT_REACHED(); + return 0; +#endif +} + +HandlerInfo* CodeBlock::handlerForBytecodeOffset(unsigned bytecodeOffset) { RELEASE_ASSERT(bytecodeOffset < instructions().size()); @@ -2879,14 +2542,10 @@ HandlerInfo* CodeBlock::handlerForBytecodeOffset(unsigned bytecodeOffset, Requir Vector<HandlerInfo>& exceptionHandlers = m_rareData->m_exceptionHandlers; for (size_t i = 0; i < exceptionHandlers.size(); ++i) { - HandlerInfo& handler = exceptionHandlers[i]; - if ((requiredHandler == RequiredHandler::CatchHandler) && !handler.isCatchHandler()) - continue; - // Handlers are ordered innermost first, so the first handler we encounter // that contains the source address is the correct handler to use. - if (handler.start <= bytecodeOffset && handler.end > bytecodeOffset) - return &handler; + if (exceptionHandlers[i].start <= bytecodeOffset && exceptionHandlers[i].end > bytecodeOffset) + return &exceptionHandlers[i]; } return 0; @@ -2895,7 +2554,7 @@ HandlerInfo* CodeBlock::handlerForBytecodeOffset(unsigned bytecodeOffset, Requir unsigned CodeBlock::lineNumberForBytecodeOffset(unsigned bytecodeOffset) { RELEASE_ASSERT(bytecodeOffset < instructions().size()); - return m_ownerExecutable->firstLine() + m_unlinkedCode->lineNumberForBytecodeOffset(bytecodeOffset); + return m_ownerExecutable->lineNo() + m_unlinkedCode->lineNumberForBytecodeOffset(bytecodeOffset); } unsigned CodeBlock::columnNumberForBytecodeOffset(unsigned bytecodeOffset) @@ -2914,7 +2573,7 @@ void CodeBlock::expressionRangeForBytecodeOffset(unsigned bytecodeOffset, int& d m_unlinkedCode->expressionRangeForBytecodeOffset(bytecodeOffset, divot, startOffset, endOffset, line, column); divot += m_sourceOffset; column += line ? 1 : firstLineColumnOffset(); - line += m_ownerExecutable->firstLine(); + line += m_ownerExecutable->lineNo(); } bool CodeBlock::hasOpDebugForLineAndColumn(unsigned line, unsigned column) @@ -2945,7 +2604,6 @@ void CodeBlock::shrinkToFit(ShrinkMode shrinkMode) if (shrinkMode == EarlyShrink) { m_constantRegisters.shrinkToFit(); - m_constantsSourceCodeRepresentation.shrinkToFit(); if (m_rareData) { m_rareData->m_switchJumpTables.shrinkToFit(); @@ -2954,25 +2612,47 @@ void CodeBlock::shrinkToFit(ShrinkMode shrinkMode) } // else don't shrink these, because we would have already pointed pointers into these tables. } +unsigned CodeBlock::addOrFindConstant(JSValue v) +{ + unsigned result; + if (findConstant(v, result)) + return result; + return addConstant(v); +} + +bool CodeBlock::findConstant(JSValue v, unsigned& index) +{ + unsigned numberOfConstants = numberOfConstantRegisters(); + for (unsigned i = 0; i < numberOfConstants; ++i) { + if (getConstant(FirstConstantRegisterIndex + i) == v) { + index = i; + return true; + } + } + index = numberOfConstants; + return false; +} + #if ENABLE(JIT) void CodeBlock::unlinkCalls() { if (!!m_alternative) m_alternative->unlinkCalls(); +#if ENABLE(LLINT) for (size_t i = 0; i < m_llintCallLinkInfos.size(); ++i) { if (m_llintCallLinkInfos[i].isLinked()) m_llintCallLinkInfos[i].unlink(); } - if (m_callLinkInfos.isEmpty()) +#endif + if (!m_callLinkInfos.size()) return; if (!m_vm->canUseJIT()) return; RepatchBuffer repatchBuffer(this); - for (auto iter = m_callLinkInfos.begin(); !!iter; ++iter) { - CallLinkInfo& info = **iter; - if (!info.isLinked()) + for (size_t i = 0; i < m_callLinkInfos.size(); i++) { + if (!m_callLinkInfos[i].isLinked()) continue; - info.unlink(repatchBuffer); + m_callLinkInfos[i].unlink(*m_vm, repatchBuffer); } } @@ -2981,34 +2661,41 @@ void CodeBlock::linkIncomingCall(ExecState* callerFrame, CallLinkInfo* incoming) noticeIncomingCall(callerFrame); m_incomingCalls.push(incoming); } - -void CodeBlock::linkIncomingPolymorphicCall(ExecState* callerFrame, PolymorphicCallNode* incoming) -{ - noticeIncomingCall(callerFrame); - m_incomingPolymorphicCalls.push(incoming); -} #endif // ENABLE(JIT) void CodeBlock::unlinkIncomingCalls() { +#if ENABLE(LLINT) while (m_incomingLLIntCalls.begin() != m_incomingLLIntCalls.end()) m_incomingLLIntCalls.begin()->unlink(); +#endif // ENABLE(LLINT) #if ENABLE(JIT) - if (m_incomingCalls.isEmpty() && m_incomingPolymorphicCalls.isEmpty()) + if (m_incomingCalls.isEmpty()) return; RepatchBuffer repatchBuffer(this); while (m_incomingCalls.begin() != m_incomingCalls.end()) - m_incomingCalls.begin()->unlink(repatchBuffer); - while (m_incomingPolymorphicCalls.begin() != m_incomingPolymorphicCalls.end()) - m_incomingPolymorphicCalls.begin()->unlink(repatchBuffer); + m_incomingCalls.begin()->unlink(*m_vm, repatchBuffer); #endif // ENABLE(JIT) } +#if ENABLE(LLINT) void CodeBlock::linkIncomingCall(ExecState* callerFrame, LLIntCallLinkInfo* incoming) { noticeIncomingCall(callerFrame); m_incomingLLIntCalls.push(incoming); } +#endif // ENABLE(LLINT) + +void CodeBlock::clearEvalCache() +{ + if (!!m_alternative) + m_alternative->clearEvalCache(); + if (CodeBlock* otherBlock = specialOSREntryBlockOrNull()) + otherBlock->clearEvalCache(); + if (!m_rareData) + return; + m_rareData->m_evalCodeCache.clear(); +} void CodeBlock::install() { @@ -3020,6 +2707,18 @@ PassRefPtr<CodeBlock> CodeBlock::newReplacement() return ownerExecutable()->newReplacementCodeBlockFor(specializationKind()); } +const SlowArgument* CodeBlock::machineSlowArguments() +{ + if (!JITCode::isOptimizingJIT(jitType())) + return symbolTable()->slowArguments(); + +#if ENABLE(DFG_JIT) + return jitCode()->dfgCommon()->slowArguments.get(); +#else // ENABLE(DFG_JIT) + return 0; +#endif // ENABLE(DFG_JIT) +} + #if ENABLE(JIT) CodeBlock* ProgramCodeBlock::replacement() { @@ -3054,27 +2753,19 @@ DFG::CapabilityLevel FunctionCodeBlock::capabilityLevelInternal() } #endif -void CodeBlock::jettison(Profiler::JettisonReason reason, ReoptimizationMode mode, const FireDetail* detail) +void CodeBlock::jettison(ReoptimizationMode mode) { - RELEASE_ASSERT(reason != Profiler::NotJettisoned); - #if ENABLE(DFG_JIT) if (DFG::shouldShowDisassembly()) { dataLog("Jettisoning ", *this); if (mode == CountReoptimization) dataLog(" and counting reoptimization"); - dataLog(" due to ", reason); - if (detail) - dataLog(", ", *detail); dataLog(".\n"); } DeferGCForAWhile deferGC(*m_heap); RELEASE_ASSERT(JITCode::isOptimizingJIT(jitType())); - if (Profiler::Compilation* compilation = jitCode()->dfgCommon()->compilation.get()) - compilation->setJettisonReason(reason, detail); - // We want to accomplish two things here: // 1) Make sure that if this CodeBlock is on the stack right now, then if we return to it // we should OSR exit at the top of the next bytecode instruction after the return. @@ -3114,7 +2805,6 @@ void CodeBlock::jettison(Profiler::JettisonReason reason, ReoptimizationMode mod dataLog(" Did install baseline version of ", *this, "\n"); #else // ENABLE(DFG_JIT) UNUSED_PARAM(mode); - UNUSED_PARAM(detail); UNREACHABLE_FOR_PLATFORM(); #endif // ENABLE(DFG_JIT) } @@ -3126,64 +2816,17 @@ JSGlobalObject* CodeBlock::globalObjectFor(CodeOrigin codeOrigin) return jsCast<FunctionExecutable*>(codeOrigin.inlineCallFrame->executable.get())->eitherCodeBlock()->globalObject(); } -class RecursionCheckFunctor { -public: - RecursionCheckFunctor(CallFrame* startCallFrame, CodeBlock* codeBlock, unsigned depthToCheck) - : m_startCallFrame(startCallFrame) - , m_codeBlock(codeBlock) - , m_depthToCheck(depthToCheck) - , m_foundStartCallFrame(false) - , m_didRecurse(false) - { } - - StackVisitor::Status operator()(StackVisitor& visitor) - { - CallFrame* currentCallFrame = visitor->callFrame(); - - if (currentCallFrame == m_startCallFrame) - m_foundStartCallFrame = true; - - if (m_foundStartCallFrame) { - if (visitor->callFrame()->codeBlock() == m_codeBlock) { - m_didRecurse = true; - return StackVisitor::Done; - } - - if (!m_depthToCheck--) - return StackVisitor::Done; - } - - return StackVisitor::Continue; - } - - bool didRecurse() const { return m_didRecurse; } - -private: - CallFrame* m_startCallFrame; - CodeBlock* m_codeBlock; - unsigned m_depthToCheck; - bool m_foundStartCallFrame; - bool m_didRecurse; -}; - void CodeBlock::noticeIncomingCall(ExecState* callerFrame) { CodeBlock* callerCodeBlock = callerFrame->codeBlock(); if (Options::verboseCallLink()) - dataLog("Noticing call link from ", pointerDump(callerCodeBlock), " to ", *this, "\n"); + dataLog("Noticing call link from ", *callerCodeBlock, " to ", *this, "\n"); -#if ENABLE(DFG_JIT) if (!m_shouldAlwaysBeInlined) return; - - if (!callerCodeBlock) { - m_shouldAlwaysBeInlined = false; - if (Options::verboseCallLink()) - dataLog(" Clearing SABI because caller is native.\n"); - return; - } +#if ENABLE(DFG_JIT) if (!hasBaselineJITProfiling()) return; @@ -3192,13 +2835,6 @@ void CodeBlock::noticeIncomingCall(ExecState* callerFrame) if (!canInline(m_capabilityLevelState)) return; - - if (!DFG::isSmallEnoughToInlineCodeInto(callerCodeBlock)) { - m_shouldAlwaysBeInlined = false; - if (Options::verboseCallLink()) - dataLog(" Clearing SABI because caller is too large.\n"); - return; - } if (callerCodeBlock->jitType() == JITCode::InterpreterThunk) { // If the caller is still in the interpreter, then we can't expect inlining to @@ -3207,14 +2843,7 @@ void CodeBlock::noticeIncomingCall(ExecState* callerFrame) // any of its callers. m_shouldAlwaysBeInlined = false; if (Options::verboseCallLink()) - dataLog(" Clearing SABI because caller is in LLInt.\n"); - return; - } - - if (JITCode::isOptimizingJIT(callerCodeBlock->jitType())) { - m_shouldAlwaysBeInlined = false; - if (Options::verboseCallLink()) - dataLog(" Clearing SABI bcause caller was already optimized.\n"); + dataLog(" Marking SABI because caller is in LLInt.\n"); return; } @@ -3224,47 +2853,42 @@ void CodeBlock::noticeIncomingCall(ExecState* callerFrame) // delay eval optimization by a *lot*. m_shouldAlwaysBeInlined = false; if (Options::verboseCallLink()) - dataLog(" Clearing SABI because caller is not a function.\n"); - return; - } - - // Recursive calls won't be inlined. - RecursionCheckFunctor functor(callerFrame, this, Options::maximumInliningDepth()); - vm()->topCallFrame->iterate(functor); - - if (functor.didRecurse()) { - if (Options::verboseCallLink()) - dataLog(" Clearing SABI because recursion was detected.\n"); - m_shouldAlwaysBeInlined = false; + dataLog(" Marking SABI because caller is not a function.\n"); return; } - if (callerCodeBlock->m_capabilityLevelState == DFG::CapabilityLevelNotSet) { - dataLog("In call from ", *callerCodeBlock, " ", callerFrame->codeOrigin(), " to ", *this, ": caller's DFG capability level is not set.\n"); - CRASH(); + ExecState* frame = callerFrame; + for (unsigned i = Options::maximumInliningDepth(); i--; frame = frame->callerFrame()) { + if (frame->isVMEntrySentinel()) + break; + if (frame->codeBlock() == this) { + // Recursive calls won't be inlined. + if (Options::verboseCallLink()) + dataLog(" Marking SABI because recursion was detected.\n"); + m_shouldAlwaysBeInlined = false; + return; + } } + RELEASE_ASSERT(callerCodeBlock->m_capabilityLevelState != DFG::CapabilityLevelNotSet); + if (canCompile(callerCodeBlock->m_capabilityLevelState)) return; if (Options::verboseCallLink()) - dataLog(" Clearing SABI because the caller is not a DFG candidate.\n"); + dataLog(" Marking SABI because the caller is not a DFG candidate.\n"); m_shouldAlwaysBeInlined = false; #endif } +#if ENABLE(JIT) unsigned CodeBlock::reoptimizationRetryCounter() const { -#if ENABLE(JIT) ASSERT(m_reoptimizationRetryCounter <= Options::reoptimizationRetryCounterMax()); return m_reoptimizationRetryCounter; -#else - return 0; -#endif // ENABLE(JIT) } -#if ENABLE(JIT) void CodeBlock::countReoptimization() { m_reoptimizationRetryCounter++; @@ -3275,11 +2899,6 @@ void CodeBlock::countReoptimization() unsigned CodeBlock::numberOfDFGCompiles() { ASSERT(JITCode::isBaselineCode(jitType())); - if (Options::testTheFTL()) { - if (m_didFailFTLCompilation) - return 1000000; - return (m_hasBeenCompiledWithFTL ? 1 : 0) + m_reoptimizationRetryCounter; - } return (JITCode::isOptimizingJIT(replacement()->jitType()) ? 1 : 0) + m_reoptimizationRetryCounter; } @@ -3360,16 +2979,13 @@ double CodeBlock::optimizationThresholdScalingFactor() ASSERT(instructionCount); // Make sure this is called only after we have an instruction stream; otherwise it'll just return the value of d, which makes no sense. double result = d + a * sqrt(instructionCount + b) + c * instructionCount; - - result *= codeTypeThresholdMultiplier(); - if (Options::verboseOSR()) { dataLog( *this, ": instruction count is ", instructionCount, ", scaling execution counter by ", result, " * ", codeTypeThresholdMultiplier(), "\n"); } - return result; + return result * codeTypeThresholdMultiplier(); } static int32_t clipThreshold(double threshold) @@ -3394,7 +3010,7 @@ int32_t CodeBlock::adjustedCounterValue(int32_t desiredThreshold) bool CodeBlock::checkIfOptimizationThresholdReached() { #if ENABLE(DFG_JIT) - if (DFG::Worklist* worklist = DFG::existingGlobalDFGWorklistOrNull()) { + if (DFG::Worklist* worklist = m_vm->worklist.get()) { if (worklist->compilationState(DFG::CompilationKey(this, DFG::DFGMode)) == DFG::Worklist::Compiled) { optimizeNextInvocation(); @@ -3460,22 +3076,8 @@ void CodeBlock::forceOptimizationSlowPathConcurrently() #if ENABLE(DFG_JIT) void CodeBlock::setOptimizationThresholdBasedOnCompilationResult(CompilationResult result) { - JITCode::JITType type = jitType(); - if (type != JITCode::BaselineJIT) { - dataLog(*this, ": expected to have baseline code but have ", type, "\n"); - RELEASE_ASSERT_NOT_REACHED(); - } - - CodeBlock* theReplacement = replacement(); - if ((result == CompilationSuccessful) != (theReplacement != this)) { - dataLog(*this, ": we have result = ", result, " but "); - if (theReplacement == this) - dataLog("we are our own replacement.\n"); - else - dataLog("our replacement is ", pointerDump(theReplacement), "\n"); - RELEASE_ASSERT_NOT_REACHED(); - } - + RELEASE_ASSERT(jitType() == JITCode::BaselineJIT); + RELEASE_ASSERT((result == CompilationSuccessful) == (replacement() != this)); switch (result) { case CompilationSuccessful: RELEASE_ASSERT(JITCode::isOptimizingJIT(replacement()->jitType())); @@ -3498,8 +3100,6 @@ void CodeBlock::setOptimizationThresholdBasedOnCompilationResult(CompilationResu optimizeAfterWarmUp(); return; } - - dataLog("Unrecognized result: ", static_cast<int>(result), "\n"); RELEASE_ASSERT_NOT_REACHED(); } @@ -3655,7 +3255,9 @@ void CodeBlock::tallyFrequentExitSites() DFG::JITCode* jitCode = m_jitCode->dfg(); for (unsigned i = 0; i < jitCode->osrExit.size(); ++i) { DFG::OSRExit& exit = jitCode->osrExit[i]; - exit.considerAddingAsFrequentExitSite(profiledBlock); + + if (!exit.considerAddingAsFrequentExitSite(profiledBlock)) + continue; } break; } @@ -3668,7 +3270,9 @@ void CodeBlock::tallyFrequentExitSites() FTL::JITCode* jitCode = m_jitCode->ftl(); for (unsigned i = 0; i < jitCode->osrExit.size(); ++i) { FTL::OSRExit& exit = jitCode->osrExit[i]; - exit.considerAddingAsFrequentExitSite(profiledBlock); + + if (!exit.considerAddingAsFrequentExitSite(profiledBlock)) + continue; } break; } @@ -3715,8 +3319,10 @@ void CodeBlock::dumpValueProfiles() unsigned CodeBlock::frameRegisterCount() { switch (jitType()) { +#if ENABLE(LLINT) case JITCode::InterpreterThunk: return LLInt::frameRegisterCountFor(this); +#endif // ENABLE(LLINT) #if ENABLE(JIT) case JITCode::BaselineJIT: @@ -3735,11 +3341,6 @@ unsigned CodeBlock::frameRegisterCount() } } -int CodeBlock::stackPointerOffset() -{ - return virtualRegisterForLocal(frameRegisterCount() - 1).offset(); -} - size_t CodeBlock::predictedMachineCodeSize() { // This will be called from CodeBlock::CodeBlock before either m_vm or the @@ -3799,40 +3400,72 @@ bool CodeBlock::usesOpcode(OpcodeID opcodeID) String CodeBlock::nameForRegister(VirtualRegister virtualRegister) { - for (unsigned i = 0; i < m_constantRegisters.size(); i++) { - if (m_constantRegisters[i].get().isEmpty()) - continue; - if (SymbolTable* symbolTable = jsDynamicCast<SymbolTable*>(m_constantRegisters[i].get())) { - ConcurrentJITLocker locker(symbolTable->m_lock); - auto end = symbolTable->end(locker); - for (auto ptr = symbolTable->begin(locker); ptr != end; ++ptr) { - if (ptr->value.varOffset() == VarOffset(virtualRegister)) { - // FIXME: This won't work from the compilation thread. - // https://bugs.webkit.org/show_bug.cgi?id=115300 - return ptr->key.get(); - } - } + ConcurrentJITLocker locker(symbolTable()->m_lock); + SymbolTable::Map::iterator end = symbolTable()->end(locker); + for (SymbolTable::Map::iterator ptr = symbolTable()->begin(locker); ptr != end; ++ptr) { + if (ptr->value.getIndex() == virtualRegister.offset()) { + // FIXME: This won't work from the compilation thread. + // https://bugs.webkit.org/show_bug.cgi?id=115300 + return String(ptr->key); } } + if (needsActivation() && virtualRegister == activationRegister()) + return ASCIILiteral("activation"); if (virtualRegister == thisRegister()) return ASCIILiteral("this"); + if (usesArguments()) { + if (virtualRegister == argumentsRegister()) + return ASCIILiteral("arguments"); + if (unmodifiedArgumentsRegister(argumentsRegister()) == virtualRegister) + return ASCIILiteral("real arguments"); + } if (virtualRegister.isArgument()) - return String::format("arguments[%3d]", virtualRegister.toArgument()); + return String::format("arguments[%3d]", virtualRegister.toArgument()).impl(); return ""; } -ValueProfile* CodeBlock::valueProfileForBytecodeOffset(int bytecodeOffset) -{ - ValueProfile* result = binarySearch<ValueProfile, int>( - m_valueProfiles, m_valueProfiles.size(), bytecodeOffset, - getValueProfileBytecodeOffset<ValueProfile>); - ASSERT(result->m_bytecodeOffset != -1); - ASSERT(instructions()[bytecodeOffset + opcodeLength( - m_vm->interpreter->getOpcodeID( - instructions()[bytecodeOffset].u.opcode)) - 1].u.profile == result); - return result; -} +namespace { + +struct VerifyCapturedDef { + void operator()(CodeBlock* codeBlock, Instruction* instruction, OpcodeID opcodeID, int operand) + { + unsigned bytecodeOffset = instruction - codeBlock->instructions().begin(); + + if (codeBlock->isConstantRegisterIndex(operand)) { + codeBlock->beginValidationDidFail(); + dataLog(" At bc#", bytecodeOffset, " encountered a definition of a constant.\n"); + codeBlock->endValidationDidFail(); + return; + } + + switch (opcodeID) { + case op_enter: + case op_captured_mov: + case op_init_lazy_reg: + case op_create_arguments: + case op_new_captured_func: + return; + default: + break; + } + + VirtualRegister virtualReg(operand); + if (!virtualReg.isLocal()) + return; + + if (codeBlock->captureCount() && codeBlock->symbolTable()->isCaptured(operand)) { + codeBlock->beginValidationDidFail(); + dataLog(" At bc#", bytecodeOffset, " encountered invalid assignment to captured variable loc", virtualReg.toLocal(), ".\n"); + codeBlock->endValidationDidFail(); + return; + } + + return; + } +}; + +} // anonymous namespace void CodeBlock::validate() { @@ -3849,15 +3482,38 @@ void CodeBlock::validate() } for (unsigned i = m_numCalleeRegisters; i--;) { + bool isCaptured = false; VirtualRegister reg = virtualRegisterForLocal(i); - if (liveAtHead.get(i)) { - beginValidationDidFail(); - dataLog(" Variable ", reg, " is expected to be dead.\n"); - dataLog(" Result: ", liveAtHead, "\n"); - endValidationDidFail(); + if (captureCount()) + isCaptured = reg.offset() <= captureStart() && reg.offset() > captureEnd(); + + if (isCaptured) { + if (!liveAtHead.get(i)) { + beginValidationDidFail(); + dataLog(" Variable loc", i, " is expected to be live because it is captured, but it isn't live.\n"); + dataLog(" Result: ", liveAtHead, "\n"); + endValidationDidFail(); + } + } else { + if (liveAtHead.get(i)) { + beginValidationDidFail(); + dataLog(" Variable loc", i, " is expected to be dead.\n"); + dataLog(" Result: ", liveAtHead, "\n"); + endValidationDidFail(); + } } } + + for (unsigned bytecodeOffset = 0; bytecodeOffset < instructions().size();) { + Instruction* currentInstruction = instructions().begin() + bytecodeOffset; + OpcodeID opcodeID = m_vm->interpreter->getOpcodeID(currentInstruction->u.opcode); + + VerifyCapturedDef verifyCapturedDef; + computeDefsForBytecodeOffset(this, bytecodeOffset, verifyCapturedDef); + + bytecodeOffset += opcodeLength(opcodeID); + } } void CodeBlock::beginValidationDidFail() @@ -3879,98 +3535,15 @@ void CodeBlock::addBreakpoint(unsigned numBreakpoints) { m_numBreakpoints += numBreakpoints; ASSERT(m_numBreakpoints); - if (JITCode::isOptimizingJIT(jitType())) - jettison(Profiler::JettisonDueToDebuggerBreakpoint); + if (jitType() == JITCode::DFGJIT) + jettison(); } void CodeBlock::setSteppingMode(CodeBlock::SteppingMode mode) { m_steppingMode = mode; - if (mode == SteppingModeEnabled && JITCode::isOptimizingJIT(jitType())) - jettison(Profiler::JettisonDueToDebuggerStepping); -} - -RareCaseProfile* CodeBlock::rareCaseProfileForBytecodeOffset(int bytecodeOffset) -{ - return tryBinarySearch<RareCaseProfile, int>( - m_rareCaseProfiles, m_rareCaseProfiles.size(), bytecodeOffset, - getRareCaseProfileBytecodeOffset); -} - -#if ENABLE(JIT) -DFG::CapabilityLevel CodeBlock::capabilityLevel() -{ - DFG::CapabilityLevel result = capabilityLevelInternal(); - m_capabilityLevelState = result; - return result; -} -#endif - -void CodeBlock::insertBasicBlockBoundariesForControlFlowProfiler(RefCountedArray<Instruction>& instructions) -{ - const Vector<size_t>& bytecodeOffsets = unlinkedCodeBlock()->opProfileControlFlowBytecodeOffsets(); - for (size_t i = 0, offsetsLength = bytecodeOffsets.size(); i < offsetsLength; i++) { - // Because op_profile_control_flow is emitted at the beginning of every basic block, finding - // the next op_profile_control_flow will give us the text range of a single basic block. - size_t startIdx = bytecodeOffsets[i]; - RELEASE_ASSERT(vm()->interpreter->getOpcodeID(instructions[startIdx].u.opcode) == op_profile_control_flow); - int basicBlockStartOffset = instructions[startIdx + 1].u.operand; - int basicBlockEndOffset; - if (i + 1 < offsetsLength) { - size_t endIdx = bytecodeOffsets[i + 1]; - RELEASE_ASSERT(vm()->interpreter->getOpcodeID(instructions[endIdx].u.opcode) == op_profile_control_flow); - basicBlockEndOffset = instructions[endIdx + 1].u.operand - 1; - } else { - basicBlockEndOffset = m_sourceOffset + m_ownerExecutable->source().length() - 1; // Offset before the closing brace. - basicBlockStartOffset = std::min(basicBlockStartOffset, basicBlockEndOffset); // Some start offsets may be at the closing brace, ensure it is the offset before. - } - - // The following check allows for the same textual JavaScript basic block to have its bytecode emitted more - // than once and still play nice with the control flow profiler. When basicBlockStartOffset is larger than - // basicBlockEndOffset, it indicates that the bytecode generator has emitted code for the same AST node - // more than once (for example: ForInNode, Finally blocks in TryNode, etc). Though these are different - // basic blocks at the bytecode level, they are generated from the same textual basic block in the JavaScript - // program. The condition: - // (basicBlockEndOffset < basicBlockStartOffset) - // is encountered when op_profile_control_flow lies across the boundary of these duplicated bytecode basic - // blocks and the textual offset goes from the end of the duplicated block back to the beginning. These - // ranges are dummy ranges and are ignored. The duplicated bytecode basic blocks point to the same - // internal data structure, so if any of them execute, it will record the same textual basic block in the - // JavaScript program as executing. - // At the bytecode level, this situation looks like: - // j: op_profile_control_flow (from j->k, we have basicBlockEndOffset < basicBlockStartOffset) - // ... - // k: op_profile_control_flow (we want to skip over the j->k block and start fresh at offset k as the start of a new basic block k->m). - // ... - // m: op_profile_control_flow - if (basicBlockEndOffset < basicBlockStartOffset) { - RELEASE_ASSERT(i + 1 < offsetsLength); // We should never encounter dummy blocks at the end of a CodeBlock. - instructions[startIdx + 1].u.basicBlockLocation = vm()->controlFlowProfiler()->dummyBasicBlock(); - continue; - } - - BasicBlockLocation* basicBlockLocation = vm()->controlFlowProfiler()->getBasicBlockLocation(m_ownerExecutable->sourceID(), basicBlockStartOffset, basicBlockEndOffset); - - // Find all functions that are enclosed within the range: [basicBlockStartOffset, basicBlockEndOffset] - // and insert these functions' start/end offsets as gaps in the current BasicBlockLocation. - // This is necessary because in the original source text of a JavaScript program, - // function literals form new basic blocks boundaries, but they aren't represented - // inside the CodeBlock's instruction stream. - auto insertFunctionGaps = [basicBlockLocation, basicBlockStartOffset, basicBlockEndOffset] (const WriteBarrier<FunctionExecutable>& functionExecutable) { - const UnlinkedFunctionExecutable* executable = functionExecutable->unlinkedExecutable(); - int functionStart = executable->typeProfilingStartOffset(); - int functionEnd = executable->typeProfilingEndOffset(); - if (functionStart >= basicBlockStartOffset && functionEnd <= basicBlockEndOffset) - basicBlockLocation->insertGap(functionStart, functionEnd); - }; - - for (const WriteBarrier<FunctionExecutable>& executable : m_functionDecls) - insertFunctionGaps(executable); - for (const WriteBarrier<FunctionExecutable>& executable : m_functionExprs) - insertFunctionGaps(executable); - - instructions[startIdx + 1].u.basicBlockLocation = basicBlockLocation; - } + if (mode == SteppingModeEnabled && jitType() == JITCode::DFGJIT) + jettison(); } } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.h b/Source/JavaScriptCore/bytecode/CodeBlock.h index 9c78eed13..0d9868079 100644 --- a/Source/JavaScriptCore/bytecode/CodeBlock.h +++ b/Source/JavaScriptCore/bytecode/CodeBlock.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2015 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Apple Inc. All rights reserved. * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca> * * Redistribution and use in source and binary forms, with or without @@ -11,7 +11,7 @@ * 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 + * 3. Neither the name of Apple Computer, 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. * @@ -45,6 +45,10 @@ #include "DFGCommon.h" #include "DFGCommonData.h" #include "DFGExitProfile.h" +#include "DFGMinifiedGraph.h" +#include "DFGOSREntry.h" +#include "DFGOSRExit.h" +#include "DFGVariableEventStream.h" #include "DeferredCompilationCallback.h" #include "EvalCodeCache.h" #include "ExecutionCounter.h" @@ -52,6 +56,7 @@ #include "HandlerInfo.h" #include "ObjectAllocationProfile.h" #include "Options.h" +#include "Operations.h" #include "PutPropertySlot.h" #include "Instruction.h" #include "JITCode.h" @@ -61,7 +66,6 @@ #include "LLIntCallLinkInfo.h" #include "LazyOperandValueProfile.h" #include "ProfilerCompilation.h" -#include "ProfilerJettisonReason.h" #include "RegExpObject.h" #include "StructureStubInfo.h" #include "UnconditionalFinalizer.h" @@ -70,6 +74,7 @@ #include "Watchpoint.h" #include <wtf/Bag.h> #include <wtf/FastMalloc.h> +#include <wtf/PassOwnPtr.h> #include <wtf/RefCountedArray.h> #include <wtf/RefPtr.h> #include <wtf/SegmentedVector.h> @@ -81,7 +86,10 @@ namespace JSC { class ExecState; class LLIntOffsetsExtractor; class RepatchBuffer; -class TypeLocation; + +inline VirtualRegister unmodifiedArgumentsRegister(VirtualRegister argumentsRegister) { return VirtualRegister(argumentsRegister.offset() + 1); } + +static ALWAYS_INLINE int missingThisObjectMarker() { return std::numeric_limits<int>::max(); } enum ReoptimizationMode { DontCountReoptimization, CountReoptimization }; @@ -109,7 +117,6 @@ public: CodeBlockHash hash() const; bool hasHash() const; bool isSafeToComputeHash() const; - CString hashAsStringIfPossible() const; CString sourceCodeForTools() const; // Not quite the actual source we parsed; this will do things like prefix the source for a function with a reified signature. CString sourceCodeOnOneLine() const; // As sourceCodeForTools(), but replaces all whitespace runs with a single space. void dumpAssumingJITType(PrintStream&, JITCode::JITType) const; @@ -124,23 +131,6 @@ public: CodeBlock* alternative() { return m_alternative.get(); } PassRefPtr<CodeBlock> releaseAlternative() { return m_alternative.release(); } void setAlternative(PassRefPtr<CodeBlock> alternative) { m_alternative = alternative; } - - template <typename Functor> void forEachRelatedCodeBlock(Functor&& functor) - { - Functor f(std::forward<Functor>(functor)); - Vector<CodeBlock*, 4> codeBlocks; - codeBlocks.append(this); - - while (!codeBlocks.isEmpty()) { - CodeBlock* currentCodeBlock = codeBlocks.takeLast(); - f(currentCodeBlock); - - if (CodeBlock* alternative = currentCodeBlock->alternative()) - codeBlocks.append(alternative); - if (CodeBlock* osrEntryBlock = currentCodeBlock->specialOSREntryBlockOrNull()) - codeBlocks.append(osrEntryBlock); - } - } CodeSpecializationKind specializationKind() const { @@ -155,14 +145,8 @@ public: void visitAggregate(SlotVisitor&); - void dumpSource(); - void dumpSource(PrintStream&); - - void dumpBytecode(); - void dumpBytecode(PrintStream&); - void dumpBytecode( - PrintStream&, unsigned bytecodeOffset, - const StubInfoMap& = StubInfoMap(), const CallLinkInfoMap& = CallLinkInfoMap()); + void dumpBytecode(PrintStream& = WTF::dataFile()); + void dumpBytecode(PrintStream&, unsigned bytecodeOffset); void printStructures(PrintStream&, const Instruction*); void printStructure(PrintStream&, const char* name, const Instruction*, int operand); @@ -185,46 +169,36 @@ public: return index >= m_numVars; } - enum class RequiredHandler { - CatchHandler, - AnyHandler - }; - HandlerInfo* handlerForBytecodeOffset(unsigned bytecodeOffset, RequiredHandler = RequiredHandler::AnyHandler); + HandlerInfo* handlerForBytecodeOffset(unsigned bytecodeOffset); unsigned lineNumberForBytecodeOffset(unsigned bytecodeOffset); unsigned columnNumberForBytecodeOffset(unsigned bytecodeOffset); void expressionRangeForBytecodeOffset(unsigned bytecodeOffset, int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column); - void getStubInfoMap(const ConcurrentJITLocker&, StubInfoMap& result); - void getStubInfoMap(StubInfoMap& result); - - void getCallLinkInfoMap(const ConcurrentJITLocker&, CallLinkInfoMap& result); - void getCallLinkInfoMap(CallLinkInfoMap& result); - - void getByValInfoMap(const ConcurrentJITLocker&, ByValInfoMap& result); - void getByValInfoMap(ByValInfoMap& result); - #if ENABLE(JIT) StructureStubInfo* addStubInfo(); - Bag<StructureStubInfo>::iterator stubInfoBegin() { return m_stubInfos.begin(); } - Bag<StructureStubInfo>::iterator stubInfoEnd() { return m_stubInfos.end(); } - - // O(n) operation. Use getStubInfoMap() unless you really only intend to get one - // stub info. - StructureStubInfo* findStubInfo(CodeOrigin); + Bag<StructureStubInfo>::iterator begin() { return m_stubInfos.begin(); } + Bag<StructureStubInfo>::iterator end() { return m_stubInfos.end(); } void resetStub(StructureStubInfo&); + + void getStubInfoMap(const ConcurrentJITLocker&, StubInfoMap& result); - ByValInfo* addByValInfo(); + ByValInfo& getByValInfo(unsigned bytecodeIndex) + { + return *(binarySearch<ByValInfo, unsigned>(m_byValInfos, m_byValInfos.size(), bytecodeIndex, getByValInfoBytecodeIndex)); + } - CallLinkInfo* addCallLinkInfo(); - Bag<CallLinkInfo>::iterator callLinkInfosBegin() { return m_callLinkInfos.begin(); } - Bag<CallLinkInfo>::iterator callLinkInfosEnd() { return m_callLinkInfos.end(); } + CallLinkInfo& getCallLinkInfo(ReturnAddressPtr returnAddress) + { + return *(binarySearch<CallLinkInfo, void*>(m_callLinkInfos, m_callLinkInfos.size(), returnAddress.value(), getCallLinkInfoReturnLocation)); + } - // This is a slow function call used primarily for compiling OSR exits in the case - // that there had been inlining. Chances are if you want to use this, you're really - // looking for a CallLinkInfoMap to amortize the cost of calling this. - CallLinkInfo* getCallLinkInfoForBytecodeIndex(unsigned bytecodeIndex); + CallLinkInfo& getCallLinkInfo(unsigned bytecodeIndex) + { + ASSERT(!JITCode::isOptimizingJIT(jitType())); + return *(binarySearch<CallLinkInfo, unsigned>(m_callLinkInfos, m_callLinkInfos.size(), bytecodeIndex, getCallLinkInfoBytecodeIndex)); + } #endif // ENABLE(JIT) void unlinkIncomingCalls(); @@ -233,14 +207,20 @@ public: void unlinkCalls(); void linkIncomingCall(ExecState* callerFrame, CallLinkInfo*); - void linkIncomingPolymorphicCall(ExecState* callerFrame, PolymorphicCallNode*); + + bool isIncomingCallAlreadyLinked(CallLinkInfo* incoming) + { + return m_incomingCalls.isOnList(incoming); + } #endif // ENABLE(JIT) +#if ENABLE(LLINT) void linkIncomingCall(ExecState* callerFrame, LLIntCallLinkInfo*); +#endif // ENABLE(LLINT) - void setJITCodeMap(std::unique_ptr<CompactJITCodeMap> jitCodeMap) + void setJITCodeMap(PassOwnPtr<CompactJITCodeMap> jitCodeMap) { - m_jitCodeMap = WTF::move(jitCodeMap); + m_jitCodeMap = jitCodeMap; } CompactJITCodeMap* jitCodeMap() { @@ -253,6 +233,8 @@ public: return static_cast<Instruction*>(returnAddress) - instructions().begin(); } + bool isNumericCompareFunction() { return m_unlinkedCode->isNumericCompareFunction(); } + unsigned numberOfInstructions() const { return m_instructions.size(); } RefCountedArray<Instruction>& instructions() { return m_instructions; } const RefCountedArray<Instruction>& instructions() const { return m_instructions; } @@ -263,21 +245,28 @@ public: unsigned instructionCount() const { return m_instructions.size(); } + int argumentIndexAfterCapture(size_t argument); + + bool hasSlowArguments(); + const SlowArgument* machineSlowArguments(); + // Exactly equivalent to codeBlock->ownerExecutable()->installCode(codeBlock); void install(); // Exactly equivalent to codeBlock->ownerExecutable()->newReplacementCodeBlockFor(codeBlock->specializationKind()) PassRefPtr<CodeBlock> newReplacement(); - void setJITCode(PassRefPtr<JITCode> code) + void setJITCode(PassRefPtr<JITCode> code, MacroAssemblerCodePtr codeWithArityCheck) { ASSERT(m_heap->isDeferred()); - m_heap->reportExtraMemoryAllocated(code->size()); + m_heap->reportExtraMemoryCost(code->size()); ConcurrentJITLocker locker(m_lock); WTF::storeStoreFence(); // This is probably not needed because the lock will also do something similar, but it's good to be paranoid. m_jitCode = code; + m_jitCodeWithArityCheck = codeWithArityCheck; } PassRefPtr<JITCode> jitCode() { return m_jitCode; } + MacroAssemblerCodePtr jitCodeWithArityCheck() { return m_jitCodeWithArityCheck; } JITCode::JITType jitType() const { JITCode* jitCode = m_jitCode.get(); @@ -296,14 +285,19 @@ public: virtual CodeBlock* replacement() = 0; virtual DFG::CapabilityLevel capabilityLevelInternal() = 0; - DFG::CapabilityLevel capabilityLevel(); + DFG::CapabilityLevel capabilityLevel() + { + DFG::CapabilityLevel result = capabilityLevelInternal(); + m_capabilityLevelState = result; + return result; + } DFG::CapabilityLevel capabilityLevelState() { return m_capabilityLevelState; } bool hasOptimizedReplacement(JITCode::JITType typeToReplace); bool hasOptimizedReplacement(); // the typeToReplace is my JITType #endif - void jettison(Profiler::JettisonReason, ReoptimizationMode = DontCountReoptimization, const FireDetail* = nullptr); + void jettison(ReoptimizationMode = DontCountReoptimization); ScriptExecutable* ownerExecutable() const { return m_ownerExecutable.get(); } @@ -313,41 +307,77 @@ public: void setThisRegister(VirtualRegister thisRegister) { m_thisRegister = thisRegister; } VirtualRegister thisRegister() const { return m_thisRegister; } + bool needsFullScopeChain() const { return m_unlinkedCode->needsFullScopeChain(); } bool usesEval() const { return m_unlinkedCode->usesEval(); } - void setScopeRegister(VirtualRegister scopeRegister) + void setArgumentsRegister(VirtualRegister argumentsRegister) { - ASSERT(scopeRegister.isLocal() || !scopeRegister.isValid()); - m_scopeRegister = scopeRegister; + ASSERT(argumentsRegister.isValid()); + m_argumentsRegister = argumentsRegister; + ASSERT(usesArguments()); } - - VirtualRegister scopeRegister() const + VirtualRegister argumentsRegister() const { - return m_scopeRegister; + ASSERT(usesArguments()); + return m_argumentsRegister; + } + VirtualRegister uncheckedArgumentsRegister() + { + if (!usesArguments()) + return VirtualRegister(); + return argumentsRegister(); } - void setActivationRegister(VirtualRegister activationRegister) { - m_lexicalEnvironmentRegister = activationRegister; + m_activationRegister = activationRegister; } VirtualRegister activationRegister() const { - ASSERT(m_lexicalEnvironmentRegister.isValid()); - return m_lexicalEnvironmentRegister; + ASSERT(needsFullScopeChain()); + return m_activationRegister; } VirtualRegister uncheckedActivationRegister() { - return m_lexicalEnvironmentRegister; + if (!needsFullScopeChain()) + return VirtualRegister(); + return activationRegister(); } + bool usesArguments() const { return m_argumentsRegister.isValid(); } + bool needsActivation() const { - ASSERT(m_lexicalEnvironmentRegister.isValid() == m_needsActivation); return m_needsActivation; } + unsigned captureCount() const + { + if (!symbolTable()) + return 0; + return symbolTable()->captureCount(); + } + + int captureStart() const + { + if (!symbolTable()) + return 0; + return symbolTable()->captureStart(); + } + + int captureEnd() const + { + if (!symbolTable()) + return 0; + return symbolTable()->captureEnd(); + } + + bool isCaptured(VirtualRegister operand, InlineCallFrame* = 0) const; + + int framePointerOffsetToGetActivationRegisters(int machineCaptureStart); + int framePointerOffsetToGetActivationRegisters(); + CodeType codeType() const { return m_unlinkedCode->codeType(); } PutPropertySlot::Context putByIdContext() const { @@ -363,8 +393,20 @@ public: size_t numberOfJumpTargets() const { return m_unlinkedCode->numberOfJumpTargets(); } unsigned jumpTarget(int index) const { return m_unlinkedCode->jumpTarget(index); } + void clearEvalCache(); + String nameForRegister(VirtualRegister); +#if ENABLE(JIT) + void setNumberOfByValInfos(size_t size) { m_byValInfos.resizeToFit(size); } + size_t numberOfByValInfos() const { return m_byValInfos.size(); } + ByValInfo& byValInfo(size_t index) { return m_byValInfos[index]; } + + void setNumberOfCallLinkInfos(size_t size) { m_callLinkInfos.resizeToFit(size); } + size_t numberOfCallLinkInfos() const { return m_callLinkInfos.size(); } + CallLinkInfo& callLinkInfo(int index) { return m_callLinkInfos[index]; } +#endif + unsigned numberOfArgumentValueProfiles() { ASSERT(m_numParameters >= 0); @@ -380,7 +422,17 @@ public: unsigned numberOfValueProfiles() { return m_valueProfiles.size(); } ValueProfile* valueProfile(int index) { return &m_valueProfiles[index]; } - ValueProfile* valueProfileForBytecodeOffset(int bytecodeOffset); + ValueProfile* valueProfileForBytecodeOffset(int bytecodeOffset) + { + ValueProfile* result = binarySearch<ValueProfile, int>( + m_valueProfiles, m_valueProfiles.size(), bytecodeOffset, + getValueProfileBytecodeOffset<ValueProfile>); + ASSERT(result->m_bytecodeOffset != -1); + ASSERT(instructions()[bytecodeOffset + opcodeLength( + m_vm->interpreter->getOpcodeID( + instructions()[bytecodeOffset].u.opcode)) - 1].u.profile == result); + return result; + } SpeculatedType valueProfilePredictionForBytecodeOffset(const ConcurrentJITLocker& locker, int bytecodeOffset) { return valueProfileForBytecodeOffset(bytecodeOffset)->computeUpdatedPrediction(locker); @@ -404,7 +456,12 @@ public: } unsigned numberOfRareCaseProfiles() { return m_rareCaseProfiles.size(); } RareCaseProfile* rareCaseProfile(int index) { return &m_rareCaseProfiles[index]; } - RareCaseProfile* rareCaseProfileForBytecodeOffset(int bytecodeOffset); + RareCaseProfile* rareCaseProfileForBytecodeOffset(int bytecodeOffset) + { + return tryBinarySearch<RareCaseProfile, int>( + m_rareCaseProfiles, m_rareCaseProfiles.size(), bytecodeOffset, + getRareCaseProfileBytecodeOffset); + } bool likelyToTakeSlowCase(int bytecodeOffset) { @@ -432,8 +489,8 @@ public: RareCaseProfile* specialFastCaseProfileForBytecodeOffset(int bytecodeOffset) { return tryBinarySearch<RareCaseProfile, int>( - m_specialFastCaseProfiles, m_specialFastCaseProfiles.size(), bytecodeOffset, - getRareCaseProfileBytecodeOffset); + m_specialFastCaseProfiles, m_specialFastCaseProfiles.size(), bytecodeOffset, + getRareCaseProfileBytecodeOffset); } bool likelyToTakeSpecialFastCase(int bytecodeOffset) @@ -519,15 +576,11 @@ public: ConcurrentJITLocker locker(m_lock); return m_exitProfile.add(locker, site); } - - bool hasExitSite(const ConcurrentJITLocker& locker, const DFG::FrequentExitSite& site) const - { - return m_exitProfile.hasExitSite(locker, site); - } + bool hasExitSite(const DFG::FrequentExitSite& site) const { ConcurrentJITLocker locker(m_lock); - return hasExitSite(locker, site); + return m_exitProfile.hasExitSite(locker, site); } DFG::ExitProfile& exitProfile() { return m_exitProfile; } @@ -536,6 +589,11 @@ public: { return m_lazyOperandValueProfiles; } +#else // ENABLE(DFG_JIT) + bool addFrequentExitSite(const DFG::FrequentExitSite&) + { + return false; + } #endif // ENABLE(DFG_JIT) // Constant Pool @@ -563,13 +621,12 @@ public: #endif Vector<WriteBarrier<Unknown>>& constants() { return m_constantRegisters; } - Vector<SourceCodeRepresentation>& constantsSourceCodeRepresentation() { return m_constantsSourceCodeRepresentation; } + size_t numberOfConstantRegisters() const { return m_constantRegisters.size(); } unsigned addConstant(JSValue v) { unsigned result = m_constantRegisters.size(); m_constantRegisters.append(WriteBarrier<Unknown>()); m_constantRegisters.last().set(m_globalObject->vm(), m_ownerExecutable.get(), v); - m_constantsSourceCodeRepresentation.append(SourceCodeRepresentation::Other); return result; } @@ -577,19 +634,19 @@ public: { unsigned result = m_constantRegisters.size(); m_constantRegisters.append(WriteBarrier<Unknown>()); - m_constantsSourceCodeRepresentation.append(SourceCodeRepresentation::Other); return result; } + bool findConstant(JSValue, unsigned& result); + unsigned addOrFindConstant(JSValue); WriteBarrier<Unknown>& constantRegister(int index) { return m_constantRegisters[index - FirstConstantRegisterIndex]; } ALWAYS_INLINE bool isConstantRegisterIndex(int index) const { return index >= FirstConstantRegisterIndex; } ALWAYS_INLINE JSValue getConstant(int index) const { return m_constantRegisters[index - FirstConstantRegisterIndex].get(); } - ALWAYS_INLINE SourceCodeRepresentation constantSourceCodeRepresentation(int index) const { return m_constantsSourceCodeRepresentation[index - FirstConstantRegisterIndex]; } FunctionExecutable* functionDecl(int index) { return m_functionDecls[index].get(); } int numberOfFunctionDecls() { return m_functionDecls.size(); } FunctionExecutable* functionExpr(int index) { return m_functionExprs[index].get(); } - + RegExp* regexp(int index) const { return m_unlinkedCode->regexp(index); } unsigned numberOfConstantBuffers() const @@ -616,26 +673,15 @@ public: return constantBufferAsVector(index).data(); } - Heap* heap() const { return m_heap; } JSGlobalObject* globalObject() { return m_globalObject.get(); } JSGlobalObject* globalObjectFor(CodeOrigin); BytecodeLivenessAnalysis& livenessAnalysis() { - { - ConcurrentJITLocker locker(m_lock); - if (!!m_livenessAnalysis) - return *m_livenessAnalysis; - } - std::unique_ptr<BytecodeLivenessAnalysis> analysis = - std::make_unique<BytecodeLivenessAnalysis>(this); - { - ConcurrentJITLocker locker(m_lock); - if (!m_livenessAnalysis) - m_livenessAnalysis = WTF::move(analysis); - return *m_livenessAnalysis; - } + if (!m_livenessAnalysis) + m_livenessAnalysis = std::make_unique<BytecodeLivenessAnalysis>(this); + return *m_livenessAnalysis; } void validate(); @@ -656,6 +702,9 @@ public: StringJumpTable& addStringSwitchJumpTable() { createRareDataIfNecessary(); m_rareData->m_stringSwitchJumpTables.append(StringJumpTable()); return m_rareData->m_stringSwitchJumpTables.last(); } StringJumpTable& stringSwitchJumpTable(int tableIndex) { RELEASE_ASSERT(m_rareData); return m_rareData->m_stringSwitchJumpTables[tableIndex]; } + + SymbolTable* symbolTable() const { return m_symbolTable.get(); } + EvalCodeCache& evalCodeCache() { createRareDataIfNecessary(); return m_rareData->m_evalCodeCache; } enum ShrinkMode { @@ -692,7 +741,7 @@ public: m_llintExecuteCounter.setNewThreshold(Options::thresholdForJITSoon(), this); } - const BaselineExecutionCounter& llintExecuteCounter() const + const ExecutionCounter& llintExecuteCounter() const { return m_llintExecuteCounter; } @@ -718,7 +767,7 @@ public: // When we observe a lot of speculation failures, we trigger a // reoptimization. But each time, we increase the optimization trigger // to avoid thrashing. - JS_EXPORT_PRIVATE unsigned reoptimizationRetryCounter() const; + unsigned reoptimizationRetryCounter() const; void countReoptimization(); #if ENABLE(JIT) unsigned numberOfDFGCompiles(); @@ -732,11 +781,11 @@ public: return &m_jitExecuteCounter.m_counter; } - static ptrdiff_t offsetOfJITExecuteCounter() { return OBJECT_OFFSETOF(CodeBlock, m_jitExecuteCounter) + OBJECT_OFFSETOF(BaselineExecutionCounter, m_counter); } - static ptrdiff_t offsetOfJITExecutionActiveThreshold() { return OBJECT_OFFSETOF(CodeBlock, m_jitExecuteCounter) + OBJECT_OFFSETOF(BaselineExecutionCounter, m_activeThreshold); } - static ptrdiff_t offsetOfJITExecutionTotalCount() { return OBJECT_OFFSETOF(CodeBlock, m_jitExecuteCounter) + OBJECT_OFFSETOF(BaselineExecutionCounter, m_totalCount); } + static ptrdiff_t offsetOfJITExecuteCounter() { return OBJECT_OFFSETOF(CodeBlock, m_jitExecuteCounter) + OBJECT_OFFSETOF(ExecutionCounter, m_counter); } + static ptrdiff_t offsetOfJITExecutionActiveThreshold() { return OBJECT_OFFSETOF(CodeBlock, m_jitExecuteCounter) + OBJECT_OFFSETOF(ExecutionCounter, m_activeThreshold); } + static ptrdiff_t offsetOfJITExecutionTotalCount() { return OBJECT_OFFSETOF(CodeBlock, m_jitExecuteCounter) + OBJECT_OFFSETOF(ExecutionCounter, m_totalCount); } - const BaselineExecutionCounter& jitExecuteCounter() const { return m_jitExecuteCounter; } + const ExecutionCounter& jitExecuteCounter() const { return m_jitExecuteCounter; } unsigned optimizationDelayCounter() const { return m_optimizationDelayCounter; } @@ -817,11 +866,10 @@ public: void updateAllPredictions(); unsigned frameRegisterCount(); - int stackPointerOffset(); bool hasOpDebugForLineAndColumn(unsigned line, unsigned column); - bool hasDebuggerRequests() const { return m_debuggerRequests; } + int hasDebuggerRequests() const { return !!m_debuggerRequests; } void* debuggerRequestsAddress() { return &m_debuggerRequests; } void addBreakpoint(unsigned numBreakpoints); @@ -837,17 +885,13 @@ public: }; void setSteppingMode(SteppingMode); - void clearDebuggerRequests() - { - m_steppingMode = SteppingModeDisabled; - m_numBreakpoints = 0; - } - + void clearDebuggerRequests() { m_debuggerRequests = 0; } + // FIXME: Make these remaining members private. int m_numCalleeRegisters; int m_numVars; - bool m_isConstructor : 1; + bool m_isConstructor; // This is intentionally public; it's the responsibility of anyone doing any // of the following to hold the lock: @@ -867,34 +911,16 @@ public: // concurrent compilation threads finish what they're doing. mutable ConcurrentJITLock m_lock; - bool m_shouldAlwaysBeInlined; // Not a bitfield because the JIT wants to store to it. - bool m_allTransitionsHaveBeenMarked : 1; // Initialized and used on every GC. + bool m_shouldAlwaysBeInlined; + bool m_allTransitionsHaveBeenMarked; // Initialized and used on every GC. - bool m_didFailFTLCompilation : 1; - bool m_hasBeenCompiledWithFTL : 1; + bool m_didFailFTLCompilation; // Internal methods for use by validation code. It would be private if it wasn't // for the fact that we use it from anonymous namespaces. void beginValidationDidFail(); NO_RETURN_DUE_TO_CRASH void endValidationDidFail(); - bool isKnownToBeLiveDuringGC(); // Will only return valid results when called during GC. Assumes that you've already established that the owner executable is live. - - struct RareData { - WTF_MAKE_FAST_ALLOCATED; - public: - Vector<HandlerInfo> m_exceptionHandlers; - - // Buffers used for large array literals - Vector<Vector<JSValue>> m_constantBuffers; - - // Jump Tables - Vector<SimpleJumpTable> m_switchJumpTables; - Vector<StringJumpTable> m_stringSwitchJumpTables; - - EvalCodeCache m_evalCodeCache; - }; - protected: virtual void visitWeakReferences(SlotVisitor&) override; virtual void finalizeUnconditionally() override; @@ -914,48 +940,70 @@ private: double optimizationThresholdScalingFactor(); +#if ENABLE(JIT) + ClosureCallStubRoutine* findClosureCallForReturnPC(ReturnAddressPtr); +#endif + void updateAllPredictionsAndCountLiveness(unsigned& numberOfLiveNonArgumentValueProfiles, unsigned& numberOfSamplesInProfiles); - void setConstantRegisters(const Vector<WriteBarrier<Unknown>>& constants, const Vector<SourceCodeRepresentation>& constantsSourceCodeRepresentation) + void setConstantRegisters(const Vector<WriteBarrier<Unknown>>& constants) { - ASSERT(constants.size() == constantsSourceCodeRepresentation.size()); size_t count = constants.size(); - m_constantRegisters.resizeToFit(count); + m_constantRegisters.resize(count); for (size_t i = 0; i < count; i++) m_constantRegisters[i].set(*m_vm, ownerExecutable(), constants[i].get()); - m_constantsSourceCodeRepresentation = constantsSourceCodeRepresentation; - } - - void replaceConstant(int index, JSValue value) - { - ASSERT(isConstantRegisterIndex(index) && static_cast<size_t>(index - FirstConstantRegisterIndex) < m_constantRegisters.size()); - m_constantRegisters[index - FirstConstantRegisterIndex].set(m_globalObject->vm(), m_ownerExecutable.get(), value); } - void dumpBytecode( - PrintStream&, ExecState*, const Instruction* begin, const Instruction*&, - const StubInfoMap& = StubInfoMap(), const CallLinkInfoMap& = CallLinkInfoMap()); + void dumpBytecode(PrintStream&, ExecState*, const Instruction* begin, const Instruction*&, const StubInfoMap& = StubInfoMap()); CString registerName(int r) const; - CString constantName(int index) const; void printUnaryOp(PrintStream&, ExecState*, int location, const Instruction*&, const char* op); void printBinaryOp(PrintStream&, ExecState*, int location, const Instruction*&, const char* op); void printConditionalJump(PrintStream&, ExecState*, const Instruction*, const Instruction*&, int location, const char* op); void printGetByIdOp(PrintStream&, ExecState*, int location, const Instruction*&); void printGetByIdCacheStatus(PrintStream&, ExecState*, int location, const StubInfoMap&); enum CacheDumpMode { DumpCaches, DontDumpCaches }; - void printCallOp(PrintStream&, ExecState*, int location, const Instruction*&, const char* op, CacheDumpMode, bool& hasPrintedProfiling, const CallLinkInfoMap&); + void printCallOp(PrintStream&, ExecState*, int location, const Instruction*&, const char* op, CacheDumpMode, bool& hasPrintedProfiling); void printPutByIdOp(PrintStream&, ExecState*, int location, const Instruction*&, const char* op); - void printPutByIdCacheStatus(PrintStream&, ExecState*, int location, const StubInfoMap&); - void printLocationAndOp(PrintStream&, ExecState*, int location, const Instruction*&, const char* op); - void printLocationOpAndRegisterOperand(PrintStream&, ExecState*, int location, const Instruction*& it, const char* op, int operand); + void printLocationAndOp(PrintStream& out, ExecState*, int location, const Instruction*&, const char* op) + { + out.printf("[%4d] %-17s ", location, op); + } + + void printLocationOpAndRegisterOperand(PrintStream& out, ExecState* exec, int location, const Instruction*& it, const char* op, int operand) + { + printLocationAndOp(out, exec, location, it, op); + out.printf("%s", registerName(operand).data()); + } void beginDumpProfiling(PrintStream&, bool& hasPrintedProfiling); void dumpValueProfiling(PrintStream&, const Instruction*&, bool& hasPrintedProfiling); void dumpArrayProfiling(PrintStream&, const Instruction*&, bool& hasPrintedProfiling); void dumpRareCaseProfile(PrintStream&, const char* name, RareCaseProfile*, bool& hasPrintedProfiling); - bool shouldImmediatelyAssumeLivenessDuringScan(); +#if ENABLE(DFG_JIT) + bool shouldImmediatelyAssumeLivenessDuringScan() + { + // Interpreter and Baseline JIT CodeBlocks don't need to be jettisoned when + // their weak references go stale. So if a basline JIT CodeBlock gets + // scanned, we can assume that this means that it's live. + if (!JITCode::isOptimizingJIT(jitType())) + return true; + + // For simplicity, we don't attempt to jettison code blocks during GC if + // they are executing. Instead we strongly mark their weak references to + // allow them to continue to execute soundly. + if (m_mayBeExecuting) + return true; + + if (Options::forceDFGCodeBlockLiveness()) + return true; + + return false; + } +#else + bool shouldImmediatelyAssumeLivenessDuringScan() { return true; } +#endif void propagateTransitions(SlotVisitor&); void determineLiveness(SlotVisitor&); @@ -966,11 +1014,9 @@ private: void createRareDataIfNecessary() { if (!m_rareData) - m_rareData = std::make_unique<RareData>(); + m_rareData = adoptPtr(new RareData); } - - void insertBasicBlockBoundariesForControlFlowProfiler(RefCountedArray<Instruction>&); - + #if ENABLE(JIT) void resetStubInternal(RepatchBuffer&, StructureStubInfo&); void resetStubDuringGCInternal(RepatchBuffer&, StructureStubInfo&); @@ -980,40 +1026,42 @@ private: union { unsigned m_debuggerRequests; struct { - unsigned m_hasDebuggerStatement : 1; unsigned m_steppingMode : 1; - unsigned m_numBreakpoints : 30; + unsigned m_numBreakpoints : 31; }; }; WriteBarrier<ScriptExecutable> m_ownerExecutable; VM* m_vm; RefCountedArray<Instruction> m_instructions; + WriteBarrier<SymbolTable> m_symbolTable; VirtualRegister m_thisRegister; - VirtualRegister m_scopeRegister; - VirtualRegister m_lexicalEnvironmentRegister; + VirtualRegister m_argumentsRegister; + VirtualRegister m_activationRegister; bool m_isStrictMode; bool m_needsActivation; bool m_mayBeExecuting; - Atomic<bool> m_visitAggregateHasBeenCalled; + uint8_t m_visitAggregateHasBeenCalled; RefPtr<SourceProvider> m_source; unsigned m_sourceOffset; unsigned m_firstLineColumnOffset; unsigned m_codeType; +#if ENABLE(LLINT) Vector<LLIntCallLinkInfo> m_llintCallLinkInfos; SentinelLinkedList<LLIntCallLinkInfo, BasicRawSentinelNode<LLIntCallLinkInfo>> m_incomingLLIntCalls; +#endif RefPtr<JITCode> m_jitCode; + MacroAssemblerCodePtr m_jitCodeWithArityCheck; #if ENABLE(JIT) Bag<StructureStubInfo> m_stubInfos; - Bag<ByValInfo> m_byValInfos; - Bag<CallLinkInfo> m_callLinkInfos; + Vector<ByValInfo> m_byValInfos; + Vector<CallLinkInfo> m_callLinkInfos; SentinelLinkedList<CallLinkInfo, BasicRawSentinelNode<CallLinkInfo>> m_incomingCalls; - SentinelLinkedList<PolymorphicCallNode, BasicRawSentinelNode<PolymorphicCallNode>> m_incomingPolymorphicCalls; #endif - std::unique_ptr<CompactJITCodeMap> m_jitCodeMap; + OwnPtr<CompactJITCodeMap> m_jitCodeMap; #if ENABLE(DFG_JIT) // This is relevant to non-DFG code blocks that serve as the profiled code block // for DFG code blocks. @@ -1033,15 +1081,14 @@ private: // TODO: This could just be a pointer to m_unlinkedCodeBlock's data, but the DFG mutates // it, so we're stuck with it for now. Vector<WriteBarrier<Unknown>> m_constantRegisters; - Vector<SourceCodeRepresentation> m_constantsSourceCodeRepresentation; Vector<WriteBarrier<FunctionExecutable>> m_functionDecls; Vector<WriteBarrier<FunctionExecutable>> m_functionExprs; RefPtr<CodeBlock> m_alternative; - BaselineExecutionCounter m_llintExecuteCounter; + ExecutionCounter m_llintExecuteCounter; - BaselineExecutionCounter m_jitExecuteCounter; + ExecutionCounter m_jitExecuteCounter; int32_t m_totalJITExecutions; uint32_t m_osrExitCounter; uint16_t m_optimizationDelayCounter; @@ -1051,7 +1098,24 @@ private: std::unique_ptr<BytecodeLivenessAnalysis> m_livenessAnalysis; - std::unique_ptr<RareData> m_rareData; + struct RareData { + WTF_MAKE_FAST_ALLOCATED; + public: + Vector<HandlerInfo> m_exceptionHandlers; + + // Buffers used for large array literals + Vector<Vector<JSValue>> m_constantBuffers; + + // Jump Tables + Vector<SimpleJumpTable> m_switchJumpTables; + Vector<StringJumpTable> m_stringSwitchJumpTables; + + EvalCodeCache m_evalCodeCache; + }; +#if COMPILER(MSVC) + friend void WTF::deleteOwnedPtr<RareData>(RareData*); +#endif + OwnPtr<RareData> m_rareData; #if ENABLE(JIT) DFG::CapabilityLevel m_capabilityLevelState; #endif @@ -1141,7 +1205,7 @@ inline CodeBlock* baselineCodeBlockForInlineCallFrame(InlineCallFrame* inlineCal RELEASE_ASSERT(inlineCallFrame); ExecutableBase* executable = inlineCallFrame->executable.get(); RELEASE_ASSERT(executable->structure()->classInfo() == FunctionExecutable::info()); - return static_cast<FunctionExecutable*>(executable)->baselineCodeBlockFor(inlineCallFrame->specializationKind()); + return static_cast<FunctionExecutable*>(executable)->baselineCodeBlockFor(inlineCallFrame->isCall ? CodeForCall : CodeForConstruct); } inline CodeBlock* baselineCodeBlockForOriginAndBaselineCodeBlock(const CodeOrigin& codeOrigin, CodeBlock* baselineCodeBlock) @@ -1151,6 +1215,24 @@ inline CodeBlock* baselineCodeBlockForOriginAndBaselineCodeBlock(const CodeOrigi return baselineCodeBlock; } +inline int CodeBlock::argumentIndexAfterCapture(size_t argument) +{ + if (argument >= static_cast<size_t>(symbolTable()->parameterCount())) + return CallFrame::argumentOffset(argument); + + const SlowArgument* slowArguments = symbolTable()->slowArguments(); + if (!slowArguments || slowArguments[argument].status == SlowArgument::Normal) + return CallFrame::argumentOffset(argument); + + ASSERT(slowArguments[argument].status == SlowArgument::Captured); + return slowArguments[argument].index; +} + +inline bool CodeBlock::hasSlowArguments() +{ + return !!symbolTable()->slowArguments(); +} + inline Register& ExecState::r(int index) { CodeBlock* codeBlock = this->codeBlock(); @@ -1159,20 +1241,21 @@ inline Register& ExecState::r(int index) return this[index]; } -inline Register& ExecState::r(VirtualRegister reg) -{ - return r(reg.offset()); -} - inline Register& ExecState::uncheckedR(int index) { RELEASE_ASSERT(index < FirstConstantRegisterIndex); return this[index]; } -inline Register& ExecState::uncheckedR(VirtualRegister reg) +inline JSValue ExecState::argumentAfterCapture(size_t argument) { - return uncheckedR(reg.offset()); + if (argument >= argumentCount()) + return jsUndefined(); + + if (!codeBlock()) + return this[argumentOffset(argument)].jsValue(); + + return this[codeBlock()->argumentIndexAfterCapture(argument)].jsValue(); } inline void CodeBlockSet::mark(void* candidateCodeBlock) @@ -1185,59 +1268,17 @@ inline void CodeBlockSet::mark(void* candidateCodeBlock) // -1 + 1 = 0 if (value + 1 <= 1) return; - - CodeBlock* codeBlock = static_cast<CodeBlock*>(candidateCodeBlock); - if (!m_oldCodeBlocks.contains(codeBlock) && !m_newCodeBlocks.contains(codeBlock)) - return; - - mark(codeBlock); -} - -inline void CodeBlockSet::mark(CodeBlock* codeBlock) -{ - if (!codeBlock) - return; - if (codeBlock->m_mayBeExecuting) + HashSet<CodeBlock*>::iterator iter = m_set.find(static_cast<CodeBlock*>(candidateCodeBlock)); + if (iter == m_set.end()) return; - codeBlock->m_mayBeExecuting = true; - // We might not have cleared the marks for this CodeBlock, but we need to visit it. - codeBlock->m_visitAggregateHasBeenCalled.store(false, std::memory_order_relaxed); + (*iter)->m_mayBeExecuting = true; #if ENABLE(GGC) - m_currentlyExecuting.append(codeBlock); + m_currentlyExecuting.append(static_cast<CodeBlock*>(candidateCodeBlock)); #endif } -template <typename Functor> inline void ScriptExecutable::forEachCodeBlock(Functor&& functor) -{ - switch (type()) { - case ProgramExecutableType: { - if (CodeBlock* codeBlock = jsCast<ProgramExecutable*>(this)->m_programCodeBlock.get()) - codeBlock->forEachRelatedCodeBlock(std::forward<Functor>(functor)); - break; - } - - case EvalExecutableType: { - if (CodeBlock* codeBlock = jsCast<EvalExecutable*>(this)->m_evalCodeBlock.get()) - codeBlock->forEachRelatedCodeBlock(std::forward<Functor>(functor)); - break; - } - - case FunctionExecutableType: { - Functor f(std::forward<Functor>(functor)); - FunctionExecutable* executable = jsCast<FunctionExecutable*>(this); - if (CodeBlock* codeBlock = executable->m_codeBlockForCall.get()) - codeBlock->forEachRelatedCodeBlock(f); - if (CodeBlock* codeBlock = executable->m_codeBlockForConstruct.get()) - codeBlock->forEachRelatedCodeBlock(f); - break; - } - default: - RELEASE_ASSERT_NOT_REACHED(); - } -} - } // namespace JSC #endif // CodeBlock_h diff --git a/Source/JavaScriptCore/bytecode/CodeBlockJettisoningWatchpoint.cpp b/Source/JavaScriptCore/bytecode/CodeBlockJettisoningWatchpoint.cpp index f9c6b1e55..be50c9778 100644 --- a/Source/JavaScriptCore/bytecode/CodeBlockJettisoningWatchpoint.cpp +++ b/Source/JavaScriptCore/bytecode/CodeBlockJettisoningWatchpoint.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2015 Apple Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,16 +28,18 @@ #include "CodeBlock.h" #include "DFGCommon.h" -#include "JSCInlines.h" namespace JSC { -void CodeBlockJettisoningWatchpoint::fireInternal(const FireDetail& detail) +void CodeBlockJettisoningWatchpoint::fireInternal() { if (DFG::shouldShowDisassembly()) dataLog("Firing watchpoint ", RawPointer(this), " on ", *m_codeBlock, "\n"); - m_codeBlock->jettison(Profiler::JettisonDueToUnprofiledWatchpoint, CountReoptimization, &detail); + m_codeBlock->jettison(CountReoptimization); + + if (isOnList()) + remove(); } } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/CodeBlockJettisoningWatchpoint.h b/Source/JavaScriptCore/bytecode/CodeBlockJettisoningWatchpoint.h index b5e6dd330..89d87f4d0 100644 --- a/Source/JavaScriptCore/bytecode/CodeBlockJettisoningWatchpoint.h +++ b/Source/JavaScriptCore/bytecode/CodeBlockJettisoningWatchpoint.h @@ -34,13 +34,18 @@ class CodeBlock; class CodeBlockJettisoningWatchpoint : public Watchpoint { public: + CodeBlockJettisoningWatchpoint() + : m_codeBlock(0) + { + } + CodeBlockJettisoningWatchpoint(CodeBlock* codeBlock) : m_codeBlock(codeBlock) { } protected: - virtual void fireInternal(const FireDetail&) override; + virtual void fireInternal() override; private: CodeBlock* m_codeBlock; diff --git a/Source/JavaScriptCore/bytecode/CodeOrigin.cpp b/Source/JavaScriptCore/bytecode/CodeOrigin.cpp index 15f759165..39b83fead 100644 --- a/Source/JavaScriptCore/bytecode/CodeOrigin.cpp +++ b/Source/JavaScriptCore/bytecode/CodeOrigin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2015 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,7 +29,7 @@ #include "CallFrame.h" #include "CodeBlock.h" #include "Executable.h" -#include "JSCInlines.h" +#include "Operations.h" namespace JSC { @@ -45,64 +45,7 @@ unsigned CodeOrigin::inlineDepth() const { return inlineDepthForCallFrame(inlineCallFrame); } - -bool CodeOrigin::isApproximatelyEqualTo(const CodeOrigin& other) const -{ - CodeOrigin a = *this; - CodeOrigin b = other; - - if (!a.isSet()) - return !b.isSet(); - if (!b.isSet()) - return false; - if (a.isHashTableDeletedValue()) - return b.isHashTableDeletedValue(); - if (b.isHashTableDeletedValue()) - return false; - - for (;;) { - ASSERT(a.isSet()); - ASSERT(b.isSet()); - - if (a.bytecodeIndex != b.bytecodeIndex) - return false; - - if ((!!a.inlineCallFrame) != (!!b.inlineCallFrame)) - return false; - - if (!a.inlineCallFrame) - return true; - - if (a.inlineCallFrame->executable.get() != b.inlineCallFrame->executable.get()) - return false; - - a = a.inlineCallFrame->caller; - b = b.inlineCallFrame->caller; - } -} - -unsigned CodeOrigin::approximateHash() const -{ - if (!isSet()) - return 0; - if (isHashTableDeletedValue()) - return 1; - - unsigned result = 2; - CodeOrigin codeOrigin = *this; - for (;;) { - result += codeOrigin.bytecodeIndex; - - if (!codeOrigin.inlineCallFrame) - return result; - - result += WTF::PtrHash<JSCell*>::hash(codeOrigin.inlineCallFrame->executable.get()); - - codeOrigin = codeOrigin.inlineCallFrame->caller; - } -} - Vector<CodeOrigin> CodeOrigin::inlineStack() const { Vector<CodeOrigin> result(inlineDepth()); @@ -141,27 +84,6 @@ void CodeOrigin::dumpInContext(PrintStream& out, DumpContext*) const dump(out); } -JSFunction* InlineCallFrame::calleeConstant() const -{ - if (calleeRecovery.isConstant()) - return jsCast<JSFunction*>(calleeRecovery.constant()); - return nullptr; -} - -void InlineCallFrame::visitAggregate(SlotVisitor& visitor) -{ - // FIXME: This is an antipattern for two reasons. References introduced by the DFG - // that aren't in the original CodeBlock being compiled should be weakly referenced. - // Inline call frames aren't in the original CodeBlock, so they qualify as weak. Also, - // those weak references should already be tracked in the DFG as weak FrozenValues. So, - // there is probably no need for this. We already have assertions that this should be - // unnecessary. Finally, just marking the executable and not anything else in the inline - // call frame is almost certainly insufficient for what this method thought it was going - // to accomplish. - // https://bugs.webkit.org/show_bug.cgi?id=146613 - visitor.append(&executable); -} - JSFunction* InlineCallFrame::calleeForCallFrame(ExecState* exec) const { return jsCast<JSFunction*>(calleeRecovery.recover(exec)); @@ -173,12 +95,6 @@ CodeBlockHash InlineCallFrame::hash() const specializationKind())->hash(); } -CString InlineCallFrame::hashAsStringIfPossible() const -{ - return jsCast<FunctionExecutable*>(executable.get())->codeBlockFor( - specializationKind())->hashAsStringIfPossible(); -} - CString InlineCallFrame::inferredName() const { return jsCast<FunctionExecutable*>(executable.get())->inferredName().utf8(); @@ -191,7 +107,7 @@ CodeBlock* InlineCallFrame::baselineCodeBlock() const void InlineCallFrame::dumpBriefFunctionInformation(PrintStream& out) const { - out.print(inferredName(), "#", hashAsStringIfPossible()); + out.print(inferredName(), "#", hash()); } void InlineCallFrame::dumpInContext(PrintStream& out, DumpContext* context) const @@ -199,14 +115,14 @@ void InlineCallFrame::dumpInContext(PrintStream& out, DumpContext* context) cons out.print(briefFunctionInformation(), ":<", RawPointer(executable.get())); if (executable->isStrictMode()) out.print(" (StrictMode)"); - out.print(", bc#", caller.bytecodeIndex, ", ", kind); + out.print(", bc#", caller.bytecodeIndex, ", ", specializationKind()); if (isClosureCall) out.print(", closure call"); else out.print(", known callee: ", inContext(calleeRecovery.constant(), context)); out.print(", numArgs+this = ", arguments.size()); - out.print(", stackOffset = ", stackOffset); - out.print(" (", virtualRegisterForLocal(0), " maps to ", virtualRegisterForLocal(0) + stackOffset, ")>"); + out.print(", stack < loc", VirtualRegister(stackOffset).toLocal()); + out.print(">"); } void InlineCallFrame::dump(PrintStream& out) const @@ -216,32 +132,3 @@ void InlineCallFrame::dump(PrintStream& out) const } // namespace JSC -namespace WTF { - -void printInternal(PrintStream& out, JSC::InlineCallFrame::Kind kind) -{ - switch (kind) { - case JSC::InlineCallFrame::Call: - out.print("Call"); - return; - case JSC::InlineCallFrame::Construct: - out.print("Construct"); - return; - case JSC::InlineCallFrame::CallVarargs: - out.print("CallVarargs"); - return; - case JSC::InlineCallFrame::ConstructVarargs: - out.print("ConstructVarargs"); - return; - case JSC::InlineCallFrame::GetterCall: - out.print("GetterCall"); - return; - case JSC::InlineCallFrame::SetterCall: - out.print("SetterCall"); - return; - } - RELEASE_ASSERT_NOT_REACHED(); -} - -} // namespace WTF - diff --git a/Source/JavaScriptCore/bytecode/CodeOrigin.h b/Source/JavaScriptCore/bytecode/CodeOrigin.h index d1879a327..ed660c247 100644 --- a/Source/JavaScriptCore/bytecode/CodeOrigin.h +++ b/Source/JavaScriptCore/bytecode/CodeOrigin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2015 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,6 +28,7 @@ #include "CodeBlockHash.h" #include "CodeSpecializationKind.h" +#include "JSFunction.h" #include "ValueRecovery.h" #include "WriteBarrier.h" #include <wtf/BitVector.h> @@ -62,7 +63,7 @@ struct CodeOrigin { CodeOrigin(WTF::HashTableDeletedValueType) : bytecodeIndex(invalidBytecodeIndex) - , inlineCallFrame(deletedMarker()) + , inlineCallFrame(bitwise_cast<InlineCallFrame*>(static_cast<uintptr_t>(1))) { } @@ -74,7 +75,6 @@ struct CodeOrigin { } bool isSet() const { return bytecodeIndex != invalidBytecodeIndex; } - bool operator!() const { return !isSet(); } bool isHashTableDeletedValue() const { @@ -97,133 +97,51 @@ struct CodeOrigin { bool operator==(const CodeOrigin& other) const; bool operator!=(const CodeOrigin& other) const { return !(*this == other); } - // This checks if the two code origins correspond to the same stack trace snippets, - // but ignore whether the InlineCallFrame's are identical. - bool isApproximatelyEqualTo(const CodeOrigin& other) const; - - unsigned approximateHash() const; - // Get the inline stack. This is slow, and is intended for debugging only. Vector<CodeOrigin> inlineStack() const; void dump(PrintStream&) const; void dumpInContext(PrintStream&, DumpContext*) const; - -private: - static InlineCallFrame* deletedMarker() - { - return bitwise_cast<InlineCallFrame*>(static_cast<uintptr_t>(1)); - } }; struct InlineCallFrame { - enum Kind { - Call, - Construct, - CallVarargs, - ConstructVarargs, - - // For these, the stackOffset incorporates the argument count plus the true return PC - // slot. - GetterCall, - SetterCall - }; - - static Kind kindFor(CodeSpecializationKind kind) - { - switch (kind) { - case CodeForCall: - return Call; - case CodeForConstruct: - return Construct; - } - RELEASE_ASSERT_NOT_REACHED(); - return Call; - } - - static Kind varargsKindFor(CodeSpecializationKind kind) - { - switch (kind) { - case CodeForCall: - return CallVarargs; - case CodeForConstruct: - return ConstructVarargs; - } - RELEASE_ASSERT_NOT_REACHED(); - return Call; - } - - static CodeSpecializationKind specializationKindFor(Kind kind) - { - switch (kind) { - case Call: - case CallVarargs: - case GetterCall: - case SetterCall: - return CodeForCall; - case Construct: - case ConstructVarargs: - return CodeForConstruct; - } - RELEASE_ASSERT_NOT_REACHED(); - return CodeForCall; - } - - static bool isVarargs(Kind kind) - { - switch (kind) { - case CallVarargs: - case ConstructVarargs: - return true; - default: - return false; - } - } - bool isVarargs() const - { - return isVarargs(static_cast<Kind>(kind)); - } - - Vector<ValueRecovery> arguments; // Includes 'this'. + Vector<ValueRecovery> arguments; WriteBarrier<ScriptExecutable> executable; ValueRecovery calleeRecovery; CodeOrigin caller; - - signed stackOffset : 28; - unsigned kind : 3; // real type is Kind + BitVector capturedVars; // Indexed by the machine call frame's variable numbering. + signed stackOffset : 30; + bool isCall : 1; bool isClosureCall : 1; // If false then we know that callee/scope are constants and the DFG won't treat them as variables, i.e. they have to be recovered manually. - VirtualRegister argumentCountRegister; // Only set when we inline a varargs call. + VirtualRegister argumentsRegister; // This is only set if the code uses arguments. The unmodified arguments register follows the unmodifiedArgumentsRegister() convention (see CodeBlock.h). // There is really no good notion of a "default" set of values for // InlineCallFrame's fields. This constructor is here just to reduce confusion if // we forgot to initialize explicitly. InlineCallFrame() : stackOffset(0) - , kind(Call) + , isCall(false) , isClosureCall(false) { } - CodeSpecializationKind specializationKind() const { return specializationKindFor(static_cast<Kind>(kind)); } + CodeSpecializationKind specializationKind() const { return specializationFromIsCall(isCall); } - JSFunction* calleeConstant() const; - void visitAggregate(SlotVisitor&); + JSFunction* calleeConstant() const + { + if (calleeRecovery.isConstant()) + return jsCast<JSFunction*>(calleeRecovery.constant()); + return 0; + } // Get the callee given a machine call frame to which this InlineCallFrame belongs. JSFunction* calleeForCallFrame(ExecState*) const; CString inferredName() const; CodeBlockHash hash() const; - CString hashAsStringIfPossible() const; CodeBlock* baselineCodeBlock() const; - void setStackOffset(signed offset) - { - stackOffset = offset; - RELEASE_ASSERT(static_cast<signed>(stackOffset) == offset); - } - ptrdiff_t callerFrameOffset() const { return stackOffset * sizeof(Register) + CallFrame::callerFrameOffset(); } ptrdiff_t returnPCOffset() const { return stackOffset * sizeof(Register) + CallFrame::returnPCOffset(); } @@ -267,18 +185,10 @@ struct CodeOriginHash { static const bool safeToCompareToEmptyOrDeleted = true; }; -struct CodeOriginApproximateHash { - static unsigned hash(const CodeOrigin& key) { return key.approximateHash(); } - static bool equal(const CodeOrigin& a, const CodeOrigin& b) { return a.isApproximatelyEqualTo(b); } - static const bool safeToCompareToEmptyOrDeleted = true; -}; - } // namespace JSC namespace WTF { -void printInternal(PrintStream&, JSC::InlineCallFrame::Kind); - template<typename T> struct DefaultHash; template<> struct DefaultHash<JSC::CodeOrigin> { typedef JSC::CodeOriginHash Hash; diff --git a/Source/JavaScriptCore/bytecode/CodeType.h b/Source/JavaScriptCore/bytecode/CodeType.h index b8e107dcf..04afc1109 100644 --- a/Source/JavaScriptCore/bytecode/CodeType.h +++ b/Source/JavaScriptCore/bytecode/CodeType.h @@ -26,6 +26,8 @@ #ifndef CodeType_h #define CodeType_h +#include <wtf/Platform.h> + namespace JSC { enum CodeType { GlobalCode, EvalCode, FunctionCode }; diff --git a/Source/JavaScriptCore/bytecode/ComplexGetStatus.cpp b/Source/JavaScriptCore/bytecode/ComplexGetStatus.cpp deleted file mode 100644 index d4ea3baa8..000000000 --- a/Source/JavaScriptCore/bytecode/ComplexGetStatus.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2014, 2015 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. - */ - -#include "config.h" -#include "ComplexGetStatus.h" - -#include "JSCInlines.h" - -namespace JSC { - -ComplexGetStatus ComplexGetStatus::computeFor( - Structure* headStructure, const ObjectPropertyConditionSet& conditionSet, UniquedStringImpl* uid) -{ - // FIXME: We should assert that we never see a structure that - // hasImpureGetOwnPropertySlot() but for which we don't - // newImpurePropertyFiresWatchpoints(). We're not at a point where we can do - // that, yet. - // https://bugs.webkit.org/show_bug.cgi?id=131810 - - ASSERT(conditionSet.isValid()); - - if (headStructure->takesSlowPathInDFGForImpureProperty()) - return takesSlowPath(); - - ComplexGetStatus result; - result.m_kind = Inlineable; - - if (!conditionSet.isEmpty()) { - result.m_conditionSet = conditionSet; - - if (!result.m_conditionSet.structuresEnsureValidity()) - return skip(); - - unsigned numberOfSlotBases = - result.m_conditionSet.numberOfConditionsWithKind(PropertyCondition::Presence); - RELEASE_ASSERT(numberOfSlotBases <= 1); - if (!numberOfSlotBases) { - // Currently we don't support misses. That's a bummer. - // FIXME: https://bugs.webkit.org/show_bug.cgi?id=133052 - return takesSlowPath(); - } - ObjectPropertyCondition base = result.m_conditionSet.slotBaseCondition(); - ASSERT(base.kind() == PropertyCondition::Presence); - - result.m_offset = base.offset(); - } else - result.m_offset = headStructure->getConcurrently(uid); - - if (!isValidOffset(result.m_offset)) - return takesSlowPath(); - - return result; -} - -} // namespace JSC - - diff --git a/Source/JavaScriptCore/bytecode/ComplexGetStatus.h b/Source/JavaScriptCore/bytecode/ComplexGetStatus.h deleted file mode 100644 index a06e995d5..000000000 --- a/Source/JavaScriptCore/bytecode/ComplexGetStatus.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2014, 2015 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 ComplexGetStatus_h -#define ComplexGetStatus_h - -#include "JSCJSValue.h" -#include "ObjectPropertyConditionSet.h" -#include "PropertyOffset.h" - -namespace JSC { - -class CodeBlock; -class StructureChain; - -// This class is useful for figuring out how to inline a cached get-like access. We -// say "get-like" because this is appropriate for loading the GetterSetter object in -// a put_by_id that hits a setter. Notably, this doesn't figure out how to call -// accessors, or even whether they should be called. What it gives us, is a way of -// determining how to load the value from the requested property (identified by a -// StringImpl* uid) from an object of the given structure in the given CodeBlock, -// assuming that such an access had already been cached by Repatch (and so Repatch had -// already done a bunch of safety checks). This doesn't reexecute any checks that -// Repatch would have executed, and for prototype chain accesses, it doesn't ask the -// objects in the prototype chain whether their getOwnPropertySlot would attempt to -// intercept the access - so this really is only appropriate if you already know that -// one of the JITOperations had OK'd this for caching and that Repatch concurred. -// -// The typical use pattern is something like: -// -// ComplexGetStatus status = ComplexGetStatus::computeFor(...); -// switch (status.kind()) { -// case ComplexGetStatus::ShouldSkip: -// // Handle the case where this kind of access is possibly safe but wouldn't -// // pass the required safety checks. For example, if an IC gives us a list of -// // accesses and one of them is ShouldSkip, then we should pretend as if it -// // wasn't even there. -// break; -// case ComplexGetStatus::TakesSlowPath: -// // This kind of access is not safe to inline. Bail out of any attempst to -// // inline. -// break; -// case ComplexGetStatus::Inlineable: -// // The good stuff goes here. If it's Inlineable then the other properties of -// // the 'status' object will tell you everything you need to know about how -// // to execute the get-like operation. -// break; -// } - -class ComplexGetStatus { -public: - enum Kind { - ShouldSkip, - TakesSlowPath, - Inlineable - }; - - ComplexGetStatus() - : m_kind(ShouldSkip) - , m_offset(invalidOffset) - { - } - - static ComplexGetStatus skip() - { - return ComplexGetStatus(); - } - - static ComplexGetStatus takesSlowPath() - { - ComplexGetStatus result; - result.m_kind = TakesSlowPath; - return result; - } - - static ComplexGetStatus computeFor( - Structure* headStructure, const ObjectPropertyConditionSet&, UniquedStringImpl* uid); - - Kind kind() const { return m_kind; } - PropertyOffset offset() const { return m_offset; } - const ObjectPropertyConditionSet& conditionSet() const { return m_conditionSet; } - -private: - Kind m_kind; - PropertyOffset m_offset; - ObjectPropertyConditionSet m_conditionSet; -}; - -} // namespace JSC - -#endif // ComplexGetStatus_h - diff --git a/Source/JavaScriptCore/bytecode/DFGExitProfile.cpp b/Source/JavaScriptCore/bytecode/DFGExitProfile.cpp index 40a25ced6..5d05bbb2f 100644 --- a/Source/JavaScriptCore/bytecode/DFGExitProfile.cpp +++ b/Source/JavaScriptCore/bytecode/DFGExitProfile.cpp @@ -28,6 +28,8 @@ #if ENABLE(DFG_JIT) +#include <wtf/PassOwnPtr.h> + namespace JSC { namespace DFG { ExitProfile::ExitProfile() { } @@ -35,12 +37,10 @@ ExitProfile::~ExitProfile() { } bool ExitProfile::add(const ConcurrentJITLocker&, const FrequentExitSite& site) { - ASSERT(site.jitType() != ExitFromAnything); - // If we've never seen any frequent exits then create the list and put this site // into it. if (!m_frequentExitSites) { - m_frequentExitSites = std::make_unique<Vector<FrequentExitSite>>(); + m_frequentExitSites = adoptPtr(new Vector<FrequentExitSite>()); m_frequentExitSites->append(site); return true; } @@ -78,7 +78,7 @@ bool ExitProfile::hasExitSite(const ConcurrentJITLocker&, const FrequentExitSite return false; for (unsigned i = m_frequentExitSites->size(); i--;) { - if (site.subsumes(m_frequentExitSites->at(i))) + if (m_frequentExitSites->at(i) == site) return true; } return false; diff --git a/Source/JavaScriptCore/bytecode/DFGExitProfile.h b/Source/JavaScriptCore/bytecode/DFGExitProfile.h index cdecbaf97..ab1a60d58 100644 --- a/Source/JavaScriptCore/bytecode/DFGExitProfile.h +++ b/Source/JavaScriptCore/bytecode/DFGExitProfile.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, 2012, 2013, 2014 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,12 +26,10 @@ #ifndef DFGExitProfile_h #define DFGExitProfile_h -#if ENABLE(DFG_JIT) - #include "ConcurrentJITLock.h" #include "ExitKind.h" -#include "ExitingJITType.h" #include <wtf/HashSet.h> +#include <wtf/OwnPtr.h> #include <wtf/Vector.h> namespace JSC { namespace DFG { @@ -41,21 +39,18 @@ public: FrequentExitSite() : m_bytecodeOffset(0) // 0 = empty value , m_kind(ExitKindUnset) - , m_jitType(ExitFromAnything) { } FrequentExitSite(WTF::HashTableDeletedValueType) : m_bytecodeOffset(1) // 1 = deleted value , m_kind(ExitKindUnset) - , m_jitType(ExitFromAnything) { } - explicit FrequentExitSite(unsigned bytecodeOffset, ExitKind kind, ExitingJITType jitType = ExitFromAnything) + explicit FrequentExitSite(unsigned bytecodeOffset, ExitKind kind) : m_bytecodeOffset(bytecodeOffset) , m_kind(kind) - , m_jitType(jitType) { if (m_kind == ArgumentsEscaped) { // Count this one globally. It doesn't matter where in the code block the arguments excaped; @@ -66,10 +61,9 @@ public: // Use this constructor if you wish for the exit site to be counted globally within its // code block. - explicit FrequentExitSite(ExitKind kind, ExitingJITType jitType = ExitFromAnything) + explicit FrequentExitSite(ExitKind kind) : m_bytecodeOffset(0) , m_kind(kind) - , m_jitType(jitType) { } @@ -81,36 +75,16 @@ public: bool operator==(const FrequentExitSite& other) const { return m_bytecodeOffset == other.m_bytecodeOffset - && m_kind == other.m_kind - && m_jitType == other.m_jitType; - } - - bool subsumes(const FrequentExitSite& other) const - { - if (m_bytecodeOffset != other.m_bytecodeOffset) - return false; - if (m_kind != other.m_kind) - return false; - if (m_jitType == ExitFromAnything) - return true; - return m_jitType == other.m_jitType; + && m_kind == other.m_kind; } unsigned hash() const { - return WTF::intHash(m_bytecodeOffset) + m_kind + m_jitType * 7; + return WTF::intHash(m_bytecodeOffset) + m_kind; } unsigned bytecodeOffset() const { return m_bytecodeOffset; } ExitKind kind() const { return m_kind; } - ExitingJITType jitType() const { return m_jitType; } - - FrequentExitSite withJITType(ExitingJITType jitType) const - { - FrequentExitSite result = *this; - result.m_jitType = jitType; - return result; - } bool isHashTableDeletedValue() const { @@ -120,7 +94,6 @@ public: private: unsigned m_bytecodeOffset; ExitKind m_kind; - ExitingJITType m_jitType; }; struct FrequentExitSiteHash { @@ -131,7 +104,6 @@ struct FrequentExitSiteHash { } } // namespace JSC::DFG - namespace WTF { template<typename T> struct DefaultHash; @@ -182,7 +154,7 @@ public: private: friend class QueryableExitProfile; - std::unique_ptr<Vector<FrequentExitSite>> m_frequentExitSites; + OwnPtr<Vector<FrequentExitSite>> m_frequentExitSites; }; class QueryableExitProfile { @@ -194,10 +166,6 @@ public: bool hasExitSite(const FrequentExitSite& site) const { - if (site.jitType() == ExitFromAnything) { - return hasExitSite(site.withJITType(ExitFromDFG)) - || hasExitSite(site.withJITType(ExitFromFTL)); - } return m_frequentExitSites.find(site) != m_frequentExitSites.end(); } @@ -216,6 +184,4 @@ private: } } // namespace JSC::DFG -#endif // ENABLE(DFG_JIT) - #endif // DFGExitProfile_h diff --git a/Source/JavaScriptCore/bytecode/DataFormat.h b/Source/JavaScriptCore/bytecode/DataFormat.h index 6d7542e2d..bb9da4c57 100644 --- a/Source/JavaScriptCore/bytecode/DataFormat.h +++ b/Source/JavaScriptCore/bytecode/DataFormat.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2011 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -56,6 +56,7 @@ enum DataFormat { // Special data formats used only for OSR. DataFormatDead = 33, // Implies jsUndefined(). + DataFormatArguments = 34 // Implies that the arguments object must be reified. }; inline const char* dataFormatToString(DataFormat dataFormat) @@ -89,6 +90,8 @@ inline const char* dataFormatToString(DataFormat dataFormat) return "JSBoolean"; case DataFormatDead: return "Dead"; + case DataFormatArguments: + return "Arguments"; default: RELEASE_ASSERT_NOT_REACHED(); return "Unknown"; diff --git a/Source/JavaScriptCore/bytecode/DeferredCompilationCallback.cpp b/Source/JavaScriptCore/bytecode/DeferredCompilationCallback.cpp index 761e95b3f..35af7c7b9 100644 --- a/Source/JavaScriptCore/bytecode/DeferredCompilationCallback.cpp +++ b/Source/JavaScriptCore/bytecode/DeferredCompilationCallback.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013, 2014 Apple Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,48 +26,10 @@ #include "config.h" #include "DeferredCompilationCallback.h" -#include "CodeBlock.h" - namespace JSC { DeferredCompilationCallback::DeferredCompilationCallback() { } DeferredCompilationCallback::~DeferredCompilationCallback() { } -void DeferredCompilationCallback::compilationDidComplete(CodeBlock* codeBlock, CompilationResult result) -{ - dumpCompiledSourcesIfNeeded(); - - switch (result) { - case CompilationFailed: - case CompilationInvalidated: - codeBlock->heap()->removeCodeBlock(codeBlock); - break; - case CompilationSuccessful: - break; - case CompilationDeferred: - RELEASE_ASSERT_NOT_REACHED(); - } -} - -Vector<DeferredSourceDump>& DeferredCompilationCallback::ensureDeferredSourceDump() -{ - if (!m_deferredSourceDump) - m_deferredSourceDump = std::make_unique<Vector<DeferredSourceDump>>(); - return *m_deferredSourceDump; -} - -void DeferredCompilationCallback::dumpCompiledSourcesIfNeeded() -{ - if (!m_deferredSourceDump) - return; - - ASSERT(Options::dumpSourceAtDFGTime()); - unsigned index = 0; - for (auto& info : *m_deferredSourceDump) { - dataLog("[", ++index, "] "); - info.dump(); - } -} - } // JSC diff --git a/Source/JavaScriptCore/bytecode/DeferredCompilationCallback.h b/Source/JavaScriptCore/bytecode/DeferredCompilationCallback.h index 37568d222..6421e3e25 100644 --- a/Source/JavaScriptCore/bytecode/DeferredCompilationCallback.h +++ b/Source/JavaScriptCore/bytecode/DeferredCompilationCallback.h @@ -27,9 +27,7 @@ #define DeferredCompilationCallback_h #include "CompilationResult.h" -#include "DeferredSourceDump.h" #include <wtf/RefCounted.h> -#include <wtf/Vector.h> namespace JSC { @@ -43,14 +41,7 @@ public: virtual ~DeferredCompilationCallback(); virtual void compilationDidBecomeReadyAsynchronously(CodeBlock*) = 0; - virtual void compilationDidComplete(CodeBlock*, CompilationResult); - - Vector<DeferredSourceDump>& ensureDeferredSourceDump(); - -private: - void dumpCompiledSourcesIfNeeded(); - - std::unique_ptr<Vector<DeferredSourceDump>> m_deferredSourceDump; + virtual void compilationDidComplete(CodeBlock*, CompilationResult) = 0; }; } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/DeferredSourceDump.cpp b/Source/JavaScriptCore/bytecode/DeferredSourceDump.cpp deleted file mode 100644 index 48079db66..000000000 --- a/Source/JavaScriptCore/bytecode/DeferredSourceDump.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -#include "config.h" -#include "DeferredSourceDump.h" - -#include "CodeBlock.h" -#include "CodeBlockWithJITType.h" - -namespace JSC { - -DeferredSourceDump::DeferredSourceDump(CodeBlock* codeBlock) - : m_codeBlock(codeBlock) - , m_rootCodeBlock(nullptr) - , m_rootJITType(JITCode::None) -{ -} - -DeferredSourceDump::DeferredSourceDump(CodeBlock* codeBlock, CodeBlock* rootCodeBlock, JITCode::JITType rootJITType, CodeOrigin callerCodeOrigin) - : m_codeBlock(codeBlock) - , m_rootCodeBlock(rootCodeBlock) - , m_rootJITType(rootJITType) - , m_callerCodeOrigin(callerCodeOrigin) -{ -} - -void DeferredSourceDump::dump() -{ - bool isInlinedFrame = !!m_rootCodeBlock; - if (isInlinedFrame) - dataLog("Inlined "); - else - dataLog("Compiled "); - dataLog(*m_codeBlock); - - if (isInlinedFrame) - dataLog(" at ", CodeBlockWithJITType(m_rootCodeBlock, m_rootJITType), " ", m_callerCodeOrigin); - - dataLog("\n'''"); - m_codeBlock->dumpSource(); - dataLog("'''\n"); -} - -} // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/EvalCodeCache.h b/Source/JavaScriptCore/bytecode/EvalCodeCache.h index 4d5909338..ff5911240 100644 --- a/Source/JavaScriptCore/bytecode/EvalCodeCache.h +++ b/Source/JavaScriptCore/bytecode/EvalCodeCache.h @@ -10,7 +10,7 @@ * 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 + * 3. Neither the name of Apple Computer, 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. * @@ -31,7 +31,6 @@ #include "Executable.h" #include "JSGlobalObject.h" -#include "Options.h" #include "SourceCode.h" #include <wtf/HashMap.h> #include <wtf/RefPtr.h> @@ -45,20 +44,18 @@ namespace JSC { public: EvalExecutable* tryGet(bool inStrictContext, const String& evalSource, JSScope* scope) { - if (isCacheable(inStrictContext, evalSource, scope)) + if (!inStrictContext && evalSource.length() < maxCacheableSourceLength && scope->begin()->isVariableObject()) return m_cacheMap.get(evalSource.impl()).get(); return 0; } - EvalExecutable* getSlow(ExecState* exec, ScriptExecutable* owner, bool inStrictContext, ThisTDZMode thisTDZMode, const String& evalSource, JSScope* scope) + EvalExecutable* getSlow(ExecState* exec, ScriptExecutable* owner, bool inStrictContext, const String& evalSource, JSScope* scope) { - VariableEnvironment variablesUnderTDZ; - JSScope::collectVariablesUnderTDZ(scope, variablesUnderTDZ); - EvalExecutable* evalExecutable = EvalExecutable::create(exec, makeSource(evalSource), inStrictContext, thisTDZMode, &variablesUnderTDZ); + EvalExecutable* evalExecutable = EvalExecutable::create(exec, makeSource(evalSource), inStrictContext); if (!evalExecutable) return 0; - if (isCacheable(inStrictContext, evalSource, scope) && m_cacheMap.size() < maxCacheEntries) + if (!inStrictContext && evalSource.length() < maxCacheableSourceLength && scope->begin()->isVariableObject() && m_cacheMap.size() < maxCacheEntries) m_cacheMap.set(evalSource.impl(), WriteBarrier<EvalExecutable>(exec->vm(), owner, evalExecutable)); return evalExecutable; @@ -74,16 +71,7 @@ namespace JSC { } private: - ALWAYS_INLINE bool isCacheable(bool inStrictContext, const String& evalSource, JSScope* scope) const - { - // If eval() is called and it has access to a lexical scope, we can't soundly cache it. - // If the eval() only has access to the "var" scope, then we can cache it. - return !inStrictContext - && evalSource.length() < Options::maximumEvalCacheableSourceLength() - && scope->begin()->isVariableObject() - && !scope->isLexicalScope() - && !scope->isCatchScope(); - } + static const unsigned maxCacheableSourceLength = 256; static const int maxCacheEntries = 64; typedef HashMap<RefPtr<StringImpl>, WriteBarrier<EvalExecutable>> EvalCacheMap; diff --git a/Source/JavaScriptCore/bytecode/ExecutableInfo.h b/Source/JavaScriptCore/bytecode/ExecutableInfo.h deleted file mode 100644 index c56a77008..000000000 --- a/Source/JavaScriptCore/bytecode/ExecutableInfo.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2012-2015 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 ExecutableInfo_h -#define ExecutableInfo_h - -#include "ParserModes.h" - -namespace JSC { - -struct ExecutableInfo { - ExecutableInfo(bool needsActivation, bool usesEval, bool isStrictMode, bool isConstructor, bool isBuiltinFunction, ConstructorKind constructorKind) - : m_needsActivation(needsActivation) - , m_usesEval(usesEval) - , m_isStrictMode(isStrictMode) - , m_isConstructor(isConstructor) - , m_isBuiltinFunction(isBuiltinFunction) - , m_constructorKind(static_cast<unsigned>(constructorKind)) - { - ASSERT(m_constructorKind == static_cast<unsigned>(constructorKind)); - } - - bool needsActivation() const { return m_needsActivation; } - bool usesEval() const { return m_usesEval; } - bool isStrictMode() const { return m_isStrictMode; } - bool isConstructor() const { return m_isConstructor; } - bool isBuiltinFunction() const { return m_isBuiltinFunction; } - ConstructorKind constructorKind() const { return static_cast<ConstructorKind>(m_constructorKind); } - -private: - unsigned m_needsActivation : 1; - unsigned m_usesEval : 1; - unsigned m_isStrictMode : 1; - unsigned m_isConstructor : 1; - unsigned m_isBuiltinFunction : 1; - unsigned m_constructorKind : 2; -}; - -} // namespace JSC - -#endif // ExecutableInfo_h diff --git a/Source/JavaScriptCore/bytecode/ExecutionCounter.cpp b/Source/JavaScriptCore/bytecode/ExecutionCounter.cpp index fe4e430f1..3a646a86a 100644 --- a/Source/JavaScriptCore/bytecode/ExecutionCounter.cpp +++ b/Source/JavaScriptCore/bytecode/ExecutionCounter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2014 Apple Inc. All rights reserved. + * Copyright (C) 2012 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,25 +28,21 @@ #include "CodeBlock.h" #include "ExecutableAllocator.h" -#include "JSCInlines.h" #include <wtf/StringExtras.h> namespace JSC { -template<CountingVariant countingVariant> -ExecutionCounter<countingVariant>::ExecutionCounter() +ExecutionCounter::ExecutionCounter() { reset(); } -template<CountingVariant countingVariant> -void ExecutionCounter<countingVariant>::forceSlowPathConcurrently() +void ExecutionCounter::forceSlowPathConcurrently() { m_counter = 0; } -template<CountingVariant countingVariant> -bool ExecutionCounter<countingVariant>::checkIfThresholdCrossedAndSet(CodeBlock* codeBlock) +bool ExecutionCounter::checkIfThresholdCrossedAndSet(CodeBlock* codeBlock) { if (hasCrossedThreshold(codeBlock)) return true; @@ -57,23 +53,21 @@ bool ExecutionCounter<countingVariant>::checkIfThresholdCrossedAndSet(CodeBlock* return false; } -template<CountingVariant countingVariant> -void ExecutionCounter<countingVariant>::setNewThreshold(int32_t threshold, CodeBlock* codeBlock) +void ExecutionCounter::setNewThreshold(int32_t threshold, CodeBlock* codeBlock) { reset(); m_activeThreshold = threshold; setThreshold(codeBlock); } -template<CountingVariant countingVariant> -void ExecutionCounter<countingVariant>::deferIndefinitely() +void ExecutionCounter::deferIndefinitely() { m_totalCount = 0; m_activeThreshold = std::numeric_limits<int32_t>::max(); m_counter = std::numeric_limits<int32_t>::min(); } -double applyMemoryUsageHeuristics(int32_t value, CodeBlock* codeBlock) +double ExecutionCounter::applyMemoryUsageHeuristics(int32_t value, CodeBlock* codeBlock) { #if ENABLE(JIT) double multiplier = @@ -88,7 +82,8 @@ double applyMemoryUsageHeuristics(int32_t value, CodeBlock* codeBlock) return multiplier * value; } -int32_t applyMemoryUsageHeuristicsAndConvertToInt(int32_t value, CodeBlock* codeBlock) +int32_t ExecutionCounter::applyMemoryUsageHeuristicsAndConvertToInt( + int32_t value, CodeBlock* codeBlock) { double doubleResult = applyMemoryUsageHeuristics(value, codeBlock); @@ -100,8 +95,7 @@ int32_t applyMemoryUsageHeuristicsAndConvertToInt(int32_t value, CodeBlock* code return static_cast<int32_t>(doubleResult); } -template<CountingVariant countingVariant> -bool ExecutionCounter<countingVariant>::hasCrossedThreshold(CodeBlock* codeBlock) const +bool ExecutionCounter::hasCrossedThreshold(CodeBlock* codeBlock) const { // This checks if the current count rounded up to the threshold we were targeting. // For example, if we are using half of available executable memory and have @@ -125,17 +119,18 @@ bool ExecutionCounter<countingVariant>::hasCrossedThreshold(CodeBlock* codeBlock return static_cast<double>(m_totalCount) + m_counter >= modifiedThreshold - static_cast<double>( - std::min(m_activeThreshold, maximumExecutionCountsBetweenCheckpoints())) / 2; + std::min(m_activeThreshold, Options::maximumExecutionCountsBetweenCheckpoints())) / 2; } -template<CountingVariant countingVariant> -bool ExecutionCounter<countingVariant>::setThreshold(CodeBlock* codeBlock) +bool ExecutionCounter::setThreshold(CodeBlock* codeBlock) { if (m_activeThreshold == std::numeric_limits<int32_t>::max()) { deferIndefinitely(); return false; } + ASSERT(!m_activeThreshold || !hasCrossedThreshold(codeBlock)); + // Compute the true total count. double trueTotalCount = count(); @@ -164,22 +159,17 @@ bool ExecutionCounter<countingVariant>::setThreshold(CodeBlock* codeBlock) return false; } -template<CountingVariant countingVariant> -void ExecutionCounter<countingVariant>::reset() +void ExecutionCounter::reset() { m_counter = 0; m_totalCount = 0; m_activeThreshold = 0; } -template<CountingVariant countingVariant> -void ExecutionCounter<countingVariant>::dump(PrintStream& out) const +void ExecutionCounter::dump(PrintStream& out) const { out.printf("%lf/%lf, %d", count(), static_cast<double>(m_activeThreshold), m_counter); } -template class ExecutionCounter<CountingForBaseline>; -template class ExecutionCounter<CountingForUpperTiers>; - } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/ExecutionCounter.h b/Source/JavaScriptCore/bytecode/ExecutionCounter.h index 5002c6c67..a7346691d 100644 --- a/Source/JavaScriptCore/bytecode/ExecutionCounter.h +++ b/Source/JavaScriptCore/bytecode/ExecutionCounter.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2014 Apple Inc. All rights reserved. + * Copyright (C) 2012 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -35,25 +35,6 @@ namespace JSC { class CodeBlock; -enum CountingVariant { - CountingForBaseline, - CountingForUpperTiers -}; - -double applyMemoryUsageHeuristics(int32_t value, CodeBlock*); -int32_t applyMemoryUsageHeuristicsAndConvertToInt(int32_t value, CodeBlock*); - -inline int32_t formattedTotalExecutionCount(float value) -{ - union { - int32_t i; - float f; - } u; - u.f = value; - return u.i; -} - -template<CountingVariant countingVariant> class ExecutionCounter { public: ExecutionCounter(); @@ -63,33 +44,31 @@ public: void deferIndefinitely(); double count() const { return static_cast<double>(m_totalCount) + m_counter; } void dump(PrintStream&) const; - - static int32_t maximumExecutionCountsBetweenCheckpoints() - { - switch (countingVariant) { - case CountingForBaseline: - return Options::maximumExecutionCountsBetweenCheckpointsForBaseline(); - case CountingForUpperTiers: - return Options::maximumExecutionCountsBetweenCheckpointsForUpperTiers(); - default: - RELEASE_ASSERT_NOT_REACHED(); - return 0; - } - } - + static double applyMemoryUsageHeuristics(int32_t value, CodeBlock*); + static int32_t applyMemoryUsageHeuristicsAndConvertToInt(int32_t value, CodeBlock*); template<typename T> static T clippedThreshold(JSGlobalObject* globalObject, T threshold) { int32_t maxThreshold; if (Options::randomizeExecutionCountsBetweenCheckpoints()) - maxThreshold = globalObject->weakRandomInteger() % maximumExecutionCountsBetweenCheckpoints(); + maxThreshold = globalObject->weakRandomInteger() % Options::maximumExecutionCountsBetweenCheckpoints(); else - maxThreshold = maximumExecutionCountsBetweenCheckpoints(); + maxThreshold = Options::maximumExecutionCountsBetweenCheckpoints(); if (threshold > maxThreshold) threshold = maxThreshold; return threshold; } + static int32_t formattedTotalCount(float value) + { + union { + int32_t i; + float f; + } u; + u.f = value; + return u.i; + } + private: bool hasCrossedThreshold(CodeBlock*) const; bool setThreshold(CodeBlock*); @@ -110,14 +89,11 @@ public: // m_counter. float m_totalCount; - // This is the threshold we were originally targeting, without any correction for + // This is the threshold we were originally targetting, without any correction for // the memory usage heuristics. int32_t m_activeThreshold; }; -typedef ExecutionCounter<CountingForBaseline> BaselineExecutionCounter; -typedef ExecutionCounter<CountingForUpperTiers> UpperTierExecutionCounter; - } // namespace JSC #endif // ExecutionCounter_h diff --git a/Source/JavaScriptCore/bytecode/ExitKind.cpp b/Source/JavaScriptCore/bytecode/ExitKind.cpp index 2524300f7..350aa5857 100644 --- a/Source/JavaScriptCore/bytecode/ExitKind.cpp +++ b/Source/JavaScriptCore/bytecode/ExitKind.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2015 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2013, 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -38,16 +38,18 @@ const char* exitKindToString(ExitKind kind) return "Unset"; case BadType: return "BadType"; - case BadCell: - return "BadCell"; - case BadIdent: - return "BadIdent"; + case BadFunction: + return "BadFunction"; case BadExecutable: return "BadExecutable"; case BadCache: return "BadCache"; - case BadConstantCache: - return "BadConstantCache"; + case BadCacheWatchpoint: + return "BadCacheWatchpoint"; + case BadWeakConstantCache: + return "BadWeakConstantCache"; + case BadWeakConstantCacheWatchpoint: + return "BadWeakConstantCacheWatchpoint"; case BadIndexingType: return "BadIndexingType"; case Overflow: @@ -66,16 +68,12 @@ const char* exitKindToString(ExitKind kind) return "InadequateCoverage"; case ArgumentsEscaped: return "ArgumentsEscaped"; - case ExoticObjectMode: - return "ExoticObjectMode"; case NotStringObject: return "NotStringObject"; - case VarargsOverflow: - return "VarargsOverflow"; - case TDZFailure: - return "TDZFailure"; case Uncountable: return "Uncountable"; + case UncountableWatchpoint: + return "UncountableWatchpoint"; case UncountableInvalidation: return "UncountableInvalidation"; case WatchdogTimerFired: @@ -94,6 +92,7 @@ bool exitKindIsCountable(ExitKind kind) RELEASE_ASSERT_NOT_REACHED(); case BadType: case Uncountable: + case UncountableWatchpoint: case LoadFromHole: // Already counted directly by the baseline JIT. case StoreToHole: // Already counted directly by the baseline JIT. case OutOfBounds: // Already counted directly by the baseline JIT. diff --git a/Source/JavaScriptCore/bytecode/ExitKind.h b/Source/JavaScriptCore/bytecode/ExitKind.h index 6f8c51200..a9f6df6d4 100644 --- a/Source/JavaScriptCore/bytecode/ExitKind.h +++ b/Source/JavaScriptCore/bytecode/ExitKind.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2015 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2013, 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,14 +28,15 @@ namespace JSC { -enum ExitKind : uint8_t { +enum ExitKind { ExitKindUnset, BadType, // We exited because a type prediction was wrong. - BadCell, // We exited because we made an incorrect assumption about what cell we would see. Usually used for function checks. - BadIdent, // We exited because we made an incorrect assumption about what identifier we would see. Usually used for cached Id check in get_by_val. + BadFunction, // We exited because we made an incorrect assumption about what function we would see. BadExecutable, // We exited because we made an incorrect assumption about what executable we would see. BadCache, // We exited because an inline cache was wrong. - BadConstantCache, // We exited because a cache on a weak constant (usually a prototype) was wrong. + BadWeakConstantCache, // We exited because a cache on a weak constant (usually a prototype) was wrong. + BadCacheWatchpoint, // Same as BadCache but from a watchpoint. + BadWeakConstantCacheWatchpoint, // Same as BadWeakConstantCache but from a watchpoint. BadIndexingType, // We exited because an indexing type was wrong. Overflow, // We exited because of overflow. NegativeZero, // We exited because we encountered negative zero. @@ -45,12 +46,10 @@ enum ExitKind : uint8_t { OutOfBounds, // We had an out-of-bounds access to an array. InadequateCoverage, // We exited because we ended up in code that didn't have profiling coverage. ArgumentsEscaped, // We exited because arguments escaped but we didn't expect them to. - ExoticObjectMode, // We exited because some exotic object that we were accessing was in an exotic mode (like Arguments with slow arguments). NotStringObject, // We exited because we shouldn't have attempted to optimize string object access. - VarargsOverflow, // We exited because a varargs call passed more arguments than we expected. - TDZFailure, // We exited because we were in the TDZ and accessed the variable. Uncountable, // We exited for none of the above reasons, and we should not count it. Most uses of this should be viewed as a FIXME. UncountableInvalidation, // We exited because the code block was invalidated; this means that we've already counted the reasons why the code block was invalidated. + UncountableWatchpoint, // We exited because of a watchpoint, which isn't counted because watchpoints do tracking themselves. WatchdogTimerFired, // We exited because we need to service the watchdog timer. DebuggerEvent // We exited because we need to service the debugger. }; @@ -58,6 +57,18 @@ enum ExitKind : uint8_t { const char* exitKindToString(ExitKind); bool exitKindIsCountable(ExitKind); +inline bool isWatchpoint(ExitKind kind) +{ + switch (kind) { + case BadCacheWatchpoint: + case BadWeakConstantCacheWatchpoint: + case UncountableWatchpoint: + return true; + default: + return false; + } +} + } // namespace JSC namespace WTF { diff --git a/Source/JavaScriptCore/bytecode/ExitingJITType.cpp b/Source/JavaScriptCore/bytecode/ExitingJITType.cpp deleted file mode 100644 index aa8f120b6..000000000 --- a/Source/JavaScriptCore/bytecode/ExitingJITType.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2014 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. - */ - -#include "config.h" -#include "ExitingJITType.h" - -#include <wtf/PrintStream.h> - -namespace WTF { - -using namespace JSC; - -void printInternal(PrintStream& out, ExitingJITType type) -{ - switch (type) { - case ExitFromAnything: - out.print("FromAnything"); - return; - case ExitFromDFG: - out.print("FromDFG"); - return; - case ExitFromFTL: - out.print("FromFTL"); - return; - } - RELEASE_ASSERT_NOT_REACHED(); -} - -} // namespace WTF - diff --git a/Source/JavaScriptCore/bytecode/ExitingJITType.h b/Source/JavaScriptCore/bytecode/ExitingJITType.h deleted file mode 100644 index e8ed03e41..000000000 --- a/Source/JavaScriptCore/bytecode/ExitingJITType.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2014 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 ExitingJITType_h -#define ExitingJITType_h - -#include "JITCode.h" - -namespace JSC { - -enum ExitingJITType : uint8_t { - ExitFromAnything, - ExitFromDFG, - ExitFromFTL -}; - -inline ExitingJITType exitingJITTypeFor(JITCode::JITType type) -{ - switch (type) { - case JITCode::DFGJIT: - return ExitFromDFG; - case JITCode::FTLJIT: - return ExitFromFTL; - default: - RELEASE_ASSERT_NOT_REACHED(); - return ExitFromAnything; - } -} - -} // namespace JSC - -namespace WTF { - -class PrintStream; -void printInternal(PrintStream&, JSC::ExitingJITType); - -} // namespace WTF - -#endif // ExitingJITType_h - diff --git a/Source/JavaScriptCore/bytecode/FullBytecodeLiveness.h b/Source/JavaScriptCore/bytecode/FullBytecodeLiveness.h index b22198a00..d34392121 100644 --- a/Source/JavaScriptCore/bytecode/FullBytecodeLiveness.h +++ b/Source/JavaScriptCore/bytecode/FullBytecodeLiveness.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -35,22 +35,33 @@ class BytecodeLivenessAnalysis; typedef HashMap<unsigned, FastBitVector, WTF::IntHash<unsigned>, WTF::UnsignedWithZeroKeyHashTraits<unsigned>> BytecodeToBitmapMap; class FullBytecodeLiveness { - WTF_MAKE_FAST_ALLOCATED; public: - const FastBitVector& getLiveness(unsigned bytecodeIndex) const + FullBytecodeLiveness() : m_codeBlock(0) { } + + // We say "out" to refer to the bitvector that contains raw results for a bytecode + // instruction. + const FastBitVector& getOut(unsigned bytecodeIndex) const { - return m_map[bytecodeIndex]; + BytecodeToBitmapMap::const_iterator iter = m_map.find(bytecodeIndex); + ASSERT(iter != m_map.end()); + return iter->value; } bool operandIsLive(int operand, unsigned bytecodeIndex) const { - return operandIsAlwaysLive(operand) || operandThatIsNotAlwaysLiveIsLive(getLiveness(bytecodeIndex), operand); + return operandIsAlwaysLive(m_codeBlock, operand) || operandThatIsNotAlwaysLiveIsLive(m_codeBlock, getOut(bytecodeIndex), operand); + } + + FastBitVector getLiveness(unsigned bytecodeIndex) const + { + return getLivenessInfo(m_codeBlock, getOut(bytecodeIndex)); } private: friend class BytecodeLivenessAnalysis; - Vector<FastBitVector, 0, UnsafeVectorOverflow> m_map; + CodeBlock* m_codeBlock; + BytecodeToBitmapMap m_map; }; } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/GetByIdStatus.cpp b/Source/JavaScriptCore/bytecode/GetByIdStatus.cpp index 89e5035f3..fbb3da1a5 100644 --- a/Source/JavaScriptCore/bytecode/GetByIdStatus.cpp +++ b/Source/JavaScriptCore/bytecode/GetByIdStatus.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2015 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,54 +26,23 @@ #include "config.h" #include "GetByIdStatus.h" -#include "AccessorCallJITStubRoutine.h" #include "CodeBlock.h" -#include "ComplexGetStatus.h" -#include "JSCInlines.h" #include "JSScope.h" #include "LLIntData.h" #include "LowLevelInterpreter.h" -#include "PolymorphicGetByIdList.h" -#include <wtf/ListDump.h> +#include "Operations.h" namespace JSC { -bool GetByIdStatus::appendVariant(const GetByIdVariant& variant) -{ - // Attempt to merge this variant with an already existing variant. - for (unsigned i = 0; i < m_variants.size(); ++i) { - if (m_variants[i].attemptToMerge(variant)) - return true; - } - - // Make sure there is no overlap. We should have pruned out opportunities for - // overlap but it's possible that an inline cache got into a weird state. We are - // defensive and bail if we detect crazy. - for (unsigned i = 0; i < m_variants.size(); ++i) { - if (m_variants[i].structureSet().overlaps(variant.structureSet())) - return false; - } - - m_variants.append(variant); - return true; -} - -#if ENABLE(DFG_JIT) -bool GetByIdStatus::hasExitSite(const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, unsigned bytecodeIndex) -{ - return profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCache)) - || profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadConstantCache)); -} -#endif - -GetByIdStatus GetByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned bytecodeIndex, UniquedStringImpl* uid) +GetByIdStatus GetByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned bytecodeIndex, StringImpl* uid) { UNUSED_PARAM(profiledBlock); UNUSED_PARAM(bytecodeIndex); UNUSED_PARAM(uid); +#if ENABLE(LLINT) Instruction* instruction = profiledBlock->instructions().begin() + bytecodeIndex; - if (instruction[0].u.opcode == LLInt::getOpcode(op_get_array_length)) + if (instruction[0].u.opcode == LLInt::getOpcode(llint_op_get_array_length)) return GetByIdStatus(NoInformation, false); Structure* structure = instruction[4].u.structure.get(); @@ -84,276 +53,253 @@ GetByIdStatus GetByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned return GetByIdStatus(NoInformation, false); unsigned attributesIgnored; - PropertyOffset offset = structure->getConcurrently(uid, attributesIgnored); + JSCell* specificValue; + PropertyOffset offset = structure->getConcurrently( + *profiledBlock->vm(), uid, attributesIgnored, specificValue); + if (structure->isDictionary()) + specificValue = 0; if (!isValidOffset(offset)) return GetByIdStatus(NoInformation, false); - return GetByIdStatus(Simple, false, GetByIdVariant(StructureSet(structure), offset)); + return GetByIdStatus(Simple, false, StructureSet(structure), offset, specificValue); +#else + return GetByIdStatus(NoInformation, false); +#endif } -GetByIdStatus GetByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& map, unsigned bytecodeIndex, UniquedStringImpl* uid) +void GetByIdStatus::computeForChain(GetByIdStatus& result, CodeBlock* profiledBlock, StringImpl* uid) { - ConcurrentJITLocker locker(profiledBlock->m_lock); +#if ENABLE(JIT) + // Validate the chain. If the chain is invalid, then currently the best thing + // we can do is to assume that TakesSlow is true. In the future, it might be + // worth exploring reifying the structure chain from the structure we've got + // instead of using the one from the cache, since that will do the right things + // if the structure chain has changed. But that may be harder, because we may + // then end up having a different type of access altogether. And it currently + // does not appear to be worth it to do so -- effectively, the heuristic we + // have now is that if the structure chain has changed between when it was + // cached on in the baseline JIT and when the DFG tried to inline the access, + // then we fall back on a polymorphic access. + if (!result.m_chain->isStillValid()) + return; - GetByIdStatus result; + if (result.m_chain->head()->takesSlowPathInDFGForImpureProperty()) + return; + size_t chainSize = result.m_chain->size(); + for (size_t i = 0; i < chainSize; i++) { + if (result.m_chain->at(i)->takesSlowPathInDFGForImpureProperty()) + return; + } -#if ENABLE(DFG_JIT) - result = computeForStubInfoWithoutExitSiteFeedback( - locker, profiledBlock, map.get(CodeOrigin(bytecodeIndex)), uid, - CallLinkStatus::computeExitSiteData(locker, profiledBlock, bytecodeIndex)); + JSObject* currentObject = result.m_chain->terminalPrototype(); + Structure* currentStructure = result.m_chain->last(); - if (!result.takesSlowPath() - && hasExitSite(locker, profiledBlock, bytecodeIndex)) - return GetByIdStatus(result.makesCalls() ? MakesCalls : TakesSlowPath, true); + ASSERT_UNUSED(currentObject, currentObject); + + unsigned attributesIgnored; + JSCell* specificValue; + + result.m_offset = currentStructure->getConcurrently( + *profiledBlock->vm(), uid, attributesIgnored, specificValue); + if (currentStructure->isDictionary()) + specificValue = 0; + if (!isValidOffset(result.m_offset)) + return; + + result.m_structureSet.add(result.m_chain->head()); + result.m_specificValue = JSValue(specificValue); #else - UNUSED_PARAM(map); + UNUSED_PARAM(result); + UNUSED_PARAM(profiledBlock); + UNUSED_PARAM(uid); + UNREACHABLE_FOR_PLATFORM(); #endif - - if (!result) - return computeFromLLInt(profiledBlock, bytecodeIndex, uid); - - return result; } -#if ENABLE(JIT) -GetByIdStatus GetByIdStatus::computeForStubInfo(const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, CodeOrigin codeOrigin, UniquedStringImpl* uid) -{ - GetByIdStatus result = GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback( - locker, profiledBlock, stubInfo, uid, - CallLinkStatus::computeExitSiteData(locker, profiledBlock, codeOrigin.bytecodeIndex)); - - if (!result.takesSlowPath() && GetByIdStatus::hasExitSite(locker, profiledBlock, codeOrigin.bytecodeIndex)) - return GetByIdStatus(result.makesCalls() ? GetByIdStatus::MakesCalls : GetByIdStatus::TakesSlowPath, true); - return result; -} -#endif // ENABLE(JIT) - -#if ENABLE(JIT) -GetByIdStatus GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback( - const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, UniquedStringImpl* uid, - CallLinkStatus::ExitSiteData callExitSiteData) +GetByIdStatus GetByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& map, unsigned bytecodeIndex, StringImpl* uid) { - if (!stubInfo) - return GetByIdStatus(NoInformation); + ConcurrentJITLocker locker(profiledBlock->m_lock); - if (!stubInfo->seen) - return GetByIdStatus(NoInformation); + UNUSED_PARAM(profiledBlock); + UNUSED_PARAM(bytecodeIndex); + UNUSED_PARAM(uid); +#if ENABLE(JIT) + StructureStubInfo* stubInfo = map.get(CodeOrigin(bytecodeIndex)); + if (!stubInfo || !stubInfo->seen) + return computeFromLLInt(profiledBlock, bytecodeIndex, uid); - PolymorphicGetByIdList* list = 0; - State slowPathState = TakesSlowPath; - if (stubInfo->accessType == access_get_by_id_list) { - list = stubInfo->u.getByIdList.list; - for (unsigned i = 0; i < list->size(); ++i) { - const GetByIdAccess& access = list->at(i); - if (access.doesCalls()) - slowPathState = MakesCalls; - } + if (stubInfo->resetByGC) + return GetByIdStatus(TakesSlowPath, true); + + PolymorphicAccessStructureList* list; + int listSize; + switch (stubInfo->accessType) { + case access_get_by_id_self_list: + list = stubInfo->u.getByIdSelfList.structureList; + listSize = stubInfo->u.getByIdSelfList.listSize; + break; + case access_get_by_id_proto_list: + list = stubInfo->u.getByIdProtoList.structureList; + listSize = stubInfo->u.getByIdProtoList.listSize; + break; + default: + list = 0; + listSize = 0; + break; + } + for (int i = 0; i < listSize; ++i) { + if (!list->list[i].isDirect) + return GetByIdStatus(MakesCalls, true); } - if (stubInfo->tookSlowPath) - return GetByIdStatus(slowPathState); + // Next check if it takes slow case, in which case we want to be kind of careful. + if (profiledBlock->likelyToTakeSlowCase(bytecodeIndex)) + return GetByIdStatus(TakesSlowPath, true); // Finally figure out if we can derive an access strategy. GetByIdStatus result; - result.m_state = Simple; result.m_wasSeenInJIT = true; // This is interesting for bytecode dumping only. switch (stubInfo->accessType) { case access_unset: - return GetByIdStatus(NoInformation); + return computeFromLLInt(profiledBlock, bytecodeIndex, uid); case access_get_by_id_self: { Structure* structure = stubInfo->u.getByIdSelf.baseObjectStructure.get(); if (structure->takesSlowPathInDFGForImpureProperty()) - return GetByIdStatus(slowPathState, true); + return GetByIdStatus(TakesSlowPath, true); unsigned attributesIgnored; - GetByIdVariant variant; - variant.m_offset = structure->getConcurrently(uid, attributesIgnored); - if (!isValidOffset(variant.m_offset)) - return GetByIdStatus(slowPathState, true); + JSCell* specificValue; + result.m_offset = structure->getConcurrently( + *profiledBlock->vm(), uid, attributesIgnored, specificValue); + if (structure->isDictionary()) + specificValue = 0; - variant.m_structureSet.add(structure); - bool didAppend = result.appendVariant(variant); - ASSERT_UNUSED(didAppend, didAppend); - return result; + if (isValidOffset(result.m_offset)) { + result.m_structureSet.add(structure); + result.m_specificValue = JSValue(specificValue); + } + + if (isValidOffset(result.m_offset)) + ASSERT(result.m_structureSet.size()); + break; } - case access_get_by_id_list: { - for (unsigned listIndex = 0; listIndex < list->size(); ++listIndex) { - Structure* structure = list->at(listIndex).structure(); + case access_get_by_id_self_list: { + for (int i = 0; i < listSize; ++i) { + ASSERT(list->list[i].isDirect); - ComplexGetStatus complexGetStatus = ComplexGetStatus::computeFor( - structure, list->at(listIndex).conditionSet(), uid); - - switch (complexGetStatus.kind()) { - case ComplexGetStatus::ShouldSkip: + Structure* structure = list->list[i].base.get(); + if (structure->takesSlowPathInDFGForImpureProperty()) + return GetByIdStatus(TakesSlowPath, true); + + if (result.m_structureSet.contains(structure)) continue; - - case ComplexGetStatus::TakesSlowPath: - return GetByIdStatus(slowPathState, true); - - case ComplexGetStatus::Inlineable: { - std::unique_ptr<CallLinkStatus> callLinkStatus; - switch (list->at(listIndex).type()) { - case GetByIdAccess::SimpleInline: - case GetByIdAccess::SimpleStub: { - break; - } - case GetByIdAccess::Getter: { - AccessorCallJITStubRoutine* stub = static_cast<AccessorCallJITStubRoutine*>( - list->at(listIndex).stubRoutine()); - callLinkStatus = std::make_unique<CallLinkStatus>( - CallLinkStatus::computeFor( - locker, profiledBlock, *stub->m_callLinkInfo, callExitSiteData)); - break; - } - case GetByIdAccess::SimpleMiss: - case GetByIdAccess::CustomGetter: - case GetByIdAccess::WatchedStub:{ - // FIXME: It would be totally sweet to support this at some point in the future. - // https://bugs.webkit.org/show_bug.cgi?id=133052 - return GetByIdStatus(slowPathState, true); - } - default: - RELEASE_ASSERT_NOT_REACHED(); - } - - GetByIdVariant variant( - StructureSet(structure), complexGetStatus.offset(), - complexGetStatus.conditionSet(), WTF::move(callLinkStatus)); - - if (!result.appendVariant(variant)) - return GetByIdStatus(slowPathState, true); + + unsigned attributesIgnored; + JSCell* specificValue; + PropertyOffset myOffset = structure->getConcurrently( + *profiledBlock->vm(), uid, attributesIgnored, specificValue); + if (structure->isDictionary()) + specificValue = 0; + + if (!isValidOffset(myOffset)) { + result.m_offset = invalidOffset; + break; + } + + if (!i) { + result.m_offset = myOffset; + result.m_specificValue = JSValue(specificValue); + } else if (result.m_offset != myOffset) { + result.m_offset = invalidOffset; break; - } } + } else if (result.m_specificValue != JSValue(specificValue)) + result.m_specificValue = JSValue(); + + result.m_structureSet.add(structure); } + + if (isValidOffset(result.m_offset)) + ASSERT(result.m_structureSet.size()); + break; + } - return result; + case access_get_by_id_proto: { + if (!stubInfo->u.getByIdProto.isDirect) + return GetByIdStatus(MakesCalls, true); + result.m_chain = adoptRef(new IntendedStructureChain( + profiledBlock, + stubInfo->u.getByIdProto.baseObjectStructure.get(), + stubInfo->u.getByIdProto.prototypeStructure.get())); + computeForChain(result, profiledBlock, uid); + break; + } + + case access_get_by_id_chain: { + if (!stubInfo->u.getByIdChain.isDirect) + return GetByIdStatus(MakesCalls, true); + result.m_chain = adoptRef(new IntendedStructureChain( + profiledBlock, + stubInfo->u.getByIdChain.baseObjectStructure.get(), + stubInfo->u.getByIdChain.chain.get(), + stubInfo->u.getByIdChain.count)); + computeForChain(result, profiledBlock, uid); + break; } default: - return GetByIdStatus(slowPathState, true); + ASSERT(!isValidOffset(result.m_offset)); + break; } - RELEASE_ASSERT_NOT_REACHED(); - return GetByIdStatus(); -} -#endif // ENABLE(JIT) - -GetByIdStatus GetByIdStatus::computeFor( - CodeBlock* profiledBlock, CodeBlock* dfgBlock, StubInfoMap& baselineMap, - StubInfoMap& dfgMap, CodeOrigin codeOrigin, UniquedStringImpl* uid) -{ -#if ENABLE(DFG_JIT) - if (dfgBlock) { - CallLinkStatus::ExitSiteData exitSiteData; - { - ConcurrentJITLocker locker(profiledBlock->m_lock); - exitSiteData = CallLinkStatus::computeExitSiteData( - locker, profiledBlock, codeOrigin.bytecodeIndex); - } - - GetByIdStatus result; - { - ConcurrentJITLocker locker(dfgBlock->m_lock); - result = computeForStubInfoWithoutExitSiteFeedback( - locker, dfgBlock, dfgMap.get(codeOrigin), uid, exitSiteData); - } - - if (result.takesSlowPath()) - return result; + if (!isValidOffset(result.m_offset)) { + result.m_state = TakesSlowPath; + result.m_structureSet.clear(); + result.m_chain.clear(); + result.m_specificValue = JSValue(); + } else + result.m_state = Simple; - { - ConcurrentJITLocker locker(profiledBlock->m_lock); - if (hasExitSite(locker, profiledBlock, codeOrigin.bytecodeIndex)) - return GetByIdStatus(TakesSlowPath, true); - } - - if (result.isSet()) - return result; - } -#else - UNUSED_PARAM(dfgBlock); - UNUSED_PARAM(dfgMap); -#endif - - return computeFor(profiledBlock, baselineMap, codeOrigin.bytecodeIndex, uid); + return result; +#else // ENABLE(JIT) + UNUSED_PARAM(map); + return GetByIdStatus(NoInformation, false); +#endif // ENABLE(JIT) } -GetByIdStatus GetByIdStatus::computeFor(const StructureSet& set, UniquedStringImpl* uid) +GetByIdStatus GetByIdStatus::computeFor(VM& vm, Structure* structure, StringImpl* uid) { // For now we only handle the super simple self access case. We could handle the // prototype case in the future. - if (set.isEmpty()) - return GetByIdStatus(); + if (!structure) + return GetByIdStatus(TakesSlowPath); - if (parseIndex(*uid)) + if (toUInt32FromStringImpl(uid) != PropertyName::NotAnIndex) return GetByIdStatus(TakesSlowPath); + if (structure->typeInfo().overridesGetOwnPropertySlot() && structure->typeInfo().type() != GlobalObjectType) + return GetByIdStatus(TakesSlowPath); + + if (!structure->propertyAccessesAreCacheable()) + return GetByIdStatus(TakesSlowPath); + GetByIdStatus result; + result.m_wasSeenInJIT = false; // To my knowledge nobody that uses computeFor(VM&, Structure*, StringImpl*) reads this field, but I might as well be honest: no, it wasn't seen in the JIT, since I computed it statically. + unsigned attributes; + JSCell* specificValue; + result.m_offset = structure->getConcurrently(vm, uid, attributes, specificValue); + if (!isValidOffset(result.m_offset)) + return GetByIdStatus(TakesSlowPath); // It's probably a prototype lookup. Give up on life for now, even though we could totally be way smarter about it. + if (attributes & Accessor) + return GetByIdStatus(MakesCalls); + if (structure->isDictionary()) + specificValue = 0; + result.m_structureSet.add(structure); + result.m_specificValue = JSValue(specificValue); result.m_state = Simple; - result.m_wasSeenInJIT = false; - for (unsigned i = 0; i < set.size(); ++i) { - Structure* structure = set[i]; - if (structure->typeInfo().overridesGetOwnPropertySlot() && structure->typeInfo().type() != GlobalObjectType) - return GetByIdStatus(TakesSlowPath); - - if (!structure->propertyAccessesAreCacheable()) - return GetByIdStatus(TakesSlowPath); - - unsigned attributes; - PropertyOffset offset = structure->getConcurrently(uid, attributes); - if (!isValidOffset(offset)) - return GetByIdStatus(TakesSlowPath); // It's probably a prototype lookup. Give up on life for now, even though we could totally be way smarter about it. - if (attributes & Accessor) - return GetByIdStatus(MakesCalls); // We could be smarter here, like strenght-reducing this to a Call. - - if (!result.appendVariant(GetByIdVariant(structure, offset))) - return GetByIdStatus(TakesSlowPath); - } - return result; } -bool GetByIdStatus::makesCalls() const -{ - switch (m_state) { - case NoInformation: - case TakesSlowPath: - return false; - case Simple: - for (unsigned i = m_variants.size(); i--;) { - if (m_variants[i].callLinkStatus()) - return true; - } - return false; - case MakesCalls: - return true; - } - RELEASE_ASSERT_NOT_REACHED(); - - return false; -} - -void GetByIdStatus::dump(PrintStream& out) const -{ - out.print("("); - switch (m_state) { - case NoInformation: - out.print("NoInformation"); - break; - case Simple: - out.print("Simple"); - break; - case TakesSlowPath: - out.print("TakesSlowPath"); - break; - case MakesCalls: - out.print("MakesCalls"); - break; - } - out.print(", ", listDump(m_variants), ", seenInJIT = ", m_wasSeenInJIT, ")"); -} - } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/GetByIdStatus.h b/Source/JavaScriptCore/bytecode/GetByIdStatus.h index d7f0ae496..a1e801cca 100644 --- a/Source/JavaScriptCore/bytecode/GetByIdStatus.h +++ b/Source/JavaScriptCore/bytecode/GetByIdStatus.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2013, 2014 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,11 +26,9 @@ #ifndef GetByIdStatus_h #define GetByIdStatus_h -#include "CallLinkStatus.h" -#include "CodeOrigin.h" -#include "ConcurrentJITLock.h" -#include "ExitingJITType.h" -#include "GetByIdVariant.h" +#include "IntendedStructureChain.h" +#include "PropertyOffset.h" +#include "StructureSet.h" #include "StructureStubInfo.h" namespace JSC { @@ -49,66 +47,57 @@ public: GetByIdStatus() : m_state(NoInformation) + , m_offset(invalidOffset) { } explicit GetByIdStatus(State state) : m_state(state) + , m_offset(invalidOffset) { ASSERT(state == NoInformation || state == TakesSlowPath || state == MakesCalls); } GetByIdStatus( - State state, bool wasSeenInJIT, const GetByIdVariant& variant = GetByIdVariant()) + State state, bool wasSeenInJIT, const StructureSet& structureSet = StructureSet(), + PropertyOffset offset = invalidOffset, JSValue specificValue = JSValue(), PassRefPtr<IntendedStructureChain> chain = nullptr) : m_state(state) + , m_structureSet(structureSet) + , m_chain(chain) + , m_specificValue(specificValue) + , m_offset(offset) , m_wasSeenInJIT(wasSeenInJIT) { - ASSERT((state == Simple) == variant.isSet()); - m_variants.append(variant); + ASSERT((state == Simple) == (offset != invalidOffset)); } - static GetByIdStatus computeFor(CodeBlock*, StubInfoMap&, unsigned bytecodeIndex, UniquedStringImpl* uid); - static GetByIdStatus computeFor(const StructureSet&, UniquedStringImpl* uid); + static GetByIdStatus computeFor(CodeBlock*, StubInfoMap&, unsigned bytecodeIndex, StringImpl* uid); + static GetByIdStatus computeFor(VM&, Structure*, StringImpl* uid); - static GetByIdStatus computeFor(CodeBlock* baselineBlock, CodeBlock* dfgBlock, StubInfoMap& baselineMap, StubInfoMap& dfgMap, CodeOrigin, UniquedStringImpl* uid); - -#if ENABLE(JIT) - static GetByIdStatus computeForStubInfo(const ConcurrentJITLocker&, CodeBlock* baselineBlock, StructureStubInfo*, CodeOrigin, UniquedStringImpl* uid); -#endif - State state() const { return m_state; } bool isSet() const { return m_state != NoInformation; } bool operator!() const { return !isSet(); } bool isSimple() const { return m_state == Simple; } - - size_t numVariants() const { return m_variants.size(); } - const Vector<GetByIdVariant, 1>& variants() const { return m_variants; } - const GetByIdVariant& at(size_t index) const { return m_variants[index]; } - const GetByIdVariant& operator[](size_t index) const { return at(index); } - bool takesSlowPath() const { return m_state == TakesSlowPath || m_state == MakesCalls; } - bool makesCalls() const; + bool makesCalls() const { return m_state == MakesCalls; } - bool wasSeenInJIT() const { return m_wasSeenInJIT; } + const StructureSet& structureSet() const { return m_structureSet; } + IntendedStructureChain* chain() const { return const_cast<IntendedStructureChain*>(m_chain.get()); } // Returns null if this is a direct access. + JSValue specificValue() const { return m_specificValue; } // Returns JSValue() if there is no specific value. + PropertyOffset offset() const { return m_offset; } - void dump(PrintStream&) const; + bool wasSeenInJIT() const { return m_wasSeenInJIT; } private: -#if ENABLE(DFG_JIT) - static bool hasExitSite(const ConcurrentJITLocker&, CodeBlock*, unsigned bytecodeIndex); -#endif -#if ENABLE(JIT) - static GetByIdStatus computeForStubInfoWithoutExitSiteFeedback( - const ConcurrentJITLocker&, CodeBlock* profiledBlock, StructureStubInfo*, - UniquedStringImpl* uid, CallLinkStatus::ExitSiteData); -#endif - static GetByIdStatus computeFromLLInt(CodeBlock*, unsigned bytecodeIndex, UniquedStringImpl* uid); - - bool appendVariant(const GetByIdVariant&); + static void computeForChain(GetByIdStatus& result, CodeBlock*, StringImpl* uid); + static GetByIdStatus computeFromLLInt(CodeBlock*, unsigned bytecodeIndex, StringImpl* uid); State m_state; - Vector<GetByIdVariant, 1> m_variants; + StructureSet m_structureSet; + RefPtr<IntendedStructureChain> m_chain; + JSValue m_specificValue; + PropertyOffset m_offset; bool m_wasSeenInJIT; }; diff --git a/Source/JavaScriptCore/bytecode/GetByIdVariant.cpp b/Source/JavaScriptCore/bytecode/GetByIdVariant.cpp deleted file mode 100644 index ea6fa12fb..000000000 --- a/Source/JavaScriptCore/bytecode/GetByIdVariant.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2014, 2015 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. - */ - -#include "config.h" -#include "GetByIdVariant.h" - -#include "CallLinkStatus.h" -#include "JSCInlines.h" -#include <wtf/ListDump.h> - -namespace JSC { - -GetByIdVariant::GetByIdVariant( - const StructureSet& structureSet, PropertyOffset offset, - const ObjectPropertyConditionSet& conditionSet, - std::unique_ptr<CallLinkStatus> callLinkStatus) - : m_structureSet(structureSet) - , m_conditionSet(conditionSet) - , m_offset(offset) - , m_callLinkStatus(WTF::move(callLinkStatus)) -{ - if (!structureSet.size()) { - ASSERT(offset == invalidOffset); - ASSERT(conditionSet.isEmpty()); - } -} - -GetByIdVariant::~GetByIdVariant() { } - -GetByIdVariant::GetByIdVariant(const GetByIdVariant& other) - : GetByIdVariant() -{ - *this = other; -} - -GetByIdVariant& GetByIdVariant::operator=(const GetByIdVariant& other) -{ - m_structureSet = other.m_structureSet; - m_conditionSet = other.m_conditionSet; - m_offset = other.m_offset; - if (other.m_callLinkStatus) - m_callLinkStatus = std::make_unique<CallLinkStatus>(*other.m_callLinkStatus); - else - m_callLinkStatus = nullptr; - return *this; -} - -bool GetByIdVariant::attemptToMerge(const GetByIdVariant& other) -{ - if (m_offset != other.m_offset) - return false; - if (m_callLinkStatus || other.m_callLinkStatus) - return false; - - if (m_conditionSet.isEmpty() != other.m_conditionSet.isEmpty()) - return false; - - ObjectPropertyConditionSet mergedConditionSet; - if (!m_conditionSet.isEmpty()) { - mergedConditionSet = m_conditionSet.mergedWith(other.m_conditionSet); - if (!mergedConditionSet.isValid() || !mergedConditionSet.hasOneSlotBaseCondition()) - return false; - } - m_conditionSet = mergedConditionSet; - - m_structureSet.merge(other.m_structureSet); - - return true; -} - -void GetByIdVariant::dump(PrintStream& out) const -{ - dumpInContext(out, 0); -} - -void GetByIdVariant::dumpInContext(PrintStream& out, DumpContext* context) const -{ - if (!isSet()) { - out.print("<empty>"); - return; - } - - out.print( - "<", inContext(structureSet(), context), ", ", inContext(m_conditionSet, context)); - out.print(", offset = ", offset()); - if (m_callLinkStatus) - out.print(", call = ", *m_callLinkStatus); - out.print(">"); -} - -} // namespace JSC - diff --git a/Source/JavaScriptCore/bytecode/GetByIdVariant.h b/Source/JavaScriptCore/bytecode/GetByIdVariant.h deleted file mode 100644 index 714fb9843..000000000 --- a/Source/JavaScriptCore/bytecode/GetByIdVariant.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2014, 2015 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 GetByIdVariant_h -#define GetByIdVariant_h - -#include "CallLinkStatus.h" -#include "JSCJSValue.h" -#include "ObjectPropertyConditionSet.h" -#include "PropertyOffset.h" -#include "StructureSet.h" - -namespace JSC { - -class CallLinkStatus; -class GetByIdStatus; -struct DumpContext; - -class GetByIdVariant { -public: - GetByIdVariant( - const StructureSet& structureSet = StructureSet(), PropertyOffset offset = invalidOffset, - const ObjectPropertyConditionSet& = ObjectPropertyConditionSet(), - std::unique_ptr<CallLinkStatus> callLinkStatus = nullptr); - - ~GetByIdVariant(); - - GetByIdVariant(const GetByIdVariant&); - GetByIdVariant& operator=(const GetByIdVariant&); - - bool isSet() const { return !!m_structureSet.size(); } - bool operator!() const { return !isSet(); } - const StructureSet& structureSet() const { return m_structureSet; } - StructureSet& structureSet() { return m_structureSet; } - - // A non-empty condition set means that this is a prototype load. - const ObjectPropertyConditionSet& conditionSet() const { return m_conditionSet; } - - PropertyOffset offset() const { return m_offset; } - CallLinkStatus* callLinkStatus() const { return m_callLinkStatus.get(); } - - bool attemptToMerge(const GetByIdVariant& other); - - void dump(PrintStream&) const; - void dumpInContext(PrintStream&, DumpContext*) const; - -private: - friend class GetByIdStatus; - - StructureSet m_structureSet; - ObjectPropertyConditionSet m_conditionSet; - PropertyOffset m_offset; - std::unique_ptr<CallLinkStatus> m_callLinkStatus; -}; - -} // namespace JSC - -#endif // GetByIdVariant_h - diff --git a/Source/JavaScriptCore/bytecode/HandlerInfo.h b/Source/JavaScriptCore/bytecode/HandlerInfo.h index acdda08ed..8396c9607 100644 --- a/Source/JavaScriptCore/bytecode/HandlerInfo.h +++ b/Source/JavaScriptCore/bytecode/HandlerInfo.h @@ -27,70 +27,16 @@ #define HandlerInfo_h #include "CodeLocation.h" +#include <wtf/Platform.h> namespace JSC { -enum class HandlerType { - Illegal = 0, - Catch = 1, - Finally = 2, - SynthesizedFinally = 3 -}; - -struct HandlerInfoBase { - HandlerType type() const { return static_cast<HandlerType>(typeBits); } - void setType(HandlerType type) { typeBits = static_cast<uint32_t>(type); } - - const char* typeName() - { - switch (type()) { - case HandlerType::Catch: - return "catch"; - case HandlerType::Finally: - return "finally"; - case HandlerType::SynthesizedFinally: - return "synthesized finally"; - default: - ASSERT_NOT_REACHED(); - } - return nullptr; - } - - bool isCatchHandler() const { return type() == HandlerType::Catch; } - +struct HandlerInfo { uint32_t start; uint32_t end; uint32_t target; - uint32_t typeBits : 2; // HandlerType -}; - -struct UnlinkedHandlerInfo : public HandlerInfoBase { - UnlinkedHandlerInfo(uint32_t start, uint32_t end, uint32_t target, HandlerType handlerType) - { - this->start = start; - this->end = end; - this->target = target; - setType(handlerType); - ASSERT(type() == handlerType); - } -}; - -struct HandlerInfo : public HandlerInfoBase { - void initialize(const UnlinkedHandlerInfo& unlinkedInfo) - { - start = unlinkedInfo.start; - end = unlinkedInfo.end; - target = unlinkedInfo.target; - typeBits = unlinkedInfo.typeBits; - } - + uint32_t scopeDepth; #if ENABLE(JIT) - void initialize(const UnlinkedHandlerInfo& unlinkedInfo, CodeLocationLabel label) - { - initialize(unlinkedInfo); - nativeCode = label; - } - CodeLocationLabel nativeCode; #endif }; diff --git a/Source/JavaScriptCore/bytecode/InlineCallFrameSet.cpp b/Source/JavaScriptCore/bytecode/InlineCallFrameSet.cpp index 82e0f7fd1..be5edb34c 100644 --- a/Source/JavaScriptCore/bytecode/InlineCallFrameSet.cpp +++ b/Source/JavaScriptCore/bytecode/InlineCallFrameSet.cpp @@ -25,7 +25,6 @@ #include "config.h" #include "InlineCallFrameSet.h" -#include "JSCInlines.h" namespace JSC { @@ -37,11 +36,5 @@ InlineCallFrame* InlineCallFrameSet::add() return m_frames.add(); } -void InlineCallFrameSet::visitAggregate(SlotVisitor& visitor) -{ - for (InlineCallFrame* callFrame : m_frames) - callFrame->visitAggregate(visitor); -} - } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/InlineCallFrameSet.h b/Source/JavaScriptCore/bytecode/InlineCallFrameSet.h index f9378e0a5..0a8b2e79c 100644 --- a/Source/JavaScriptCore/bytecode/InlineCallFrameSet.h +++ b/Source/JavaScriptCore/bytecode/InlineCallFrameSet.h @@ -28,11 +28,12 @@ #include "CodeOrigin.h" #include <wtf/Bag.h> -#include <wtf/RefCounted.h> +#include <wtf/Noncopyable.h> namespace JSC { -class InlineCallFrameSet : public RefCounted<InlineCallFrameSet> { +class InlineCallFrameSet { + WTF_MAKE_NONCOPYABLE(InlineCallFrameSet); public: InlineCallFrameSet(); ~InlineCallFrameSet(); @@ -44,8 +45,6 @@ public: typedef Bag<InlineCallFrame>::iterator iterator; iterator begin() { return m_frames.begin(); } iterator end() { return m_frames.end(); } - - void visitAggregate(SlotVisitor&); private: Bag<InlineCallFrame> m_frames; diff --git a/Source/JavaScriptCore/bytecode/Instruction.h b/Source/JavaScriptCore/bytecode/Instruction.h index c20a4f728..00bd8155b 100644 --- a/Source/JavaScriptCore/bytecode/Instruction.h +++ b/Source/JavaScriptCore/bytecode/Instruction.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2012-2015 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2012, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,7 @@ * 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 + * 3. Neither the name of Apple Computer, 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. * @@ -29,16 +29,12 @@ #ifndef Instruction_h #define Instruction_h -#include "BasicBlockLocation.h" #include "MacroAssembler.h" #include "Opcode.h" -#include "SymbolTable.h" -#include "TypeLocation.h" #include "PropertySlot.h" #include "SpecialPointer.h" #include "Structure.h" #include "StructureChain.h" -#include "ToThisStatus.h" #include "VirtualRegister.h" #include <wtf/VectorTraits.h> @@ -47,7 +43,7 @@ namespace JSC { class ArrayAllocationProfile; class ArrayProfile; class ObjectAllocationProfile; -class WatchpointSet; +class VariableWatchpointSet; struct LLIntCallLinkInfo; struct ValueProfile; @@ -98,33 +94,30 @@ struct Instruction { Instruction(ArrayProfile* profile) { u.arrayProfile = profile; } Instruction(ArrayAllocationProfile* profile) { u.arrayAllocationProfile = profile; } Instruction(ObjectAllocationProfile* profile) { u.objectAllocationProfile = profile; } - Instruction(WriteBarrier<Unknown>* variablePointer) { u.variablePointer = variablePointer; } + Instruction(WriteBarrier<Unknown>* registerPointer) { u.registerPointer = registerPointer; } Instruction(Special::Pointer pointer) { u.specialPointer = pointer; } - Instruction(UniquedStringImpl* uid) { u.uid = uid; } + Instruction(StringImpl* uid) { u.uid = uid; } Instruction(bool* predicatePointer) { u.predicatePointer = predicatePointer; } union { Opcode opcode; int operand; WriteBarrierBase<Structure> structure; - WriteBarrierBase<SymbolTable> symbolTable; WriteBarrierBase<StructureChain> structureChain; WriteBarrierBase<JSCell> jsCell; - WriteBarrier<Unknown>* variablePointer; + WriteBarrier<Unknown>* registerPointer; Special::Pointer specialPointer; PropertySlot::GetValueFunc getterFunc; LLIntCallLinkInfo* callLinkInfo; - UniquedStringImpl* uid; + StringImpl* uid; ValueProfile* profile; ArrayProfile* arrayProfile; ArrayAllocationProfile* arrayAllocationProfile; ObjectAllocationProfile* objectAllocationProfile; - WatchpointSet* watchpointSet; + VariableWatchpointSet* watchpointSet; + WriteBarrierBase<JSActivation> activation; void* pointer; bool* predicatePointer; - ToThisStatus toThisStatus; - TypeLocation* location; - BasicBlockLocation* basicBlockLocation; } u; private: diff --git a/Source/JavaScriptCore/bytecode/JumpTable.cpp b/Source/JavaScriptCore/bytecode/JumpTable.cpp index e22ad03c9..ef7098b65 100644 --- a/Source/JavaScriptCore/bytecode/JumpTable.cpp +++ b/Source/JavaScriptCore/bytecode/JumpTable.cpp @@ -11,7 +11,7 @@ * 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 + * 3. Neither the name of Apple Computer, 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. * diff --git a/Source/JavaScriptCore/bytecode/JumpTable.h b/Source/JavaScriptCore/bytecode/JumpTable.h index b83e842cb..55d6855a5 100644 --- a/Source/JavaScriptCore/bytecode/JumpTable.h +++ b/Source/JavaScriptCore/bytecode/JumpTable.h @@ -11,7 +11,7 @@ * 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 + * 3. Neither the name of Apple Computer, 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. * @@ -94,12 +94,6 @@ namespace JSC { } #if ENABLE(JIT) - void ensureCTITable() - { - ASSERT(ctiOffsets.isEmpty() || ctiOffsets.size() == branchOffsets.size()); - ctiOffsets.grow(branchOffsets.size()); - } - inline CodeLocationLabel ctiForValue(int32_t value) { if (value >= min && static_cast<uint32_t>(value - min) < ctiOffsets.size()) diff --git a/Source/JavaScriptCore/bytecode/LLIntCallLinkInfo.h b/Source/JavaScriptCore/bytecode/LLIntCallLinkInfo.h index 2645dd5be..bfb951018 100644 --- a/Source/JavaScriptCore/bytecode/LLIntCallLinkInfo.h +++ b/Source/JavaScriptCore/bytecode/LLIntCallLinkInfo.h @@ -45,7 +45,7 @@ struct LLIntCallLinkInfo : public BasicRawSentinelNode<LLIntCallLinkInfo> { remove(); } - bool isLinked() { return !!callee; } + bool isLinked() { return callee; } void unlink() { diff --git a/Source/JavaScriptCore/bytecode/LazyOperandValueProfile.cpp b/Source/JavaScriptCore/bytecode/LazyOperandValueProfile.cpp index de654db68..a8ad779ac 100644 --- a/Source/JavaScriptCore/bytecode/LazyOperandValueProfile.cpp +++ b/Source/JavaScriptCore/bytecode/LazyOperandValueProfile.cpp @@ -26,7 +26,7 @@ #include "config.h" #include "LazyOperandValueProfile.h" -#include "JSCInlines.h" +#include "Operations.h" namespace JSC { @@ -46,7 +46,7 @@ LazyOperandValueProfile* CompressedLazyOperandValueProfileHolder::add( const ConcurrentJITLocker&, const LazyOperandValueProfileKey& key) { if (!m_data) - m_data = std::make_unique<LazyOperandValueProfile::List>(); + m_data = adoptPtr(new LazyOperandValueProfile::List()); else { for (unsigned i = 0; i < m_data->size(); ++i) { if (m_data->at(i).key() == key) diff --git a/Source/JavaScriptCore/bytecode/LazyOperandValueProfile.h b/Source/JavaScriptCore/bytecode/LazyOperandValueProfile.h index 74e4f3318..95ef941cd 100644 --- a/Source/JavaScriptCore/bytecode/LazyOperandValueProfile.h +++ b/Source/JavaScriptCore/bytecode/LazyOperandValueProfile.h @@ -31,6 +31,7 @@ #include "VirtualRegister.h" #include <wtf/HashMap.h> #include <wtf/Noncopyable.h> +#include <wtf/OwnPtr.h> #include <wtf/SegmentedVector.h> namespace JSC { @@ -160,7 +161,7 @@ public: private: friend class LazyOperandValueProfileParser; - std::unique_ptr<LazyOperandValueProfile::List> m_data; + OwnPtr<LazyOperandValueProfile::List> m_data; }; class LazyOperandValueProfileParser { diff --git a/Source/JavaScriptCore/bytecode/MethodOfGettingAValueProfile.cpp b/Source/JavaScriptCore/bytecode/MethodOfGettingAValueProfile.cpp index bec692ef7..1ac5bb5a0 100644 --- a/Source/JavaScriptCore/bytecode/MethodOfGettingAValueProfile.cpp +++ b/Source/JavaScriptCore/bytecode/MethodOfGettingAValueProfile.cpp @@ -29,7 +29,6 @@ #if ENABLE(DFG_JIT) #include "CodeBlock.h" -#include "JSCInlines.h" namespace JSC { diff --git a/Source/JavaScriptCore/bytecode/MethodOfGettingAValueProfile.h b/Source/JavaScriptCore/bytecode/MethodOfGettingAValueProfile.h index 846f8cf7a..c6fe6c5f0 100644 --- a/Source/JavaScriptCore/bytecode/MethodOfGettingAValueProfile.h +++ b/Source/JavaScriptCore/bytecode/MethodOfGettingAValueProfile.h @@ -26,6 +26,8 @@ #ifndef MethodOfGettingAValueProfile_h #define MethodOfGettingAValueProfile_h +#include <wtf/Platform.h> + // This is guarded by ENABLE_DFG_JIT only because it uses some value profiles // that are currently only used if the DFG is enabled (i.e. they are not // available in the profile-only configuration). Hopefully someday all of diff --git a/Source/JavaScriptCore/bytecode/ObjectAllocationProfile.h b/Source/JavaScriptCore/bytecode/ObjectAllocationProfile.h index 5fa706d25..9a9db0bc7 100644 --- a/Source/JavaScriptCore/bytecode/ObjectAllocationProfile.h +++ b/Source/JavaScriptCore/bytecode/ObjectAllocationProfile.h @@ -89,23 +89,13 @@ public: if (inlineCapacity > JSFinalObject::maxInlineCapacity()) inlineCapacity = JSFinalObject::maxInlineCapacity(); - Structure* structure = vm.prototypeMap.emptyObjectStructureForPrototype(prototype, inlineCapacity); - - // Ensure that if another thread sees the structure, it will see it properly created - WTF::storeStoreFence(); - m_allocator = allocator; - m_structure.set(vm, owner, structure); + m_structure.set(vm, owner, + vm.prototypeMap.emptyObjectStructureForPrototype(prototype, inlineCapacity)); } - Structure* structure() - { - Structure* structure = m_structure.get(); - // Ensure that if we see the structure, it has been properly created - WTF::loadLoadFence(); - return structure; - } - unsigned inlineCapacity() { return structure()->inlineCapacity(); } + Structure* structure() { return m_structure.get(); } + unsigned inlineCapacity() { return m_structure->inlineCapacity(); } void clear() { @@ -127,8 +117,8 @@ private: return 0; size_t count = 0; - PropertyNameArray propertyNameArray(&vm, PropertyNameMode::StringsAndSymbols); - prototype->structure()->getPropertyNamesFromStructure(vm, propertyNameArray, EnumerationMode()); + PropertyNameArray propertyNameArray(&vm); + prototype->structure()->getPropertyNamesFromStructure(vm, propertyNameArray, ExcludeDontEnumProperties); PropertyNameArrayData::PropertyNameVector& propertyNameVector = propertyNameArray.data()->propertyNameVector(); for (size_t i = 0; i < propertyNameVector.size(); ++i) { JSValue value = prototype->getDirect(vm, propertyNameVector[i]); diff --git a/Source/JavaScriptCore/bytecode/ObjectPropertyCondition.cpp b/Source/JavaScriptCore/bytecode/ObjectPropertyCondition.cpp deleted file mode 100644 index 1f153b956..000000000 --- a/Source/JavaScriptCore/bytecode/ObjectPropertyCondition.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -#include "config.h" -#include "ObjectPropertyCondition.h" - -#include "JSCInlines.h" -#include "TrackedReferences.h" - -namespace JSC { - -void ObjectPropertyCondition::dumpInContext(PrintStream& out, DumpContext* context) const -{ - if (!*this) { - out.print("<invalid>"); - return; - } - - out.print("<", inContext(JSValue(m_object), context), ": ", inContext(m_condition, context), ">"); -} - -void ObjectPropertyCondition::dump(PrintStream& out) const -{ - dumpInContext(out, nullptr); -} - -bool ObjectPropertyCondition::structureEnsuresValidityAssumingImpurePropertyWatchpoint( - Structure* structure) const -{ - return m_condition.isStillValidAssumingImpurePropertyWatchpoint(structure); -} - -bool ObjectPropertyCondition::structureEnsuresValidityAssumingImpurePropertyWatchpoint() const -{ - if (!*this) - return false; - - return structureEnsuresValidityAssumingImpurePropertyWatchpoint(m_object->structure()); -} - -bool ObjectPropertyCondition::validityRequiresImpurePropertyWatchpoint(Structure* structure) const -{ - return m_condition.validityRequiresImpurePropertyWatchpoint(structure); -} - -bool ObjectPropertyCondition::validityRequiresImpurePropertyWatchpoint() const -{ - if (!*this) - return false; - - return validityRequiresImpurePropertyWatchpoint(m_object->structure()); -} - -bool ObjectPropertyCondition::isStillValid(Structure* structure) const -{ - return m_condition.isStillValid(structure, m_object); -} - -bool ObjectPropertyCondition::isStillValid() const -{ - if (!*this) - return false; - - return isStillValid(m_object->structure()); -} - -bool ObjectPropertyCondition::structureEnsuresValidity(Structure* structure) const -{ - return m_condition.isStillValid(structure); -} - -bool ObjectPropertyCondition::structureEnsuresValidity() const -{ - if (!*this) - return false; - - return structureEnsuresValidity(m_object->structure()); -} - -bool ObjectPropertyCondition::isWatchableAssumingImpurePropertyWatchpoint( - Structure* structure, PropertyCondition::WatchabilityEffort effort) const -{ - return m_condition.isWatchableAssumingImpurePropertyWatchpoint(structure, m_object, effort); -} - -bool ObjectPropertyCondition::isWatchableAssumingImpurePropertyWatchpoint( - PropertyCondition::WatchabilityEffort effort) const -{ - if (!*this) - return false; - - return isWatchableAssumingImpurePropertyWatchpoint(m_object->structure(), effort); -} - -bool ObjectPropertyCondition::isWatchable( - Structure* structure, PropertyCondition::WatchabilityEffort effort) const -{ - return m_condition.isWatchable(structure, m_object, effort); -} - -bool ObjectPropertyCondition::isWatchable(PropertyCondition::WatchabilityEffort effort) const -{ - if (!*this) - return false; - - return isWatchable(m_object->structure(), effort); -} - -bool ObjectPropertyCondition::isStillLive() const -{ - if (!*this) - return false; - - if (!Heap::isMarked(m_object)) - return false; - - return m_condition.isStillLive(); -} - -void ObjectPropertyCondition::validateReferences(const TrackedReferences& tracked) const -{ - if (!*this) - return; - - tracked.check(m_object); - m_condition.validateReferences(tracked); -} - -ObjectPropertyCondition ObjectPropertyCondition::attemptToMakeEquivalenceWithoutBarrier() const -{ - PropertyCondition result = condition().attemptToMakeEquivalenceWithoutBarrier(object()); - if (!result) - return ObjectPropertyCondition(); - return ObjectPropertyCondition(object(), result); -} - -} // namespace JSC - diff --git a/Source/JavaScriptCore/bytecode/ObjectPropertyCondition.h b/Source/JavaScriptCore/bytecode/ObjectPropertyCondition.h deleted file mode 100644 index 372e68aea..000000000 --- a/Source/JavaScriptCore/bytecode/ObjectPropertyCondition.h +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (C) 2015 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 ObjectPropertyCondition_h -#define ObjectPropertyCondition_h - -#include "JSObject.h" -#include "PropertyCondition.h" -#include <wtf/HashMap.h> - -namespace JSC { - -class TrackedReferences; - -class ObjectPropertyCondition { -public: - ObjectPropertyCondition() - : m_object(nullptr) - { - } - - ObjectPropertyCondition(WTF::HashTableDeletedValueType token) - : m_object(nullptr) - , m_condition(token) - { - } - - ObjectPropertyCondition(JSObject* object, const PropertyCondition& condition) - : m_object(object) - , m_condition(condition) - { - } - - static ObjectPropertyCondition presenceWithoutBarrier( - JSObject* object, UniquedStringImpl* uid, PropertyOffset offset, unsigned attributes) - { - ObjectPropertyCondition result; - result.m_object = object; - result.m_condition = PropertyCondition::presenceWithoutBarrier(uid, offset, attributes); - return result; - } - - static ObjectPropertyCondition presence( - VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, PropertyOffset offset, - unsigned attributes) - { - if (owner) - vm.heap.writeBarrier(owner); - return presenceWithoutBarrier(object, uid, offset, attributes); - } - - // NOTE: The prototype is the storedPrototype, not the prototypeForLookup. - static ObjectPropertyCondition absenceWithoutBarrier( - JSObject* object, UniquedStringImpl* uid, JSObject* prototype) - { - ObjectPropertyCondition result; - result.m_object = object; - result.m_condition = PropertyCondition::absenceWithoutBarrier(uid, prototype); - return result; - } - - static ObjectPropertyCondition absence( - VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, JSObject* prototype) - { - if (owner) - vm.heap.writeBarrier(owner); - return absenceWithoutBarrier(object, uid, prototype); - } - - static ObjectPropertyCondition absenceOfSetterWithoutBarrier( - JSObject* object, UniquedStringImpl* uid, JSObject* prototype) - { - ObjectPropertyCondition result; - result.m_object = object; - result.m_condition = PropertyCondition::absenceOfSetterWithoutBarrier(uid, prototype); - return result; - } - - static ObjectPropertyCondition absenceOfSetter( - VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, JSObject* prototype) - { - if (owner) - vm.heap.writeBarrier(owner); - return absenceOfSetterWithoutBarrier(object, uid, prototype); - } - - static ObjectPropertyCondition equivalenceWithoutBarrier( - JSObject* object, UniquedStringImpl* uid, JSValue value) - { - ObjectPropertyCondition result; - result.m_object = object; - result.m_condition = PropertyCondition::equivalenceWithoutBarrier(uid, value); - return result; - } - - static ObjectPropertyCondition equivalence( - VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, JSValue value) - { - if (owner) - vm.heap.writeBarrier(owner); - return equivalenceWithoutBarrier(object, uid, value); - } - - bool operator!() const { return !m_condition; }; - - JSObject* object() const { return m_object; } - PropertyCondition condition() const { return m_condition; } - - PropertyCondition::Kind kind() const { return condition().kind(); } - UniquedStringImpl* uid() const { return condition().uid(); } - bool hasOffset() const { return condition().hasOffset(); } - PropertyOffset offset() const { return condition().offset(); } - unsigned hasAttributes() const { return condition().hasAttributes(); } - unsigned attributes() const { return condition().attributes(); } - bool hasPrototype() const { return condition().hasPrototype(); } - JSObject* prototype() const { return condition().prototype(); } - bool hasRequiredValue() const { return condition().hasRequiredValue(); } - JSValue requiredValue() const { return condition().requiredValue(); } - - void dumpInContext(PrintStream&, DumpContext*) const; - void dump(PrintStream&) const; - - unsigned hash() const - { - return WTF::PtrHash<JSObject*>::hash(m_object) ^ m_condition.hash(); - } - - bool operator==(const ObjectPropertyCondition& other) const - { - return m_object == other.m_object - && m_condition == other.m_condition; - } - - bool isHashTableDeletedValue() const - { - return !m_object && m_condition.isHashTableDeletedValue(); - } - - // Two conditions are compatible if they are identical or if they speak of different uids or - // different objects. If false is returned, you have to decide how to resolve the conflict - - // for example if there is a Presence and an Equivalence then in some cases you'll want the - // more general of the two while in other cases you'll want the more specific of the two. This - // will also return false for contradictions, like Presence and Absence on the same - // object/uid. By convention, invalid conditions aren't compatible with anything. - bool isCompatibleWith(const ObjectPropertyCondition& other) const - { - if (!*this || !other) - return false; - return *this == other || uid() != other.uid() || object() != other.object(); - } - - // These validity-checking methods can optionally take a Struture* instead of loading the - // Structure* from the object. If you're in the concurrent JIT, then you must use the forms - // that take an explicit Structure* because you want the compiler to optimize for the same - // structure that you validated (i.e. avoid a TOCTOU race). - - // Checks if the object's structure claims that the property won't be intercepted. Validity - // does not require watchpoints on the object. - bool structureEnsuresValidityAssumingImpurePropertyWatchpoint(Structure*) const; - bool structureEnsuresValidityAssumingImpurePropertyWatchpoint() const; - - // Returns true if we need an impure property watchpoint to ensure validity even if - // isStillValidAccordingToStructure() returned true. - bool validityRequiresImpurePropertyWatchpoint(Structure*) const; - bool validityRequiresImpurePropertyWatchpoint() const; - - // Checks if the condition still holds. May conservatively return false, if the object and - // structure alone don't guarantee the condition. Note that this may return true if the - // condition still requires some watchpoints on the object in addition to checking the - // structure. If you want to check if the condition holds by using the structure alone, - // use structureEnsuresValidity(). - bool isStillValid(Structure*) const; - bool isStillValid() const; - - // Shorthand for condition().isStillValid(structure). - bool structureEnsuresValidity(Structure*) const; - bool structureEnsuresValidity() const; - - // This means that it's still valid and we could enforce validity by setting a transition - // watchpoint on the structure and possibly an impure property watchpoint. - bool isWatchableAssumingImpurePropertyWatchpoint( - Structure*, - PropertyCondition::WatchabilityEffort = PropertyCondition::MakeNoChanges) const; - bool isWatchableAssumingImpurePropertyWatchpoint( - PropertyCondition::WatchabilityEffort = PropertyCondition::MakeNoChanges) const; - - // This means that it's still valid and we could enforce validity by setting a transition - // watchpoint on the structure. - bool isWatchable( - Structure*, - PropertyCondition::WatchabilityEffort = PropertyCondition::MakeNoChanges) const; - bool isWatchable( - PropertyCondition::WatchabilityEffort = PropertyCondition::MakeNoChanges) const; - - bool watchingRequiresStructureTransitionWatchpoint() const - { - return condition().watchingRequiresStructureTransitionWatchpoint(); - } - bool watchingRequiresReplacementWatchpoint() const - { - return condition().watchingRequiresReplacementWatchpoint(); - } - - // This means that the objects involved in this are still live. - bool isStillLive() const; - - void validateReferences(const TrackedReferences&) const; - - bool isValidValueForPresence(JSValue value) const - { - return condition().isValidValueForPresence(value); - } - - ObjectPropertyCondition attemptToMakeEquivalenceWithoutBarrier() const; - -private: - JSObject* m_object; - PropertyCondition m_condition; -}; - -struct ObjectPropertyConditionHash { - static unsigned hash(const ObjectPropertyCondition& key) { return key.hash(); } - static bool equal( - const ObjectPropertyCondition& a, const ObjectPropertyCondition& b) - { - return a == b; - } - static const bool safeToCompareToEmptyOrDeleted = true; -}; - -} // namespace JSC - -namespace WTF { - -template<typename T> struct DefaultHash; -template<> struct DefaultHash<JSC::ObjectPropertyCondition> { - typedef JSC::ObjectPropertyConditionHash Hash; -}; - -template<typename T> struct HashTraits; -template<> struct HashTraits<JSC::ObjectPropertyCondition> : SimpleClassHashTraits<JSC::ObjectPropertyCondition> { }; - -} // namespace WTF - -#endif // ObjectPropertyCondition_h - diff --git a/Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.cpp b/Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.cpp deleted file mode 100644 index 81860651f..000000000 --- a/Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.cpp +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -#include "config.h" -#include "ObjectPropertyConditionSet.h" - -#include "JSCInlines.h" -#include <wtf/ListDump.h> - -namespace JSC { - -ObjectPropertyCondition ObjectPropertyConditionSet::forObject(JSObject* object) const -{ - for (const ObjectPropertyCondition& condition : *this) { - if (condition.object() == object) - return condition; - } - return ObjectPropertyCondition(); -} - -ObjectPropertyCondition ObjectPropertyConditionSet::forConditionKind( - PropertyCondition::Kind kind) const -{ - for (const ObjectPropertyCondition& condition : *this) { - if (condition.kind() == kind) - return condition; - } - return ObjectPropertyCondition(); -} - -unsigned ObjectPropertyConditionSet::numberOfConditionsWithKind(PropertyCondition::Kind kind) const -{ - unsigned result = 0; - for (const ObjectPropertyCondition& condition : *this) { - if (condition.kind() == kind) - result++; - } - return result; -} - -bool ObjectPropertyConditionSet::hasOneSlotBaseCondition() const -{ - return numberOfConditionsWithKind(PropertyCondition::Presence) == 1; -} - -ObjectPropertyCondition ObjectPropertyConditionSet::slotBaseCondition() const -{ - ObjectPropertyCondition result; - unsigned numFound = 0; - for (const ObjectPropertyCondition& condition : *this) { - if (condition.kind() == PropertyCondition::Presence) { - result = condition; - numFound++; - } - } - RELEASE_ASSERT(numFound == 1); - return result; -} - -ObjectPropertyConditionSet ObjectPropertyConditionSet::mergedWith( - const ObjectPropertyConditionSet& other) const -{ - if (!isValid() || !other.isValid()) - return invalid(); - - Vector<ObjectPropertyCondition> result; - - if (!isEmpty()) - result.appendVector(m_data->vector); - - for (const ObjectPropertyCondition& newCondition : other) { - for (const ObjectPropertyCondition& existingCondition : *this) { - if (newCondition == existingCondition) - continue; - if (!newCondition.isCompatibleWith(existingCondition)) - return invalid(); - result.append(newCondition); - } - } - - return create(result); -} - -bool ObjectPropertyConditionSet::structuresEnsureValidity() const -{ - if (!isValid()) - return false; - - for (const ObjectPropertyCondition& condition : *this) { - if (!condition.structureEnsuresValidity()) - return false; - } - return true; -} - -bool ObjectPropertyConditionSet::structuresEnsureValidityAssumingImpurePropertyWatchpoint() const -{ - if (!isValid()) - return false; - - for (const ObjectPropertyCondition& condition : *this) { - if (!condition.structureEnsuresValidityAssumingImpurePropertyWatchpoint()) - return false; - } - return true; -} - -bool ObjectPropertyConditionSet::needImpurePropertyWatchpoint() const -{ - for (const ObjectPropertyCondition& condition : *this) { - if (condition.validityRequiresImpurePropertyWatchpoint()) - return true; - } - return false; -} - -bool ObjectPropertyConditionSet::areStillLive() const -{ - for (const ObjectPropertyCondition& condition : *this) { - if (!condition.isStillLive()) - return false; - } - return true; -} - -void ObjectPropertyConditionSet::dumpInContext(PrintStream& out, DumpContext* context) const -{ - if (!isValid()) { - out.print("<invalid>"); - return; - } - - out.print("["); - if (m_data) - out.print(listDumpInContext(m_data->vector, context)); - out.print("]"); -} - -void ObjectPropertyConditionSet::dump(PrintStream& out) const -{ - dumpInContext(out, nullptr); -} - -namespace { - -bool verbose = false; - -ObjectPropertyCondition generateCondition( - VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, PropertyCondition::Kind conditionKind) -{ - Structure* structure = object->structure(); - if (verbose) - dataLog("Creating condition ", conditionKind, " for ", pointerDump(structure), "\n"); - - ObjectPropertyCondition result; - switch (conditionKind) { - case PropertyCondition::Presence: { - unsigned attributes; - PropertyOffset offset = structure->getConcurrently(uid, attributes); - if (offset == invalidOffset) - return ObjectPropertyCondition(); - result = ObjectPropertyCondition::presence(vm, owner, object, uid, offset, attributes); - break; - } - case PropertyCondition::Absence: { - result = ObjectPropertyCondition::absence( - vm, owner, object, uid, object->structure()->storedPrototypeObject()); - break; - } - case PropertyCondition::AbsenceOfSetter: { - result = ObjectPropertyCondition::absenceOfSetter( - vm, owner, object, uid, object->structure()->storedPrototypeObject()); - break; - } - default: - RELEASE_ASSERT_NOT_REACHED(); - return ObjectPropertyCondition(); - } - - if (!result.structureEnsuresValidityAssumingImpurePropertyWatchpoint()) { - if (verbose) - dataLog("Failed to create condition: ", result, "\n"); - return ObjectPropertyCondition(); - } - - if (verbose) - dataLog("New condition: ", result, "\n"); - return result; -} - -enum Concurrency { - MainThread, - Concurrent -}; -template<typename Functor> -ObjectPropertyConditionSet generateConditions( - VM& vm, JSGlobalObject* globalObject, Structure* structure, JSObject* prototype, const Functor& functor, - Concurrency concurrency = MainThread) -{ - Vector<ObjectPropertyCondition> conditions; - - for (;;) { - if (verbose) - dataLog("Considering structure: ", pointerDump(structure), "\n"); - - if (structure->isProxy()) { - if (verbose) - dataLog("It's a proxy, so invalid.\n"); - return ObjectPropertyConditionSet::invalid(); - } - - JSValue value = structure->prototypeForLookup(globalObject); - - if (value.isNull()) { - if (!prototype) { - if (verbose) - dataLog("Reached end up prototype chain as expected, done.\n"); - break; - } - if (verbose) - dataLog("Unexpectedly reached end of prototype chain, so invalid.\n"); - return ObjectPropertyConditionSet::invalid(); - } - - JSObject* object = jsCast<JSObject*>(value); - structure = object->structure(vm); - - // Since we're accessing a prototype repeatedly, it's a good bet that it should not be - // treated as a dictionary. - if (structure->isDictionary()) { - if (concurrency == MainThread) - structure->flattenDictionaryStructure(vm, object); - else { - if (verbose) - dataLog("Cannot flatten dictionary when not on main thread, so invalid.\n"); - return ObjectPropertyConditionSet::invalid(); - } - } - - if (!functor(conditions, object)) { - if (verbose) - dataLog("Functor failed, invalid.\n"); - return ObjectPropertyConditionSet::invalid(); - } - - if (object == prototype) { - if (verbose) - dataLog("Reached desired prototype, done.\n"); - break; - } - } - - if (verbose) - dataLog("Returning conditions: ", listDump(conditions), "\n"); - return ObjectPropertyConditionSet::create(conditions); -} - -} // anonymous namespace - -ObjectPropertyConditionSet generateConditionsForPropertyMiss( - VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, UniquedStringImpl* uid) -{ - return generateConditions( - vm, exec->lexicalGlobalObject(), headStructure, nullptr, - [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool { - ObjectPropertyCondition result = - generateCondition(vm, owner, object, uid, PropertyCondition::Absence); - if (!result) - return false; - conditions.append(result); - return true; - }); -} - -ObjectPropertyConditionSet generateConditionsForPropertySetterMiss( - VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, UniquedStringImpl* uid) -{ - return generateConditions( - vm, exec->lexicalGlobalObject(), headStructure, nullptr, - [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool { - ObjectPropertyCondition result = - generateCondition(vm, owner, object, uid, PropertyCondition::AbsenceOfSetter); - if (!result) - return false; - conditions.append(result); - return true; - }); -} - -ObjectPropertyConditionSet generateConditionsForPrototypePropertyHit( - VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, JSObject* prototype, - UniquedStringImpl* uid) -{ - return generateConditions( - vm, exec->lexicalGlobalObject(), headStructure, prototype, - [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool { - PropertyCondition::Kind kind = - object == prototype ? PropertyCondition::Presence : PropertyCondition::Absence; - ObjectPropertyCondition result = - generateCondition(vm, owner, object, uid, kind); - if (!result) - return false; - conditions.append(result); - return true; - }); -} - -ObjectPropertyConditionSet generateConditionsForPrototypePropertyHitCustom( - VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, JSObject* prototype, - UniquedStringImpl* uid) -{ - return generateConditions( - vm, exec->lexicalGlobalObject(), headStructure, prototype, - [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool { - if (object == prototype) - return true; - ObjectPropertyCondition result = - generateCondition(vm, owner, object, uid, PropertyCondition::Absence); - if (!result) - return false; - conditions.append(result); - return true; - }); -} - -ObjectPropertyConditionSet generateConditionsForPropertySetterMissConcurrently( - VM& vm, JSGlobalObject* globalObject, Structure* headStructure, UniquedStringImpl* uid) -{ - return generateConditions( - vm, globalObject, headStructure, nullptr, - [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool { - ObjectPropertyCondition result = - generateCondition(vm, nullptr, object, uid, PropertyCondition::AbsenceOfSetter); - if (!result) - return false; - conditions.append(result); - return true; - }, Concurrent); -} - -} // namespace JSC - diff --git a/Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.h b/Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.h deleted file mode 100644 index 957eaac25..000000000 --- a/Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.h +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2015 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 ObjectPropertyConditionSet_h -#define ObjectPropertyConditionSet_h - -#include "ObjectPropertyCondition.h" -#include <wtf/FastMalloc.h> -#include <wtf/RefCounted.h> -#include <wtf/Vector.h> - -namespace JSC { - -// An object property condition set is used to represent the set of additional conditions -// that need to be met for some heap access to be valid. The set can have the following -// interesting states: -// -// Empty: There are no special conditions that need to be met. -// Invalid: The heap access is never valid. -// Non-empty: The heap access is valid if all the ObjectPropertyConditions in the set are valid. - -class ObjectPropertyConditionSet { -public: - ObjectPropertyConditionSet() { } - - static ObjectPropertyConditionSet invalid() - { - ObjectPropertyConditionSet result; - result.m_data = adoptRef(new Data()); - return result; - } - - static ObjectPropertyConditionSet create(const Vector<ObjectPropertyCondition>& vector) - { - if (vector.isEmpty()) - return ObjectPropertyConditionSet(); - - ObjectPropertyConditionSet result; - result.m_data = adoptRef(new Data()); - result.m_data->vector = vector; - return result; - } - - bool isValid() const - { - return !m_data || !m_data->vector.isEmpty(); - } - - bool isEmpty() const - { - return !m_data; - } - - typedef const ObjectPropertyCondition* iterator; - - iterator begin() const - { - if (!m_data) - return nullptr; - return m_data->vector.begin(); - } - iterator end() const - { - if (!m_data) - return nullptr; - return m_data->vector.end(); - } - - ObjectPropertyCondition forObject(JSObject*) const; - ObjectPropertyCondition forConditionKind(PropertyCondition::Kind) const; - - unsigned numberOfConditionsWithKind(PropertyCondition::Kind) const; - - bool hasOneSlotBaseCondition() const; - - // If this is a condition set for a prototype hit, then this is guaranteed to return the - // condition on the prototype itself. This allows you to get the object, offset, and - // attributes for the prototype. This will RELEASE_ASSERT that there is exactly one Presence - // in the set, and it will return that presence. - ObjectPropertyCondition slotBaseCondition() const; - - // Attempt to create a new condition set by merging this one with the other one. This will - // fail if any of the conditions are incompatible with each other. When if fails, it returns - // invalid(). - ObjectPropertyConditionSet mergedWith(const ObjectPropertyConditionSet& other) const; - - bool structuresEnsureValidity() const; - bool structuresEnsureValidityAssumingImpurePropertyWatchpoint() const; - - bool needImpurePropertyWatchpoint() const; - bool areStillLive() const; - - void dumpInContext(PrintStream&, DumpContext*) const; - void dump(PrintStream&) const; - - // Helpers for using this in a union. - void* releaseRawPointer() - { - return static_cast<void*>(m_data.leakRef()); - } - static ObjectPropertyConditionSet adoptRawPointer(void* rawPointer) - { - ObjectPropertyConditionSet result; - result.m_data = adoptRef(static_cast<Data*>(rawPointer)); - return result; - } - static ObjectPropertyConditionSet fromRawPointer(void* rawPointer) - { - ObjectPropertyConditionSet result; - result.m_data = static_cast<Data*>(rawPointer); - return result; - } - - // FIXME: Everything below here should be private, but cannot be because of a bug in VS. - - // Internally, this represents Invalid using a pointer to a Data that has an empty vector. - - // FIXME: This could be made more compact by having it internally use a vector that just has - // the non-uid portion of ObjectPropertyCondition, and then requiring that the callers of all - // of the APIs supply the uid. - - class Data : public ThreadSafeRefCounted<Data> { - WTF_MAKE_NONCOPYABLE(Data); - WTF_MAKE_FAST_ALLOCATED; - - public: - Data() { } - - Vector<ObjectPropertyCondition> vector; - }; - -private: - RefPtr<Data> m_data; -}; - -ObjectPropertyConditionSet generateConditionsForPropertyMiss( - VM&, JSCell* owner, ExecState*, Structure* headStructure, UniquedStringImpl* uid); -ObjectPropertyConditionSet generateConditionsForPropertySetterMiss( - VM&, JSCell* owner, ExecState*, Structure* headStructure, UniquedStringImpl* uid); -ObjectPropertyConditionSet generateConditionsForPrototypePropertyHit( - VM&, JSCell* owner, ExecState*, Structure* headStructure, JSObject* prototype, - UniquedStringImpl* uid); -ObjectPropertyConditionSet generateConditionsForPrototypePropertyHitCustom( - VM&, JSCell* owner, ExecState*, Structure* headStructure, JSObject* prototype, - UniquedStringImpl* uid); - -ObjectPropertyConditionSet generateConditionsForPropertySetterMissConcurrently( - VM&, JSGlobalObject*, Structure* headStructure, UniquedStringImpl* uid); - -} // namespace JSC - -#endif // ObjectPropertyConditionSet_h - diff --git a/Source/JavaScriptCore/bytecode/Opcode.cpp b/Source/JavaScriptCore/bytecode/Opcode.cpp index 3efa34934..26f53511a 100644 --- a/Source/JavaScriptCore/bytecode/Opcode.cpp +++ b/Source/JavaScriptCore/bytecode/Opcode.cpp @@ -11,7 +11,7 @@ * 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 + * 3. Neither the name of Apple Computer, 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. * diff --git a/Source/JavaScriptCore/bytecode/Opcode.h b/Source/JavaScriptCore/bytecode/Opcode.h index 1ed48c2ce..e8636e785 100644 --- a/Source/JavaScriptCore/bytecode/Opcode.h +++ b/Source/JavaScriptCore/bytecode/Opcode.h @@ -11,7 +11,7 @@ * 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 + * 3. Neither the name of Apple Computer, 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. * @@ -30,7 +30,6 @@ #ifndef Opcode_h #define Opcode_h -#include "Bytecodes.h" #include "LLIntOpcode.h" #include <algorithm> @@ -41,8 +40,158 @@ namespace JSC { #define FOR_EACH_CORE_OPCODE_ID_WITH_EXTENSION(macro, extension__) \ - FOR_EACH_BYTECODE_ID(macro) \ - extension__ + macro(op_enter, 1) \ + macro(op_create_activation, 2) \ + macro(op_touch_entry, 1) \ + macro(op_init_lazy_reg, 2) \ + macro(op_create_arguments, 2) \ + macro(op_create_this, 4) \ + macro(op_get_callee, 3) \ + macro(op_to_this, 3) \ + \ + macro(op_new_object, 4) \ + macro(op_new_array, 5) \ + macro(op_new_array_with_size, 4) \ + macro(op_new_array_buffer, 5) \ + macro(op_new_regexp, 3) \ + macro(op_mov, 3) \ + macro(op_captured_mov, 4) \ + \ + macro(op_not, 3) \ + macro(op_eq, 4) \ + macro(op_eq_null, 3) \ + macro(op_neq, 4) \ + macro(op_neq_null, 3) \ + macro(op_stricteq, 4) \ + macro(op_nstricteq, 4) \ + macro(op_less, 4) \ + macro(op_lesseq, 4) \ + macro(op_greater, 4) \ + macro(op_greatereq, 4) \ + \ + macro(op_inc, 2) \ + macro(op_dec, 2) \ + macro(op_to_number, 3) \ + macro(op_negate, 3) \ + macro(op_add, 5) \ + macro(op_mul, 5) \ + macro(op_div, 5) \ + macro(op_mod, 4) \ + macro(op_sub, 5) \ + \ + macro(op_lshift, 4) \ + macro(op_rshift, 4) \ + macro(op_urshift, 4) \ + macro(op_unsigned, 3) \ + macro(op_bitand, 5) \ + macro(op_bitxor, 5) \ + macro(op_bitor, 5) \ + \ + macro(op_check_has_instance, 5) \ + macro(op_instanceof, 4) \ + macro(op_typeof, 3) \ + macro(op_is_undefined, 3) \ + macro(op_is_boolean, 3) \ + macro(op_is_number, 3) \ + macro(op_is_string, 3) \ + macro(op_is_object, 3) \ + macro(op_is_function, 3) \ + macro(op_in, 4) \ + \ + macro(op_init_global_const_nop, 5) \ + macro(op_init_global_const, 5) \ + macro(op_get_by_id, 9) /* has value profiling */ \ + macro(op_get_by_id_out_of_line, 9) /* has value profiling */ \ + macro(op_get_by_id_self, 9) /* has value profiling */ \ + macro(op_get_by_id_proto, 9) /* has value profiling */ \ + macro(op_get_by_id_chain, 9) /* has value profiling */ \ + macro(op_get_by_id_getter_self, 9) /* has value profiling */ \ + macro(op_get_by_id_getter_proto, 9) /* has value profiling */ \ + macro(op_get_by_id_getter_chain, 9) /* has value profiling */ \ + macro(op_get_by_id_custom_self, 9) /* has value profiling */ \ + macro(op_get_by_id_custom_proto, 9) /* has value profiling */ \ + macro(op_get_by_id_custom_chain, 9) /* has value profiling */ \ + macro(op_get_by_id_generic, 9) /* has value profiling */ \ + macro(op_get_array_length, 9) /* has value profiling */ \ + macro(op_get_string_length, 9) /* has value profiling */ \ + macro(op_get_arguments_length, 4) \ + macro(op_put_by_id, 9) \ + macro(op_put_by_id_out_of_line, 9) \ + macro(op_put_by_id_transition, 9) \ + macro(op_put_by_id_transition_direct, 9) \ + macro(op_put_by_id_transition_direct_out_of_line, 9) \ + macro(op_put_by_id_transition_normal, 9) \ + macro(op_put_by_id_transition_normal_out_of_line, 9) \ + macro(op_put_by_id_replace, 9) \ + macro(op_put_by_id_generic, 9) \ + macro(op_del_by_id, 4) \ + macro(op_get_by_val, 6) /* has value profiling */ \ + macro(op_get_argument_by_val, 6) /* must be the same size as op_get_by_val */ \ + macro(op_get_by_pname, 7) \ + macro(op_put_by_val, 5) \ + macro(op_put_by_val_direct, 5) \ + macro(op_del_by_val, 4) \ + macro(op_put_by_index, 4) \ + macro(op_put_getter_setter, 5) \ + \ + macro(op_jmp, 2) \ + macro(op_jtrue, 3) \ + macro(op_jfalse, 3) \ + macro(op_jeq_null, 3) \ + macro(op_jneq_null, 3) \ + macro(op_jneq_ptr, 4) \ + macro(op_jless, 4) \ + macro(op_jlesseq, 4) \ + macro(op_jgreater, 4) \ + macro(op_jgreatereq, 4) \ + macro(op_jnless, 4) \ + macro(op_jnlesseq, 4) \ + macro(op_jngreater, 4) \ + macro(op_jngreatereq, 4) \ + \ + macro(op_loop_hint, 1) \ + \ + macro(op_switch_imm, 4) \ + macro(op_switch_char, 4) \ + macro(op_switch_string, 4) \ + \ + macro(op_new_func, 4) \ + macro(op_new_captured_func, 4) \ + macro(op_new_func_exp, 3) \ + macro(op_call, 8) /* has value profiling */ \ + macro(op_call_eval, 8) /* has value profiling */ \ + macro(op_call_varargs, 8) /* has value profiling */ \ + macro(op_tear_off_activation, 2) \ + macro(op_tear_off_arguments, 3) \ + macro(op_ret, 2) \ + macro(op_ret_object_or_this, 3) \ + \ + macro(op_construct, 8) \ + macro(op_strcat, 4) \ + macro(op_to_primitive, 3) \ + \ + macro(op_get_pnames, 6) \ + macro(op_next_pname, 7) \ + \ + macro(op_resolve_scope, 6) \ + macro(op_get_from_scope, 8) /* has value profiling */ \ + macro(op_put_to_scope, 7) \ + \ + macro(op_push_with_scope, 2) \ + macro(op_pop_scope, 1) \ + macro(op_push_name_scope, 4) \ + \ + macro(op_catch, 2) \ + macro(op_throw, 2) \ + macro(op_throw_static_error, 3) \ + \ + macro(op_debug, 3) \ + macro(op_profile_will_call, 2) \ + macro(op_profile_did_call, 2) \ + \ + extension__ \ + \ + macro(op_end, 2) // end must be the last opcode in the list #define FOR_EACH_CORE_OPCODE_ID(macro) \ FOR_EACH_CORE_OPCODE_ID_WITH_EXTENSION(macro, /* No extension */ ) @@ -59,11 +208,7 @@ namespace JSC { #undef OPCODE_ID_ENUM const int maxOpcodeLength = 9; -#if !ENABLE(JIT) -const int numOpcodeIDs = NUMBER_OF_BYTECODE_IDS + NUMBER_OF_CLOOP_BYTECODE_HELPER_IDS + NUMBER_OF_BYTECODE_HELPER_IDS; -#else -const int numOpcodeIDs = NUMBER_OF_BYTECODE_IDS + NUMBER_OF_BYTECODE_HELPER_IDS; -#endif +const int numOpcodeIDs = op_end + 1; #define OPCODE_ID_LENGTHS(id, length) const int id##_length = length; FOR_EACH_OPCODE_ID(OPCODE_ID_LENGTHS); @@ -75,7 +220,7 @@ const int numOpcodeIDs = NUMBER_OF_BYTECODE_IDS + NUMBER_OF_BYTECODE_HELPER_IDS; const int opcodeLengths[numOpcodeIDs] = { FOR_EACH_OPCODE_ID(OPCODE_ID_LENGTH_MAP) }; #undef OPCODE_ID_LENGTH_MAP -#define VERIFY_OPCODE_ID(id, size) COMPILE_ASSERT(id <= numOpcodeIDs, ASSERT_THAT_JS_OPCODE_IDS_ARE_VALID); +#define VERIFY_OPCODE_ID(id, size) COMPILE_ASSERT(id <= op_end, ASSERT_THAT_JS_OPCODE_IDS_ARE_VALID); FOR_EACH_OPCODE_ID(VERIFY_OPCODE_ID); #undef VERIFY_OPCODE_ID diff --git a/Source/JavaScriptCore/bytecode/Operands.h b/Source/JavaScriptCore/bytecode/Operands.h index 78ddaa525..f21e05f5f 100644 --- a/Source/JavaScriptCore/bytecode/Operands.h +++ b/Source/JavaScriptCore/bytecode/Operands.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, 2012, 2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -52,10 +52,10 @@ class Operands { public: Operands() { } - explicit Operands(size_t numArguments, size_t numLocals, const T& initialValue = Traits::defaultValue()) + explicit Operands(size_t numArguments, size_t numLocals) { - m_arguments.fill(initialValue, numArguments); - m_locals.fill(initialValue, numLocals); + m_arguments.fill(Traits::defaultValue(), numArguments); + m_locals.fill(Traits::defaultValue(), numLocals); } template<typename U, typename OtherTraits> @@ -96,7 +96,7 @@ public: return local(idx); } - void ensureLocals(size_t size, const T& ensuredValue = Traits::defaultValue()) + void ensureLocals(size_t size) { if (size <= m_locals.size()) return; @@ -104,7 +104,7 @@ public: size_t oldSize = m_locals.size(); m_locals.resize(size); for (size_t i = oldSize; i < m_locals.size(); ++i) - m_locals[i] = ensuredValue; + m_locals[i] = Traits::defaultValue(); } void setLocal(size_t idx, const T& value) @@ -149,7 +149,6 @@ public: } const T& operand(int operand) const { return const_cast<const T&>(const_cast<Operands*>(this)->operand(operand)); } - const T& operand(VirtualRegister operand) const { return const_cast<const T&>(const_cast<Operands*>(this)->operand(operand)); } bool hasOperand(int operand) const { @@ -210,10 +209,6 @@ public: return virtualRegisterForArgument(index).offset(); return virtualRegisterForLocal(index - numberOfArguments()).offset(); } - VirtualRegister virtualRegisterForIndex(size_t index) const - { - return VirtualRegister(operandForIndex(index)); - } size_t indexForOperand(int operand) const { if (operandIsArgument(operand)) @@ -257,7 +252,11 @@ public: } void dumpInContext(PrintStream& out, DumpContext* context) const; - void dump(PrintStream& out) const; + + void dump(PrintStream& out) const + { + dumpInContext(out, 0); + } private: Vector<T, 8> m_arguments; diff --git a/Source/JavaScriptCore/bytecode/OperandsInlines.h b/Source/JavaScriptCore/bytecode/OperandsInlines.h index c9dee88c7..74ad60bc1 100644 --- a/Source/JavaScriptCore/bytecode/OperandsInlines.h +++ b/Source/JavaScriptCore/bytecode/OperandsInlines.h @@ -47,22 +47,6 @@ void Operands<T, Traits>::dumpInContext(PrintStream& out, DumpContext* context) } } -template<typename T, typename Traits> -void Operands<T, Traits>::dump(PrintStream& out) const -{ - CommaPrinter comma(" "); - for (size_t argumentIndex = numberOfArguments(); argumentIndex--;) { - if (Traits::isEmptyForDump(argument(argumentIndex))) - continue; - out.print(comma, "arg", argumentIndex, ":", argument(argumentIndex)); - } - for (size_t localIndex = 0; localIndex < numberOfLocals(); ++localIndex) { - if (Traits::isEmptyForDump(local(localIndex))) - continue; - out.print(comma, "loc", localIndex, ":", local(localIndex)); - } -} - } // namespace JSC #endif // OperandsInlines_h diff --git a/Source/JavaScriptCore/bytecode/PolymorphicAccessStructureList.h b/Source/JavaScriptCore/bytecode/PolymorphicAccessStructureList.h index f8b64750c..61d97354f 100644 --- a/Source/JavaScriptCore/bytecode/PolymorphicAccessStructureList.h +++ b/Source/JavaScriptCore/bytecode/PolymorphicAccessStructureList.h @@ -29,6 +29,7 @@ #include "JITStubRoutine.h" #include "Structure.h" #include "StructureChain.h" +#include <wtf/Platform.h> #define POLYMORPHIC_LIST_CACHE_SIZE 8 @@ -45,58 +46,84 @@ struct PolymorphicAccessStructureList { WTF_MAKE_FAST_ALLOCATED; public: struct PolymorphicStubInfo { - bool isDirect : 1; - unsigned count : 31; + bool isChain; + bool isDirect; RefPtr<JITStubRoutine> stubRoutine; WriteBarrier<Structure> base; - WriteBarrier<StructureChain> chain; + union { + WriteBarrierBase<Structure> proto; + WriteBarrierBase<StructureChain> chain; + } u; PolymorphicStubInfo() { + u.proto.clear(); } void set(VM& vm, JSCell* owner, PassRefPtr<JITStubRoutine> _stubRoutine, Structure* _base, bool _isDirect) { stubRoutine = _stubRoutine; base.set(vm, owner, _base); + u.proto.clear(); + isChain = false; isDirect = _isDirect; - count = 0; } - - void set(VM& vm, JSCell* owner, PassRefPtr<JITStubRoutine> _stubRoutine, Structure* _base, StructureChain* _chain, bool _isDirect, unsigned _count) + + void set(VM& vm, JSCell* owner, PassRefPtr<JITStubRoutine> _stubRoutine, Structure* _base, Structure* _proto, bool _isDirect) + { + stubRoutine = _stubRoutine; + base.set(vm, owner, _base); + u.proto.set(vm, owner, _proto); + isChain = false; + isDirect = _isDirect; + } + + void set(VM& vm, JSCell* owner, PassRefPtr<JITStubRoutine> _stubRoutine, Structure* _base, StructureChain* _chain, bool _isDirect) { stubRoutine = _stubRoutine; base.set(vm, owner, _base); - chain.set(vm, owner, _chain); + u.chain.set(vm, owner, _chain); + isChain = true; isDirect = _isDirect; - count = _count; } } list[POLYMORPHIC_LIST_CACHE_SIZE]; - + PolymorphicAccessStructureList() { } - + PolymorphicAccessStructureList(VM& vm, JSCell* owner, PassRefPtr<JITStubRoutine> stubRoutine, Structure* firstBase, bool isDirect) { list[0].set(vm, owner, stubRoutine, firstBase, isDirect); } - PolymorphicAccessStructureList(VM& vm, JSCell* owner, PassRefPtr<JITStubRoutine> stubRoutine, Structure* firstBase, StructureChain* firstChain, bool isDirect, unsigned count) + PolymorphicAccessStructureList(VM& vm, JSCell* owner, PassRefPtr<JITStubRoutine> stubRoutine, Structure* firstBase, Structure* firstProto, bool isDirect) { - list[0].set(vm, owner, stubRoutine, firstBase, firstChain, isDirect, count); + list[0].set(vm, owner, stubRoutine, firstBase, firstProto, isDirect); + } + + PolymorphicAccessStructureList(VM& vm, JSCell* owner, PassRefPtr<JITStubRoutine> stubRoutine, Structure* firstBase, StructureChain* firstChain, bool isDirect) + { + list[0].set(vm, owner, stubRoutine, firstBase, firstChain, isDirect); } bool visitWeak(int count) { for (int i = 0; i < count; ++i) { PolymorphicStubInfo& info = list[i]; - if (!info.base) + if (!info.base) { + // We're being marked during initialisation of an entry + ASSERT(!info.u.proto); continue; + } if (!Heap::isMarked(info.base.get())) return false; - if (info.chain && !Heap::isMarked(info.chain.get())) + if (info.u.proto && !info.isChain + && !Heap::isMarked(info.u.proto.get())) + return false; + if (info.u.chain && info.isChain + && !Heap::isMarked(info.u.chain.get())) return false; } diff --git a/Source/JavaScriptCore/bytecode/PolymorphicGetByIdList.cpp b/Source/JavaScriptCore/bytecode/PolymorphicGetByIdList.cpp deleted file mode 100644 index 27e806d2c..000000000 --- a/Source/JavaScriptCore/bytecode/PolymorphicGetByIdList.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) 2014, 2015 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. - */ - -#include "config.h" -#include "PolymorphicGetByIdList.h" - -#if ENABLE(JIT) - -#include "CodeBlock.h" -#include "Heap.h" -#include "JSCInlines.h" -#include "StructureStubInfo.h" - -namespace JSC { - -GetByIdAccess::GetByIdAccess( - VM& vm, JSCell* owner, AccessType type, PassRefPtr<JITStubRoutine> stubRoutine, - Structure* structure, const ObjectPropertyConditionSet& conditionSet) - : m_type(type) - , m_structure(vm, owner, structure) - , m_conditionSet(conditionSet) - , m_stubRoutine(stubRoutine) -{ -} - -GetByIdAccess::~GetByIdAccess() -{ -} - -GetByIdAccess GetByIdAccess::fromStructureStubInfo(StructureStubInfo& stubInfo) -{ - MacroAssemblerCodePtr initialSlowPath = - stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase); - - GetByIdAccess result; - - RELEASE_ASSERT(stubInfo.accessType == access_get_by_id_self); - - result.m_type = SimpleInline; - result.m_structure.copyFrom(stubInfo.u.getByIdSelf.baseObjectStructure); - result.m_stubRoutine = JITStubRoutine::createSelfManagedRoutine(initialSlowPath); - - return result; -} - -bool GetByIdAccess::visitWeak(RepatchBuffer& repatchBuffer) const -{ - if (m_structure && !Heap::isMarked(m_structure.get())) - return false; - if (!m_conditionSet.areStillLive()) - return false; - if (!m_stubRoutine->visitWeak(repatchBuffer)) - return false; - return true; -} - -PolymorphicGetByIdList::PolymorphicGetByIdList(StructureStubInfo& stubInfo) -{ - if (stubInfo.accessType == access_unset) - return; - - m_list.append(GetByIdAccess::fromStructureStubInfo(stubInfo)); -} - -PolymorphicGetByIdList* PolymorphicGetByIdList::from(StructureStubInfo& stubInfo) -{ - if (stubInfo.accessType == access_get_by_id_list) - return stubInfo.u.getByIdList.list; - - ASSERT( - stubInfo.accessType == access_get_by_id_self - || stubInfo.accessType == access_unset); - - PolymorphicGetByIdList* result = new PolymorphicGetByIdList(stubInfo); - - stubInfo.initGetByIdList(result); - - return result; -} - -PolymorphicGetByIdList::~PolymorphicGetByIdList() { } - -MacroAssemblerCodePtr PolymorphicGetByIdList::currentSlowPathTarget( - StructureStubInfo& stubInfo) const -{ - if (isEmpty()) - return stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase); - return m_list.last().stubRoutine()->code().code(); -} - -void PolymorphicGetByIdList::addAccess(const GetByIdAccess& access) -{ - ASSERT(!isFull()); - // Make sure that the resizing optimizes for space, not time. - m_list.resizeToFit(m_list.size() + 1); - m_list.last() = access; -} - -bool PolymorphicGetByIdList::isFull() const -{ - ASSERT(size() <= POLYMORPHIC_LIST_CACHE_SIZE); - return size() == POLYMORPHIC_LIST_CACHE_SIZE; -} - -bool PolymorphicGetByIdList::isAlmostFull() const -{ - ASSERT(size() <= POLYMORPHIC_LIST_CACHE_SIZE); - return size() >= POLYMORPHIC_LIST_CACHE_SIZE - 1; -} - -bool PolymorphicGetByIdList::didSelfPatching() const -{ - for (unsigned i = size(); i--;) { - if (at(i).type() == GetByIdAccess::SimpleInline) - return true; - } - return false; -} - -bool PolymorphicGetByIdList::visitWeak(RepatchBuffer& repatchBuffer) const -{ - for (unsigned i = size(); i--;) { - if (!at(i).visitWeak(repatchBuffer)) - return false; - } - return true; -} - -} // namespace JSC - -#endif // ENABLE(JIT) - - diff --git a/Source/JavaScriptCore/bytecode/PolymorphicGetByIdList.h b/Source/JavaScriptCore/bytecode/PolymorphicGetByIdList.h deleted file mode 100644 index 60476cc38..000000000 --- a/Source/JavaScriptCore/bytecode/PolymorphicGetByIdList.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2014, 2015 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 PolymorphicGetByIdList_h -#define PolymorphicGetByIdList_h - -#if ENABLE(JIT) - -#include "CodeOrigin.h" -#include "MacroAssembler.h" -#include "ObjectPropertyConditionSet.h" -#include "Opcode.h" -#include "Structure.h" -#include <wtf/Vector.h> - -namespace JSC { - -class CodeBlock; -struct StructureStubInfo; - -class GetByIdAccess { -public: - enum AccessType { - Invalid, - SimpleInline, // This is the patched inline access. - SimpleStub, // This is a stub. - WatchedStub, - Getter, - CustomGetter, - SimpleMiss, - }; - - GetByIdAccess() - : m_type(Invalid) - { - } - - GetByIdAccess( - VM&, JSCell* owner, AccessType, PassRefPtr<JITStubRoutine>, Structure*, - const ObjectPropertyConditionSet& = ObjectPropertyConditionSet()); - - ~GetByIdAccess(); - - static GetByIdAccess fromStructureStubInfo(StructureStubInfo&); - - bool isSet() const { return m_type != Invalid; } - bool operator!() const { return !isSet(); } - - AccessType type() const { return m_type; } - - Structure* structure() const { return m_structure.get(); } - - const ObjectPropertyConditionSet& conditionSet() const { return m_conditionSet; } - - JITStubRoutine* stubRoutine() const - { - ASSERT(isSet()); - return m_stubRoutine.get(); - } - - bool doesCalls() const { return type() == Getter || type() == CustomGetter; } - bool isWatched() const { return type() == WatchedStub; } - bool isSimple() const { return !doesCalls() && !isWatched(); } - - bool visitWeak(RepatchBuffer&) const; - -private: - friend class CodeBlock; - - AccessType m_type; - WriteBarrier<Structure> m_structure; - ObjectPropertyConditionSet m_conditionSet; - RefPtr<JITStubRoutine> m_stubRoutine; -}; - -class PolymorphicGetByIdList { - WTF_MAKE_FAST_ALLOCATED; -public: - // Either creates a new polymorphic get list, or returns the one that is already in - // place. - static PolymorphicGetByIdList* from(StructureStubInfo&); - - ~PolymorphicGetByIdList(); - - MacroAssemblerCodePtr currentSlowPathTarget(StructureStubInfo& stubInfo) const; - - void addAccess(const GetByIdAccess&); - - bool isEmpty() const { return m_list.isEmpty(); } - unsigned size() const { return m_list.size(); } - bool isFull() const; - bool isAlmostFull() const; // True if adding an element would make isFull() true. - const GetByIdAccess& at(unsigned i) const { return m_list[i]; } - const GetByIdAccess& operator[](unsigned i) const { return m_list[i]; } - - bool didSelfPatching() const; // Are any of the accesses SimpleInline? - - bool visitWeak(RepatchBuffer&) const; - -private: - friend class CodeBlock; - - PolymorphicGetByIdList(StructureStubInfo&); - - Vector<GetByIdAccess, 2> m_list; -}; - -} // namespace JSC - -#endif // ENABLE(JIT) - -#endif // PolymorphicGetByIdList_h - diff --git a/Source/JavaScriptCore/bytecode/PolymorphicPutByIdList.cpp b/Source/JavaScriptCore/bytecode/PolymorphicPutByIdList.cpp index 7eddb8621..6a6ec8141 100644 --- a/Source/JavaScriptCore/bytecode/PolymorphicPutByIdList.cpp +++ b/Source/JavaScriptCore/bytecode/PolymorphicPutByIdList.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2014, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2012 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,11 +32,10 @@ namespace JSC { -PutByIdAccess PutByIdAccess::fromStructureStubInfo(StructureStubInfo& stubInfo) +PutByIdAccess PutByIdAccess::fromStructureStubInfo( + StructureStubInfo& stubInfo, + MacroAssemblerCodePtr initialSlowPath) { - MacroAssemblerCodePtr initialSlowPath = - stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase); - PutByIdAccess result; switch (stubInfo.accessType) { @@ -51,8 +50,7 @@ PutByIdAccess PutByIdAccess::fromStructureStubInfo(StructureStubInfo& stubInfo) result.m_type = Transition; result.m_oldStructure.copyFrom(stubInfo.u.putByIdTransition.previousStructure); result.m_newStructure.copyFrom(stubInfo.u.putByIdTransition.structure); - result.m_conditionSet = ObjectPropertyConditionSet::adoptRawPointer( - stubInfo.u.putByIdTransition.rawConditionSet); + result.m_chain.copyFrom(stubInfo.u.putByIdTransition.chain); result.m_stubRoutine = stubInfo.stubRoutine; break; @@ -63,11 +61,8 @@ PutByIdAccess PutByIdAccess::fromStructureStubInfo(StructureStubInfo& stubInfo) return result; } -bool PutByIdAccess::visitWeak(RepatchBuffer& repatchBuffer) const +bool PutByIdAccess::visitWeak() const { - if (!m_conditionSet.areStillLive()) - return false; - switch (m_type) { case Replace: if (!Heap::isMarked(m_oldStructure.get())) @@ -78,42 +73,39 @@ bool PutByIdAccess::visitWeak(RepatchBuffer& repatchBuffer) const return false; if (!Heap::isMarked(m_newStructure.get())) return false; - break; - case Setter: - case CustomSetter: - if (!Heap::isMarked(m_oldStructure.get())) + if (!Heap::isMarked(m_chain.get())) return false; break; default: RELEASE_ASSERT_NOT_REACHED(); return false; } - if (!m_stubRoutine->visitWeak(repatchBuffer)) - return false; return true; } PolymorphicPutByIdList::PolymorphicPutByIdList( - PutKind putKind, StructureStubInfo& stubInfo) + PutKind putKind, + StructureStubInfo& stubInfo, + MacroAssemblerCodePtr initialSlowPath) : m_kind(putKind) { - if (stubInfo.accessType != access_unset) - m_list.append(PutByIdAccess::fromStructureStubInfo(stubInfo)); + m_list.append(PutByIdAccess::fromStructureStubInfo(stubInfo, initialSlowPath)); } PolymorphicPutByIdList* PolymorphicPutByIdList::from( - PutKind putKind, StructureStubInfo& stubInfo) + PutKind putKind, + StructureStubInfo& stubInfo, + MacroAssemblerCodePtr initialSlowPath) { if (stubInfo.accessType == access_put_by_id_list) return stubInfo.u.putByIdList.list; ASSERT(stubInfo.accessType == access_put_by_id_replace - || stubInfo.accessType == access_put_by_id_transition_normal - || stubInfo.accessType == access_put_by_id_transition_direct - || stubInfo.accessType == access_unset); + || stubInfo.accessType == access_put_by_id_transition_normal + || stubInfo.accessType == access_put_by_id_transition_direct); PolymorphicPutByIdList* result = - new PolymorphicPutByIdList(putKind, stubInfo); + new PolymorphicPutByIdList(putKind, stubInfo, initialSlowPath); stubInfo.initPutByIdList(result); @@ -138,14 +130,14 @@ void PolymorphicPutByIdList::addAccess(const PutByIdAccess& putByIdAccess) { ASSERT(!isFull()); // Make sure that the resizing optimizes for space, not time. - m_list.resizeToFit(m_list.size() + 1); + m_list.resize(m_list.size() + 1); m_list.last() = putByIdAccess; } -bool PolymorphicPutByIdList::visitWeak(RepatchBuffer& repatchBuffer) const +bool PolymorphicPutByIdList::visitWeak() const { for (unsigned i = 0; i < size(); ++i) { - if (!at(i).visitWeak(repatchBuffer)) + if (!at(i).visitWeak()) return false; } return true; diff --git a/Source/JavaScriptCore/bytecode/PolymorphicPutByIdList.h b/Source/JavaScriptCore/bytecode/PolymorphicPutByIdList.h index 02939f311..d9fe2e7cf 100644 --- a/Source/JavaScriptCore/bytecode/PolymorphicPutByIdList.h +++ b/Source/JavaScriptCore/bytecode/PolymorphicPutByIdList.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2014, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2012 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,14 +26,14 @@ #ifndef PolymorphicPutByIdList_h #define PolymorphicPutByIdList_h +#include <wtf/Platform.h> + #if ENABLE(JIT) #include "CodeOrigin.h" #include "MacroAssembler.h" -#include "ObjectPropertyConditionSet.h" #include "Opcode.h" #include "PutKind.h" -#include "PutPropertySlot.h" #include "Structure.h" #include <wtf/Vector.h> @@ -47,9 +47,7 @@ public: enum AccessType { Invalid, Transition, - Replace, - Setter, - CustomSetter + Replace }; PutByIdAccess() @@ -62,19 +60,18 @@ public: JSCell* owner, Structure* oldStructure, Structure* newStructure, - const ObjectPropertyConditionSet& conditionSet, + StructureChain* chain, PassRefPtr<JITStubRoutine> stubRoutine) { PutByIdAccess result; result.m_type = Transition; result.m_oldStructure.set(vm, owner, oldStructure); result.m_newStructure.set(vm, owner, newStructure); - result.m_conditionSet = conditionSet; - result.m_customSetter = 0; + result.m_chain.set(vm, owner, chain); result.m_stubRoutine = stubRoutine; return result; } - + static PutByIdAccess replace( VM& vm, JSCell* owner, @@ -84,32 +81,13 @@ public: PutByIdAccess result; result.m_type = Replace; result.m_oldStructure.set(vm, owner, structure); - result.m_customSetter = 0; - result.m_stubRoutine = stubRoutine; - return result; - } - - - static PutByIdAccess setter( - VM& vm, - JSCell* owner, - AccessType accessType, - Structure* structure, - const ObjectPropertyConditionSet& conditionSet, - PutPropertySlot::PutValueFunc customSetter, - PassRefPtr<JITStubRoutine> stubRoutine) - { - RELEASE_ASSERT(accessType == Setter || accessType == CustomSetter); - PutByIdAccess result; - result.m_oldStructure.set(vm, owner, structure); - result.m_type = accessType; - result.m_conditionSet = conditionSet; - result.m_customSetter = customSetter; result.m_stubRoutine = stubRoutine; return result; } - static PutByIdAccess fromStructureStubInfo(StructureStubInfo&); + static PutByIdAccess fromStructureStubInfo( + StructureStubInfo&, + MacroAssemblerCodePtr initialSlowPath); bool isSet() const { return m_type != Invalid; } bool operator!() const { return !isSet(); } @@ -118,21 +96,19 @@ public: bool isTransition() const { return m_type == Transition; } bool isReplace() const { return m_type == Replace; } - bool isSetter() const { return m_type == Setter; } - bool isCustom() const { return m_type == CustomSetter; } Structure* oldStructure() const { // Using this instead of isSet() to make this assertion robust against the possibility // of additional access types being added. - ASSERT(isTransition() || isReplace() || isSetter() || isCustom()); + ASSERT(isTransition() || isReplace()); return m_oldStructure.get(); } Structure* structure() const { - ASSERT(isReplace() || isSetter() || isCustom()); + ASSERT(isReplace()); return m_oldStructure.get(); } @@ -142,21 +118,19 @@ public: return m_newStructure.get(); } - const ObjectPropertyConditionSet& conditionSet() const { return m_conditionSet; } - - JITStubRoutine* stubRoutine() const + StructureChain* chain() const { - ASSERT(isTransition() || isReplace() || isSetter() || isCustom()); - return m_stubRoutine.get(); + ASSERT(isTransition()); + return m_chain.get(); } - - PutPropertySlot::PutValueFunc customSetter() const + + PassRefPtr<JITStubRoutine> stubRoutine() const { - ASSERT(isCustom()); - return m_customSetter; + ASSERT(isTransition() || isReplace()); + return m_stubRoutine; } - - bool visitWeak(RepatchBuffer&) const; + + bool visitWeak() const; private: friend class CodeBlock; @@ -164,17 +138,27 @@ private: AccessType m_type; WriteBarrier<Structure> m_oldStructure; WriteBarrier<Structure> m_newStructure; - ObjectPropertyConditionSet m_conditionSet; - PutPropertySlot::PutValueFunc m_customSetter; + WriteBarrier<StructureChain> m_chain; RefPtr<JITStubRoutine> m_stubRoutine; }; class PolymorphicPutByIdList { WTF_MAKE_FAST_ALLOCATED; public: + // Initialize from a stub info; this will place one element in the list and it will + // be created by converting the stub info's put by id access information into our + // PutByIdAccess. + PolymorphicPutByIdList( + PutKind, + StructureStubInfo&, + MacroAssemblerCodePtr initialSlowPath); + // Either creates a new polymorphic put list, or returns the one that is already // in place. - static PolymorphicPutByIdList* from(PutKind, StructureStubInfo&); + static PolymorphicPutByIdList* from( + PutKind, + StructureStubInfo&, + MacroAssemblerCodePtr initialSlowPath); ~PolymorphicPutByIdList(); @@ -194,16 +178,11 @@ public: PutKind kind() const { return m_kind; } - bool visitWeak(RepatchBuffer&) const; + bool visitWeak() const; private: friend class CodeBlock; - // Initialize from a stub info; this will place one element in the list and it will - // be created by converting the stub info's put by id access information into our - // PutByIdAccess. - PolymorphicPutByIdList(PutKind, StructureStubInfo&); - Vector<PutByIdAccess, 2> m_list; PutKind m_kind; }; diff --git a/Source/JavaScriptCore/bytecode/PreciseJumpTargets.cpp b/Source/JavaScriptCore/bytecode/PreciseJumpTargets.cpp index 414dfd97d..ede8a3643 100644 --- a/Source/JavaScriptCore/bytecode/PreciseJumpTargets.cpp +++ b/Source/JavaScriptCore/bytecode/PreciseJumpTargets.cpp @@ -26,8 +26,6 @@ #include "config.h" #include "PreciseJumpTargets.h" -#include "JSCInlines.h" - namespace JSC { template <size_t vectorSize> @@ -73,6 +71,12 @@ static void getJumpTargetsForBytecodeOffset(CodeBlock* codeBlock, Interpreter* i out.append(bytecodeOffset + current[2].u.operand); break; } + case op_get_pnames: + out.append(bytecodeOffset + current[5].u.operand); + break; + case op_next_pname: + out.append(bytecodeOffset + current[6].u.operand); + break; case op_check_has_instance: out.append(bytecodeOffset + current[4].u.operand); break; @@ -119,7 +123,6 @@ void computePreciseJumpTargets(CodeBlock* codeBlock, Vector<unsigned, 32>& out) lastValue = value; } out.resize(toIndex); - out.shrinkToFit(); } void findJumpTargetsForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecodeOffset, Vector<unsigned, 1>& out) diff --git a/Source/JavaScriptCore/bytecode/PreciseJumpTargets.h b/Source/JavaScriptCore/bytecode/PreciseJumpTargets.h index 852413d77..fb60f9b9b 100644 --- a/Source/JavaScriptCore/bytecode/PreciseJumpTargets.h +++ b/Source/JavaScriptCore/bytecode/PreciseJumpTargets.h @@ -30,9 +30,7 @@ namespace JSC { -// Return a sorted list of bytecode index that are the destination of a jump. void computePreciseJumpTargets(CodeBlock*, Vector<unsigned, 32>& out); - void findJumpTargetsForBytecodeOffset(CodeBlock*, unsigned bytecodeOffset, Vector<unsigned, 1>& out); } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/TrackedReferences.cpp b/Source/JavaScriptCore/bytecode/ProfiledCodeBlockJettisoningWatchpoint.cpp index d98fa9759..edf8e228d 100644 --- a/Source/JavaScriptCore/bytecode/TrackedReferences.cpp +++ b/Source/JavaScriptCore/bytecode/ProfiledCodeBlockJettisoningWatchpoint.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Apple Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -24,57 +24,38 @@ */ #include "config.h" -#include "TrackedReferences.h" +#include "ProfiledCodeBlockJettisoningWatchpoint.h" -#include "JSCInlines.h" -#include <wtf/CommaPrinter.h> +#include "CodeBlock.h" +#include "DFGCommon.h" +#include "DFGExitProfile.h" namespace JSC { -TrackedReferences::TrackedReferences() +void ProfiledCodeBlockJettisoningWatchpoint::fireInternal() { -} - -TrackedReferences::~TrackedReferences() -{ -} - -void TrackedReferences::add(JSCell* cell) -{ - if (cell) - m_references.add(cell); -} - -void TrackedReferences::add(JSValue value) -{ - if (value.isCell()) - add(value.asCell()); -} - -void TrackedReferences::check(JSCell* cell) const -{ - if (!cell) - return; + if (DFG::shouldShowDisassembly()) { + dataLog( + "Firing profiled watchpoint ", RawPointer(this), " on ", *m_codeBlock, " due to ", + m_exitKind, " at ", m_codeOrigin, "\n"); + } - if (m_references.contains(cell)) - return; + // FIXME: Maybe this should call alternative(). + // https://bugs.webkit.org/show_bug.cgi?id=123677 + CodeBlock* machineBaselineCodeBlock = m_codeBlock->baselineAlternative(); + CodeBlock* sourceBaselineCodeBlock = + baselineCodeBlockForOriginAndBaselineCodeBlock( + m_codeOrigin, machineBaselineCodeBlock); - dataLog("Found untracked reference: ", RawPointer(cell), "\n"); - dataLog("All tracked references: ", *this, "\n"); - RELEASE_ASSERT_NOT_REACHED(); -} - -void TrackedReferences::check(JSValue value) const -{ - if (value.isCell()) - check(value.asCell()); -} - -void TrackedReferences::dump(PrintStream& out) const -{ - CommaPrinter comma; - for (JSCell* cell : m_references) - out.print(comma, RawPointer(cell)); + if (sourceBaselineCodeBlock) { + sourceBaselineCodeBlock->addFrequentExitSite( + DFG::FrequentExitSite(m_codeOrigin.bytecodeIndex, m_exitKind)); + } + + m_codeBlock->jettison(CountReoptimization); + + if (isOnList()) + remove(); } } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/DeferredSourceDump.h b/Source/JavaScriptCore/bytecode/ProfiledCodeBlockJettisoningWatchpoint.h index 72cb6b3b8..108e23a37 100644 --- a/Source/JavaScriptCore/bytecode/DeferredSourceDump.h +++ b/Source/JavaScriptCore/bytecode/ProfiledCodeBlockJettisoningWatchpoint.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Apple Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -20,33 +20,46 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DeferredSourceDump_h -#define DeferredSourceDump_h +#ifndef ProfiledCodeBlockJettisoningWatchpoint_h +#define ProfiledCodeBlockJettisoningWatchpoint_h #include "CodeOrigin.h" -#include "JITCode.h" +#include "ExitKind.h" +#include "Watchpoint.h" namespace JSC { class CodeBlock; -class DeferredSourceDump { +class ProfiledCodeBlockJettisoningWatchpoint : public Watchpoint { public: - DeferredSourceDump(CodeBlock*); - DeferredSourceDump(CodeBlock*, CodeBlock* rootCodeBlock, JITCode::JITType rootJITType, CodeOrigin callerCodeOrigin); - - void dump(); + ProfiledCodeBlockJettisoningWatchpoint() + : m_exitKind(ExitKindUnset) + , m_codeBlock(0) + { + } + + ProfiledCodeBlockJettisoningWatchpoint( + CodeOrigin codeOrigin, ExitKind exitKind, CodeBlock* codeBlock) + : m_codeOrigin(codeOrigin) + , m_exitKind(exitKind) + , m_codeBlock(codeBlock) + { + } + +protected: + virtual void fireInternal() override; private: + CodeOrigin m_codeOrigin; + ExitKind m_exitKind; CodeBlock* m_codeBlock; - CodeBlock* m_rootCodeBlock; - JITCode::JITType m_rootJITType; - CodeOrigin m_callerCodeOrigin; }; } // namespace JSC -#endif // DeferredSourceDump_h +#endif // ProfiledCodeBlockJettisoningWatchpoint_h + diff --git a/Source/JavaScriptCore/bytecode/PropertyCondition.cpp b/Source/JavaScriptCore/bytecode/PropertyCondition.cpp deleted file mode 100644 index 8aab4eaec..000000000 --- a/Source/JavaScriptCore/bytecode/PropertyCondition.cpp +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -#include "config.h" -#include "PropertyCondition.h" - -#include "GetterSetter.h" -#include "JSCInlines.h" -#include "TrackedReferences.h" - -namespace JSC { - -static bool verbose = false; - -void PropertyCondition::dumpInContext(PrintStream& out, DumpContext* context) const -{ - if (!*this) { - out.print("<invalid>"); - return; - } - - out.print(m_kind, " of ", m_uid); - switch (m_kind) { - case Presence: - out.print(" at ", offset(), " with attributes ", attributes()); - return; - case Absence: - case AbsenceOfSetter: - out.print(" with prototype ", inContext(JSValue(prototype()), context)); - return; - case Equivalence: - out.print(" with ", inContext(requiredValue(), context)); - return; - } - RELEASE_ASSERT_NOT_REACHED(); -} - -void PropertyCondition::dump(PrintStream& out) const -{ - dumpInContext(out, nullptr); -} - -bool PropertyCondition::isStillValidAssumingImpurePropertyWatchpoint( - Structure* structure, JSObject* base) const -{ - if (verbose) { - dataLog( - "Determining validity of ", *this, " with structure ", pointerDump(structure), " and base ", - JSValue(base), " assuming impure property watchpoints are set.\n"); - } - - if (!*this) { - if (verbose) - dataLog("Invalid because unset.\n"); - return false; - } - - if (!structure->propertyAccessesAreCacheable()) { - if (verbose) - dataLog("Invalid because accesses are not cacheable.\n"); - return false; - } - - switch (m_kind) { - case Presence: { - unsigned currentAttributes; - PropertyOffset currentOffset = structure->getConcurrently(uid(), currentAttributes); - if (currentOffset != offset() || currentAttributes != attributes()) { - if (verbose) { - dataLog( - "Invalid because we need offset, attributes to be ", offset(), ", ", attributes(), - " but they are ", currentOffset, ", ", currentAttributes, "\n"); - } - return false; - } - return true; - } - - case Absence: { - if (structure->isDictionary()) { - if (verbose) - dataLog("Invalid because it's a dictionary.\n"); - return false; - } - - PropertyOffset currentOffset = structure->getConcurrently(uid()); - if (currentOffset != invalidOffset) { - if (verbose) - dataLog("Invalid because the property exists at offset: ", currentOffset, "\n"); - return false; - } - - if (structure->storedPrototypeObject() != prototype()) { - if (verbose) { - dataLog( - "Invalid because the prototype is ", structure->storedPrototype(), " even though " - "it should have been ", JSValue(prototype()), "\n"); - } - return false; - } - - return true; - } - - case AbsenceOfSetter: { - if (structure->isDictionary()) { - if (verbose) - dataLog("Invalid because it's a dictionary.\n"); - return false; - } - - unsigned currentAttributes; - PropertyOffset currentOffset = structure->getConcurrently(uid(), currentAttributes); - if (currentOffset != invalidOffset) { - if (currentAttributes & (Accessor | CustomAccessor)) { - if (verbose) { - dataLog( - "Invalid because we expected not to have a setter, but we have one at offset ", - currentOffset, " with attributes ", currentAttributes, "\n"); - } - return false; - } - } - - if (structure->storedPrototypeObject() != prototype()) { - if (verbose) { - dataLog( - "Invalid because the prototype is ", structure->storedPrototype(), " even though " - "it should have been ", JSValue(prototype()), "\n"); - } - return false; - } - - return true; - } - - case Equivalence: { - if (!base || base->structure() != structure) { - // Conservatively return false, since we cannot verify this one without having the - // object. - if (verbose) { - dataLog( - "Invalid because we don't have a base or the base has the wrong structure: ", - RawPointer(base), "\n"); - } - return false; - } - - // FIXME: This is somewhat racy, and maybe more risky than we want. - // https://bugs.webkit.org/show_bug.cgi?id=134641 - - PropertyOffset currentOffset = structure->getConcurrently(uid()); - JSValue currentValue = base->getDirect(currentOffset); - if (currentValue != requiredValue()) { - if (verbose) { - dataLog( - "Invalid because the value is ", currentValue, " but we require ", requiredValue(), - "\n"); - } - return false; - } - - return true; - } } - - RELEASE_ASSERT_NOT_REACHED(); - return false; -} - -bool PropertyCondition::validityRequiresImpurePropertyWatchpoint(Structure* structure) const -{ - if (!*this) - return false; - - switch (m_kind) { - case Presence: - case Absence: - case Equivalence: - return structure->needImpurePropertyWatchpoint(); - default: - return false; - } -} - -bool PropertyCondition::isStillValid(Structure* structure, JSObject* base) const -{ - if (!isStillValidAssumingImpurePropertyWatchpoint(structure, base)) - return false; - - // Currently we assume that an impure property can cause a property to appear, and can also - // "shadow" an existing JS property on the same object. Hence it affects both presence and - // absence. It doesn't affect AbsenceOfSetter because impure properties aren't ever setters. - switch (m_kind) { - case Presence: - case Absence: - case Equivalence: - if (structure->typeInfo().hasImpureGetOwnPropertySlot()) - return false; - break; - default: - break; - } - - return true; -} - -bool PropertyCondition::isWatchableWhenValid( - Structure* structure, WatchabilityEffort effort) const -{ - if (structure->transitionWatchpointSetHasBeenInvalidated()) - return false; - - switch (m_kind) { - case Equivalence: { - PropertyOffset offset = structure->getConcurrently(uid()); - - // This method should only be called when some variant of isValid returned true, which - // implies that we already confirmed that the structure knows of the property. We should - // also have verified that the Structure is a cacheable dictionary, which means we - // shouldn't have a TOCTOU race either. - RELEASE_ASSERT(offset != invalidOffset); - - WatchpointSet* set; - switch (effort) { - case MakeNoChanges: - set = structure->propertyReplacementWatchpointSet(offset); - break; - case EnsureWatchability: - set = structure->ensurePropertyReplacementWatchpointSet( - *Heap::heap(structure)->vm(), offset); - break; - } - - if (!set || !set->isStillValid()) - return false; - - break; - } - - default: - break; - } - - return true; -} - -bool PropertyCondition::isWatchableAssumingImpurePropertyWatchpoint( - Structure* structure, JSObject* base, WatchabilityEffort effort) const -{ - return isStillValidAssumingImpurePropertyWatchpoint(structure, base) - && isWatchableWhenValid(structure, effort); -} - -bool PropertyCondition::isWatchable( - Structure* structure, JSObject* base, WatchabilityEffort effort) const -{ - return isStillValid(structure, base) - && isWatchableWhenValid(structure, effort); -} - -bool PropertyCondition::isStillLive() const -{ - if (hasPrototype() && prototype() && !Heap::isMarked(prototype())) - return false; - - if (hasRequiredValue() - && requiredValue() - && requiredValue().isCell() - && !Heap::isMarked(requiredValue().asCell())) - return false; - - return true; -} - -void PropertyCondition::validateReferences(const TrackedReferences& tracked) const -{ - if (hasPrototype()) - tracked.check(prototype()); - - if (hasRequiredValue()) - tracked.check(requiredValue()); -} - -bool PropertyCondition::isValidValueForAttributes(JSValue value, unsigned attributes) -{ - bool attributesClaimAccessor = !!(attributes & Accessor); - bool valueClaimsAccessor = !!jsDynamicCast<GetterSetter*>(value); - return attributesClaimAccessor == valueClaimsAccessor; -} - -bool PropertyCondition::isValidValueForPresence(JSValue value) const -{ - return isValidValueForAttributes(value, attributes()); -} - -PropertyCondition PropertyCondition::attemptToMakeEquivalenceWithoutBarrier(JSObject* base) const -{ - Structure* structure = base->structure(); - if (!structure->isValidOffset(offset())) - return PropertyCondition(); - JSValue value = base->getDirect(offset()); - if (!isValidValueForPresence(value)) - return PropertyCondition(); - return equivalenceWithoutBarrier(uid(), value); -} - -} // namespace JSC - -namespace WTF { - -void printInternal(PrintStream& out, JSC::PropertyCondition::Kind condition) -{ - switch (condition) { - case JSC::PropertyCondition::Presence: - out.print("Presence"); - return; - case JSC::PropertyCondition::Absence: - out.print("Absence"); - return; - case JSC::PropertyCondition::AbsenceOfSetter: - out.print("Absence"); - return; - case JSC::PropertyCondition::Equivalence: - out.print("Equivalence"); - return; - } - RELEASE_ASSERT_NOT_REACHED(); -} - -} // namespace WTF diff --git a/Source/JavaScriptCore/bytecode/PropertyCondition.h b/Source/JavaScriptCore/bytecode/PropertyCondition.h deleted file mode 100644 index bd08c3b9d..000000000 --- a/Source/JavaScriptCore/bytecode/PropertyCondition.h +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright (C) 2015 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 PropertyCondition_h -#define PropertyCondition_h - -#include "JSObject.h" -#include <wtf/HashMap.h> - -namespace JSC { - -class TrackedReferences; - -class PropertyCondition { -public: - enum Kind { - Presence, - Absence, - AbsenceOfSetter, - Equivalence // An adaptive watchpoint on this will be a pair of watchpoints, and when the structure transitions, we will set the replacement watchpoint on the new structure. - }; - - PropertyCondition() - : m_uid(nullptr) - , m_kind(Presence) - { - memset(&u, 0, sizeof(u)); - } - - PropertyCondition(WTF::HashTableDeletedValueType) - : m_uid(nullptr) - , m_kind(Absence) - { - memset(&u, 0, sizeof(u)); - } - - static PropertyCondition presenceWithoutBarrier(UniquedStringImpl* uid, PropertyOffset offset, unsigned attributes) - { - PropertyCondition result; - result.m_uid = uid; - result.m_kind = Presence; - result.u.presence.offset = offset; - result.u.presence.attributes = attributes; - return result; - } - - static PropertyCondition presence( - VM&, JSCell*, UniquedStringImpl* uid, PropertyOffset offset, unsigned attributes) - { - return presenceWithoutBarrier(uid, offset, attributes); - } - - // NOTE: The prototype is the storedPrototype not the prototypeForLookup. - static PropertyCondition absenceWithoutBarrier(UniquedStringImpl* uid, JSObject* prototype) - { - PropertyCondition result; - result.m_uid = uid; - result.m_kind = Absence; - result.u.absence.prototype = prototype; - return result; - } - - static PropertyCondition absence( - VM& vm, JSCell* owner, UniquedStringImpl* uid, JSObject* prototype) - { - if (owner) - vm.heap.writeBarrier(owner); - return absenceWithoutBarrier(uid, prototype); - } - - static PropertyCondition absenceOfSetterWithoutBarrier( - UniquedStringImpl* uid, JSObject* prototype) - { - PropertyCondition result; - result.m_uid = uid; - result.m_kind = AbsenceOfSetter; - result.u.absence.prototype = prototype; - return result; - } - - static PropertyCondition absenceOfSetter( - VM& vm, JSCell* owner, UniquedStringImpl* uid, JSObject* prototype) - { - if (owner) - vm.heap.writeBarrier(owner); - return absenceOfSetterWithoutBarrier(uid, prototype); - } - - static PropertyCondition equivalenceWithoutBarrier( - UniquedStringImpl* uid, JSValue value) - { - PropertyCondition result; - result.m_uid = uid; - result.m_kind = Equivalence; - result.u.equivalence.value = JSValue::encode(value); - return result; - } - - static PropertyCondition equivalence( - VM& vm, JSCell* owner, UniquedStringImpl* uid, JSValue value) - { - if (value.isCell() && owner) - vm.heap.writeBarrier(owner); - return equivalenceWithoutBarrier(uid, value); - } - - bool operator!() const { return !m_uid && m_kind == Presence; }; - - Kind kind() const { return m_kind; } - UniquedStringImpl* uid() const { return m_uid; } - - bool hasOffset() const { return !!*this && m_kind == Presence; }; - PropertyOffset offset() const - { - ASSERT(hasOffset()); - return u.presence.offset; - } - bool hasAttributes() const { return !!*this && m_kind == Presence; }; - unsigned attributes() const - { - ASSERT(hasAttributes()); - return u.presence.attributes; - } - - bool hasPrototype() const { return !!*this && (m_kind == Absence || m_kind == AbsenceOfSetter); } - JSObject* prototype() const - { - ASSERT(hasPrototype()); - return u.absence.prototype; - } - - bool hasRequiredValue() const { return !!*this && m_kind == Equivalence; } - JSValue requiredValue() const - { - ASSERT(hasRequiredValue()); - return JSValue::decode(u.equivalence.value); - } - - void dumpInContext(PrintStream&, DumpContext*) const; - void dump(PrintStream&) const; - - unsigned hash() const - { - unsigned result = WTF::PtrHash<UniquedStringImpl*>::hash(m_uid) + static_cast<unsigned>(m_kind); - switch (m_kind) { - case Presence: - result ^= u.presence.offset; - result ^= u.presence.attributes; - break; - case Absence: - case AbsenceOfSetter: - result ^= WTF::PtrHash<JSObject*>::hash(u.absence.prototype); - break; - case Equivalence: - result ^= EncodedJSValueHash::hash(u.equivalence.value); - break; - } - return result; - } - - bool operator==(const PropertyCondition& other) const - { - if (m_uid != other.m_uid) - return false; - if (m_kind != other.m_kind) - return false; - switch (m_kind) { - case Presence: - return u.presence.offset == other.u.presence.offset - && u.presence.attributes == other.u.presence.attributes; - case Absence: - case AbsenceOfSetter: - return u.absence.prototype == other.u.absence.prototype; - case Equivalence: - return u.equivalence.value == other.u.equivalence.value; - } - RELEASE_ASSERT_NOT_REACHED(); - return false; - } - - bool isHashTableDeletedValue() const - { - return !m_uid && m_kind == Absence; - } - - // Two conditions are compatible if they are identical or if they speak of different uids. If - // false is returned, you have to decide how to resolve the conflict - for example if there is - // a Presence and an Equivalence then in some cases you'll want the more general of the two - // while in other cases you'll want the more specific of the two. This will also return false - // for contradictions, like Presence and Absence on the same uid. By convention, invalid - // conditions aren't compatible with anything. - bool isCompatibleWith(const PropertyCondition& other) const - { - if (!*this || !other) - return false; - return *this == other || uid() != other.uid(); - } - - // Checks if the object's structure claims that the property won't be intercepted. - bool isStillValidAssumingImpurePropertyWatchpoint(Structure*, JSObject* base = nullptr) const; - - // Returns true if we need an impure property watchpoint to ensure validity even if - // isStillValidAccordingToStructure() returned true. - bool validityRequiresImpurePropertyWatchpoint(Structure*) const; - - // Checks if the condition is still valid right now for the given object and structure. - // May conservatively return false, if the object and structure alone don't guarantee the - // condition. This happens for an Absence condition on an object that may have impure - // properties. If the object is not supplied, then a "true" return indicates that checking if - // an object has the given structure guarantees the condition still holds. If an object is - // supplied, then you may need to use some other watchpoints on the object to guarantee the - // condition in addition to the structure check. - bool isStillValid(Structure*, JSObject* base = nullptr) const; - - // In some cases, the condition is not watchable, but could be made watchable by enabling the - // appropriate watchpoint. For example, replacement watchpoints are enabled only when some - // access is cached on the property in some structure. This is mainly to save space for - // dictionary properties or properties that never get very hot. But, it's always safe to - // enable watching, provided that this is called from the main thread. - enum WatchabilityEffort { - // This is the default. It means that we don't change the state of any Structure or - // object, and implies that if the property happens not to be watchable then we don't make - // it watchable. This is mandatory if calling from a JIT thread. This is also somewhat - // preferable when first deciding whether to watch a condition for the first time (i.e. - // not from a watchpoint fire that causes us to see if we should adapt), since a - // watchpoint not being initialized for watching implies that maybe we don't know enough - // yet to make it profitable to watch -- as in, the thing being watched may not have - // stabilized yet. We prefer to only assume that a condition will hold if it has been - // known to hold for a while already. - MakeNoChanges, - - // Do what it takes to ensure that the property can be watched, if doing so has no - // user-observable effect. For now this just means that we will ensure that a property - // replacement watchpoint is enabled if it hadn't been enabled already. Do not use this - // from JIT threads, since the act of enabling watchpoints is not thread-safe. - EnsureWatchability - }; - - // This means that it's still valid and we could enforce validity by setting a transition - // watchpoint on the structure and possibly an impure property watchpoint. - bool isWatchableAssumingImpurePropertyWatchpoint( - Structure*, JSObject* base = nullptr, WatchabilityEffort = MakeNoChanges) const; - - // This means that it's still valid and we could enforce validity by setting a transition - // watchpoint on the structure. - bool isWatchable( - Structure*, JSObject* base = nullptr, WatchabilityEffort = MakeNoChanges) const; - - bool watchingRequiresStructureTransitionWatchpoint() const - { - // Currently, this is required for all of our conditions. - return !!*this; - } - bool watchingRequiresReplacementWatchpoint() const - { - return !!*this && m_kind == Equivalence; - } - - // This means that the objects involved in this are still live. - bool isStillLive() const; - - void validateReferences(const TrackedReferences&) const; - - static bool isValidValueForAttributes(JSValue value, unsigned attributes); - - bool isValidValueForPresence(JSValue) const; - - PropertyCondition attemptToMakeEquivalenceWithoutBarrier(JSObject* base) const; - -private: - bool isWatchableWhenValid(Structure*, WatchabilityEffort) const; - - UniquedStringImpl* m_uid; - Kind m_kind; - union { - struct { - PropertyOffset offset; - unsigned attributes; - } presence; - struct { - JSObject* prototype; - } absence; - struct { - EncodedJSValue value; - } equivalence; - } u; -}; - -struct PropertyConditionHash { - static unsigned hash(const PropertyCondition& key) { return key.hash(); } - static bool equal( - const PropertyCondition& a, const PropertyCondition& b) - { - return a == b; - } - static const bool safeToCompareToEmptyOrDeleted = true; -}; - -} // namespace JSC - -namespace WTF { - -void printInternal(PrintStream&, JSC::PropertyCondition::Kind); - -template<typename T> struct DefaultHash; -template<> struct DefaultHash<JSC::PropertyCondition> { - typedef JSC::PropertyConditionHash Hash; -}; - -template<typename T> struct HashTraits; -template<> struct HashTraits<JSC::PropertyCondition> : SimpleClassHashTraits<JSC::PropertyCondition> { }; - -} // namespace WTF - -#endif // PropertyCondition_h - diff --git a/Source/JavaScriptCore/bytecode/PutByIdStatus.cpp b/Source/JavaScriptCore/bytecode/PutByIdStatus.cpp index cc5da3b19..17cf70897 100644 --- a/Source/JavaScriptCore/bytecode/PutByIdStatus.cpp +++ b/Source/JavaScriptCore/bytecode/PutByIdStatus.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2015 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,404 +26,207 @@ #include "config.h" #include "PutByIdStatus.h" -#include "AccessorCallJITStubRoutine.h" #include "CodeBlock.h" -#include "ComplexGetStatus.h" #include "LLIntData.h" #include "LowLevelInterpreter.h" -#include "JSCInlines.h" -#include "PolymorphicPutByIdList.h" +#include "Operations.h" #include "Structure.h" #include "StructureChain.h" -#include <wtf/ListDump.h> namespace JSC { -bool PutByIdStatus::appendVariant(const PutByIdVariant& variant) -{ - for (unsigned i = 0; i < m_variants.size(); ++i) { - if (m_variants[i].attemptToMerge(variant)) - return true; - } - for (unsigned i = 0; i < m_variants.size(); ++i) { - if (m_variants[i].oldStructure().overlaps(variant.oldStructure())) - return false; - } - m_variants.append(variant); - return true; -} - -#if ENABLE(DFG_JIT) -bool PutByIdStatus::hasExitSite(const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, unsigned bytecodeIndex) -{ - return profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCache)) - || profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadConstantCache)); - -} -#endif - -PutByIdStatus PutByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned bytecodeIndex, UniquedStringImpl* uid) +PutByIdStatus PutByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned bytecodeIndex, StringImpl* uid) { UNUSED_PARAM(profiledBlock); UNUSED_PARAM(bytecodeIndex); UNUSED_PARAM(uid); +#if ENABLE(LLINT) Instruction* instruction = profiledBlock->instructions().begin() + bytecodeIndex; Structure* structure = instruction[4].u.structure.get(); if (!structure) - return PutByIdStatus(NoInformation); + return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset); - if (instruction[0].u.opcode == LLInt::getOpcode(op_put_by_id) - || instruction[0].u.opcode == LLInt::getOpcode(op_put_by_id_out_of_line)) { - PropertyOffset offset = structure->getConcurrently(uid); + if (instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id) + || instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id_out_of_line)) { + PropertyOffset offset = structure->getConcurrently(*profiledBlock->vm(), uid); if (!isValidOffset(offset)) - return PutByIdStatus(NoInformation); + return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset); - return PutByIdVariant::replace(structure, offset); + return PutByIdStatus(SimpleReplace, structure, 0, 0, offset); } ASSERT(structure->transitionWatchpointSetHasBeenInvalidated()); - ASSERT(instruction[0].u.opcode == LLInt::getOpcode(op_put_by_id_transition_direct) - || instruction[0].u.opcode == LLInt::getOpcode(op_put_by_id_transition_normal) - || instruction[0].u.opcode == LLInt::getOpcode(op_put_by_id_transition_direct_out_of_line) - || instruction[0].u.opcode == LLInt::getOpcode(op_put_by_id_transition_normal_out_of_line)); + ASSERT(instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id_transition_direct) + || instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id_transition_normal) + || instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id_transition_direct_out_of_line) + || instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id_transition_normal_out_of_line)); Structure* newStructure = instruction[6].u.structure.get(); + StructureChain* chain = instruction[7].u.structureChain.get(); + ASSERT(newStructure); + ASSERT(chain); - PropertyOffset offset = newStructure->getConcurrently(uid); + PropertyOffset offset = newStructure->getConcurrently(*profiledBlock->vm(), uid); if (!isValidOffset(offset)) - return PutByIdStatus(NoInformation); + return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset); - ObjectPropertyConditionSet conditionSet; - if (instruction[0].u.opcode == LLInt::getOpcode(op_put_by_id_transition_normal) - || instruction[0].u.opcode == LLInt::getOpcode(op_put_by_id_transition_normal_out_of_line)) { - conditionSet = - generateConditionsForPropertySetterMissConcurrently( - *profiledBlock->vm(), profiledBlock->globalObject(), structure, uid); - if (!conditionSet.isValid()) - return PutByIdStatus(NoInformation); - } - - return PutByIdVariant::transition(structure, newStructure, conditionSet, offset); + return PutByIdStatus( + SimpleTransition, structure, newStructure, + chain ? adoptRef(new IntendedStructureChain(profiledBlock, structure, chain)) : 0, + offset); +#else + return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset); +#endif } -PutByIdStatus PutByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& map, unsigned bytecodeIndex, UniquedStringImpl* uid) +PutByIdStatus PutByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& map, unsigned bytecodeIndex, StringImpl* uid) { ConcurrentJITLocker locker(profiledBlock->m_lock); UNUSED_PARAM(profiledBlock); UNUSED_PARAM(bytecodeIndex); UNUSED_PARAM(uid); -#if ENABLE(DFG_JIT) - if (hasExitSite(locker, profiledBlock, bytecodeIndex)) - return PutByIdStatus(TakesSlowPath); +#if ENABLE(JIT) + if (profiledBlock->likelyToTakeSlowCase(bytecodeIndex)) + return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset); StructureStubInfo* stubInfo = map.get(CodeOrigin(bytecodeIndex)); - PutByIdStatus result = computeForStubInfo( - locker, profiledBlock, stubInfo, uid, - CallLinkStatus::computeExitSiteData(locker, profiledBlock, bytecodeIndex)); - if (!result) + if (!stubInfo || !stubInfo->seen) return computeFromLLInt(profiledBlock, bytecodeIndex, uid); - return result; -#else // ENABLE(JIT) - UNUSED_PARAM(map); - return PutByIdStatus(NoInformation); -#endif // ENABLE(JIT) -} + if (stubInfo->resetByGC) + return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset); -#if ENABLE(JIT) -PutByIdStatus PutByIdStatus::computeForStubInfo( - const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, - UniquedStringImpl* uid, CallLinkStatus::ExitSiteData callExitSiteData) -{ - if (!stubInfo) - return PutByIdStatus(); - - if (stubInfo->tookSlowPath) - return PutByIdStatus(TakesSlowPath); - - if (!stubInfo->seen) - return PutByIdStatus(); - switch (stubInfo->accessType) { case access_unset: // If the JIT saw it but didn't optimize it, then assume that this takes slow path. - return PutByIdStatus(TakesSlowPath); + return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset); case access_put_by_id_replace: { PropertyOffset offset = - stubInfo->u.putByIdReplace.baseObjectStructure->getConcurrently(uid); + stubInfo->u.putByIdReplace.baseObjectStructure->getConcurrently( + *profiledBlock->vm(), uid); if (isValidOffset(offset)) { - return PutByIdVariant::replace( - stubInfo->u.putByIdReplace.baseObjectStructure.get(), offset); + return PutByIdStatus( + SimpleReplace, + stubInfo->u.putByIdReplace.baseObjectStructure.get(), + 0, 0, + offset); } - return PutByIdStatus(TakesSlowPath); + return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset); } case access_put_by_id_transition_normal: case access_put_by_id_transition_direct: { ASSERT(stubInfo->u.putByIdTransition.previousStructure->transitionWatchpointSetHasBeenInvalidated()); PropertyOffset offset = - stubInfo->u.putByIdTransition.structure->getConcurrently(uid); + stubInfo->u.putByIdTransition.structure->getConcurrently( + *profiledBlock->vm(), uid); if (isValidOffset(offset)) { - ObjectPropertyConditionSet conditionSet = ObjectPropertyConditionSet::fromRawPointer( - stubInfo->u.putByIdTransition.rawConditionSet); - if (!conditionSet.structuresEnsureValidity()) - return PutByIdStatus(TakesSlowPath); - return PutByIdVariant::transition( + return PutByIdStatus( + SimpleTransition, stubInfo->u.putByIdTransition.previousStructure.get(), stubInfo->u.putByIdTransition.structure.get(), - conditionSet, offset); + stubInfo->u.putByIdTransition.chain ? adoptRef(new IntendedStructureChain( + profiledBlock, stubInfo->u.putByIdTransition.previousStructure.get(), + stubInfo->u.putByIdTransition.chain.get())) : 0, + offset); } - return PutByIdStatus(TakesSlowPath); - } - - case access_put_by_id_list: { - PolymorphicPutByIdList* list = stubInfo->u.putByIdList.list; - - PutByIdStatus result; - result.m_state = Simple; - - State slowPathState = TakesSlowPath; - for (unsigned i = 0; i < list->size(); ++i) { - const PutByIdAccess& access = list->at(i); - - switch (access.type()) { - case PutByIdAccess::Setter: - case PutByIdAccess::CustomSetter: - slowPathState = MakesCalls; - break; - default: - break; - } - } - - for (unsigned i = 0; i < list->size(); ++i) { - const PutByIdAccess& access = list->at(i); - - PutByIdVariant variant; - - switch (access.type()) { - case PutByIdAccess::Replace: { - Structure* structure = access.structure(); - PropertyOffset offset = structure->getConcurrently(uid); - if (!isValidOffset(offset)) - return PutByIdStatus(slowPathState); - variant = PutByIdVariant::replace(structure, offset); - break; - } - - case PutByIdAccess::Transition: { - PropertyOffset offset = - access.newStructure()->getConcurrently(uid); - if (!isValidOffset(offset)) - return PutByIdStatus(slowPathState); - ObjectPropertyConditionSet conditionSet = access.conditionSet(); - if (!conditionSet.structuresEnsureValidity()) - return PutByIdStatus(slowPathState); - variant = PutByIdVariant::transition( - access.oldStructure(), access.newStructure(), conditionSet, offset); - break; - } - - case PutByIdAccess::Setter: { - Structure* structure = access.structure(); - - ComplexGetStatus complexGetStatus = ComplexGetStatus::computeFor( - structure, access.conditionSet(), uid); - - switch (complexGetStatus.kind()) { - case ComplexGetStatus::ShouldSkip: - continue; - - case ComplexGetStatus::TakesSlowPath: - return PutByIdStatus(slowPathState); - - case ComplexGetStatus::Inlineable: { - AccessorCallJITStubRoutine* stub = static_cast<AccessorCallJITStubRoutine*>( - access.stubRoutine()); - std::unique_ptr<CallLinkStatus> callLinkStatus = - std::make_unique<CallLinkStatus>( - CallLinkStatus::computeFor( - locker, profiledBlock, *stub->m_callLinkInfo, callExitSiteData)); - - variant = PutByIdVariant::setter( - structure, complexGetStatus.offset(), complexGetStatus.conditionSet(), - WTF::move(callLinkStatus)); - } } - break; - } - - case PutByIdAccess::CustomSetter: - return PutByIdStatus(MakesCalls); - - default: - return PutByIdStatus(slowPathState); - } - - if (!result.appendVariant(variant)) - return PutByIdStatus(slowPathState); - } - - return result; + return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset); } default: - return PutByIdStatus(TakesSlowPath); - } -} -#endif - -PutByIdStatus PutByIdStatus::computeFor(CodeBlock* baselineBlock, CodeBlock* dfgBlock, StubInfoMap& baselineMap, StubInfoMap& dfgMap, CodeOrigin codeOrigin, UniquedStringImpl* uid) -{ -#if ENABLE(DFG_JIT) - if (dfgBlock) { - CallLinkStatus::ExitSiteData exitSiteData; - { - ConcurrentJITLocker locker(baselineBlock->m_lock); - if (hasExitSite(locker, baselineBlock, codeOrigin.bytecodeIndex)) - return PutByIdStatus(TakesSlowPath); - exitSiteData = CallLinkStatus::computeExitSiteData( - locker, baselineBlock, codeOrigin.bytecodeIndex); - } - - PutByIdStatus result; - { - ConcurrentJITLocker locker(dfgBlock->m_lock); - result = computeForStubInfo( - locker, dfgBlock, dfgMap.get(codeOrigin), uid, exitSiteData); - } - - // We use TakesSlowPath in some cases where the stub was unset. That's weird and - // it would be better not to do that. But it means that we have to defend - // ourselves here. - if (result.isSimple()) - return result; + // FIXME: We should handle polymorphic PutById. We probably have some interesting things + // we could do about it. + return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset); } -#else - UNUSED_PARAM(dfgBlock); - UNUSED_PARAM(dfgMap); -#endif - - return computeFor(baselineBlock, baselineMap, codeOrigin.bytecodeIndex, uid); +#else // ENABLE(JIT) + UNUSED_PARAM(map); + return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset); +#endif // ENABLE(JIT) } -PutByIdStatus PutByIdStatus::computeFor(JSGlobalObject* globalObject, const StructureSet& set, UniquedStringImpl* uid, bool isDirect) +PutByIdStatus PutByIdStatus::computeFor(VM& vm, JSGlobalObject* globalObject, Structure* structure, StringImpl* uid, bool isDirect) { - if (parseIndex(*uid)) + if (toUInt32FromStringImpl(uid) != PropertyName::NotAnIndex) return PutByIdStatus(TakesSlowPath); - if (set.isEmpty()) - return PutByIdStatus(); - - PutByIdStatus result; - result.m_state = Simple; - for (unsigned i = 0; i < set.size(); ++i) { - Structure* structure = set[i]; - - if (structure->typeInfo().overridesGetOwnPropertySlot() && structure->typeInfo().type() != GlobalObjectType) - return PutByIdStatus(TakesSlowPath); - - if (!structure->propertyAccessesAreCacheable()) - return PutByIdStatus(TakesSlowPath); + if (!structure) + return PutByIdStatus(TakesSlowPath); - unsigned attributes; - PropertyOffset offset = structure->getConcurrently(uid, attributes); - if (isValidOffset(offset)) { - if (attributes & CustomAccessor) - return PutByIdStatus(MakesCalls); + if (structure->typeInfo().overridesGetOwnPropertySlot() && structure->typeInfo().type() != GlobalObjectType) + return PutByIdStatus(TakesSlowPath); - if (attributes & (Accessor | ReadOnly)) - return PutByIdStatus(TakesSlowPath); - - WatchpointSet* replaceSet = structure->propertyReplacementWatchpointSet(offset); - if (!replaceSet || replaceSet->isStillValid()) { - // When this executes, it'll create, and fire, this replacement watchpoint set. - // That means that this has probably never executed or that something fishy is - // going on. Also, we cannot create or fire the watchpoint set from the concurrent - // JIT thread, so even if we wanted to do this, we'd need to have a lazy thingy. - // So, better leave this alone and take slow path. - return PutByIdStatus(TakesSlowPath); - } - - if (!result.appendVariant(PutByIdVariant::replace(structure, offset))) - return PutByIdStatus(TakesSlowPath); - continue; - } - - // Our hypothesis is that we're doing a transition. Before we prove that this is really - // true, we want to do some sanity checks. + if (!structure->propertyAccessesAreCacheable()) + return PutByIdStatus(TakesSlowPath); - // Don't cache put transitions on dictionaries. - if (structure->isDictionary()) + unsigned attributes; + JSCell* specificValue; + PropertyOffset offset = structure->getConcurrently(vm, uid, attributes, specificValue); + if (isValidOffset(offset)) { + if (attributes & (Accessor | ReadOnly)) return PutByIdStatus(TakesSlowPath); - - // If the structure corresponds to something that isn't an object, then give up, since - // we don't want to be adding properties to strings. - if (!structure->typeInfo().isObject()) + if (specificValue) { + // We need the PutById slow path to verify that we're storing the right value into + // the specialized slot. return PutByIdStatus(TakesSlowPath); - - ObjectPropertyConditionSet conditionSet; - if (!isDirect) { - conditionSet = generateConditionsForPropertySetterMissConcurrently( - globalObject->vm(), globalObject, structure, uid); - if (!conditionSet.isValid()) - return PutByIdStatus(TakesSlowPath); } - - // We only optimize if there is already a structure that the transition is cached to. - Structure* transition = Structure::addPropertyTransitionToExistingStructureConcurrently(structure, uid, 0, offset); - if (!transition) - return PutByIdStatus(TakesSlowPath); - ASSERT(isValidOffset(offset)); - - bool didAppend = result.appendVariant( - PutByIdVariant::transition(structure, transition, conditionSet, offset)); - if (!didAppend) - return PutByIdStatus(TakesSlowPath); + return PutByIdStatus(SimpleReplace, structure, 0, 0, offset); } - return result; -} - -bool PutByIdStatus::makesCalls() const -{ - if (m_state == MakesCalls) - return true; + // Our hypothesis is that we're doing a transition. Before we prove that this is really + // true, we want to do some sanity checks. - if (m_state != Simple) - return false; - - for (unsigned i = m_variants.size(); i--;) { - if (m_variants[i].makesCalls()) - return true; - } - - return false; -} + // Don't cache put transitions on dictionaries. + if (structure->isDictionary()) + return PutByIdStatus(TakesSlowPath); -void PutByIdStatus::dump(PrintStream& out) const -{ - switch (m_state) { - case NoInformation: - out.print("(NoInformation)"); - return; + // If the structure corresponds to something that isn't an object, then give up, since + // we don't want to be adding properties to strings. + if (structure->typeInfo().type() == StringType) + return PutByIdStatus(TakesSlowPath); + + RefPtr<IntendedStructureChain> chain; + if (!isDirect) { + chain = adoptRef(new IntendedStructureChain(globalObject, structure)); - case Simple: - out.print("(", listDump(m_variants), ")"); - return; + // If the prototype chain has setters or read-only properties, then give up. + if (chain->mayInterceptStoreTo(vm, uid)) + return PutByIdStatus(TakesSlowPath); - case TakesSlowPath: - out.print("(TakesSlowPath)"); - return; - case MakesCalls: - out.print("(MakesCalls)"); - return; + // If the prototype chain hasn't been normalized (i.e. there are proxies or dictionaries) + // then give up. The dictionary case would only happen if this structure has not been + // used in an optimized put_by_id transition. And really the only reason why we would + // bail here is that I don't really feel like having the optimizing JIT go and flatten + // dictionaries if we have evidence to suggest that those objects were never used as + // prototypes in a cacheable prototype access - i.e. there's a good chance that some of + // the other checks below will fail. + if (!chain->isNormalized()) + return PutByIdStatus(TakesSlowPath); } - RELEASE_ASSERT_NOT_REACHED(); + // We only optimize if there is already a structure that the transition is cached to. + // Among other things, this allows us to guard against a transition with a specific + // value. + // + // - If we're storing a value that could be specific: this would only be a problem if + // the existing transition did have a specific value already, since if it didn't, + // then we would behave "as if" we were not storing a specific value. If it did + // have a specific value, then we'll know - the fact that we pass 0 for + // specificValue will tell us. + // + // - If we're not storing a value that could be specific: again, this would only be a + // problem if the existing transition did have a specific value, which we check for + // by passing 0 for the specificValue. + Structure* transition = Structure::addPropertyTransitionToExistingStructureConcurrently(structure, uid, 0, 0, offset); + if (!transition) + return PutByIdStatus(TakesSlowPath); // This occurs in bizarre cases only. See above. + ASSERT(!transition->transitionDidInvolveSpecificValue()); + ASSERT(isValidOffset(offset)); + + return PutByIdStatus(SimpleTransition, structure, transition, chain.release(), offset); } } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/PutByIdStatus.h b/Source/JavaScriptCore/bytecode/PutByIdStatus.h index 652ccc18a..c0a1bc35c 100644 --- a/Source/JavaScriptCore/bytecode/PutByIdStatus.h +++ b/Source/JavaScriptCore/bytecode/PutByIdStatus.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2013, 2014 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,9 +26,8 @@ #ifndef PutByIdStatus_h #define PutByIdStatus_h -#include "CallLinkStatus.h" -#include "ExitingJITType.h" -#include "PutByIdVariant.h" +#include "IntendedStructureChain.h" +#include "PropertyOffset.h" #include "StructureStubInfo.h" #include <wtf/text/StringImpl.h> @@ -45,66 +44,77 @@ public: enum State { // It's uncached so we have no information. NoInformation, - // It's cached as a simple store of some kind. - Simple, + // It's cached as a direct store into an object property for cases where the object + // already has the property. + SimpleReplace, + // It's cached as a transition from one structure that lacks the property to one that + // includes the property, and a direct store to this new property. + SimpleTransition, // It's known to often take slow path. - TakesSlowPath, - // It's known to take paths that make calls. - MakesCalls + TakesSlowPath }; PutByIdStatus() : m_state(NoInformation) + , m_oldStructure(0) + , m_newStructure(0) + , m_structureChain(0) + , m_offset(invalidOffset) { } explicit PutByIdStatus(State state) : m_state(state) + , m_oldStructure(0) + , m_newStructure(0) + , m_structureChain(0) + , m_offset(invalidOffset) { - ASSERT(m_state == NoInformation || m_state == TakesSlowPath || m_state == MakesCalls); + ASSERT(m_state == NoInformation || m_state == TakesSlowPath); } - PutByIdStatus(const PutByIdVariant& variant) - : m_state(Simple) + PutByIdStatus( + State state, + Structure* oldStructure, + Structure* newStructure, + PassRefPtr<IntendedStructureChain> structureChain, + PropertyOffset offset) + : m_state(state) + , m_oldStructure(oldStructure) + , m_newStructure(newStructure) + , m_structureChain(structureChain) + , m_offset(offset) { - m_variants.append(variant); + ASSERT((m_state == NoInformation || m_state == TakesSlowPath) == !m_oldStructure); + ASSERT((m_state != SimpleTransition) == !m_newStructure); + ASSERT(!((m_state != SimpleTransition) && m_structureChain)); + ASSERT((m_state == NoInformation || m_state == TakesSlowPath) == (m_offset == invalidOffset)); } - static PutByIdStatus computeFor(CodeBlock*, StubInfoMap&, unsigned bytecodeIndex, UniquedStringImpl* uid); - static PutByIdStatus computeFor(JSGlobalObject*, const StructureSet&, UniquedStringImpl* uid, bool isDirect); - - static PutByIdStatus computeFor(CodeBlock* baselineBlock, CodeBlock* dfgBlock, StubInfoMap& baselineMap, StubInfoMap& dfgMap, CodeOrigin, UniquedStringImpl* uid); + static PutByIdStatus computeFor(CodeBlock*, StubInfoMap&, unsigned bytecodeIndex, StringImpl* uid); + static PutByIdStatus computeFor(VM&, JSGlobalObject*, Structure*, StringImpl* uid, bool isDirect); State state() const { return m_state; } bool isSet() const { return m_state != NoInformation; } bool operator!() const { return m_state == NoInformation; } - bool isSimple() const { return m_state == Simple; } - bool takesSlowPath() const { return m_state == TakesSlowPath || m_state == MakesCalls; } - bool makesCalls() const; + bool isSimpleReplace() const { return m_state == SimpleReplace; } + bool isSimpleTransition() const { return m_state == SimpleTransition; } + bool takesSlowPath() const { return m_state == TakesSlowPath; } - size_t numVariants() const { return m_variants.size(); } - const Vector<PutByIdVariant, 1>& variants() const { return m_variants; } - const PutByIdVariant& at(size_t index) const { return m_variants[index]; } - const PutByIdVariant& operator[](size_t index) const { return at(index); } - - void dump(PrintStream&) const; + Structure* oldStructure() const { return m_oldStructure; } + Structure* newStructure() const { return m_newStructure; } + IntendedStructureChain* structureChain() const { return m_structureChain.get(); } + PropertyOffset offset() const { return m_offset; } private: -#if ENABLE(DFG_JIT) - static bool hasExitSite(const ConcurrentJITLocker&, CodeBlock*, unsigned bytecodeIndex); -#endif -#if ENABLE(JIT) - static PutByIdStatus computeForStubInfo( - const ConcurrentJITLocker&, CodeBlock*, StructureStubInfo*, UniquedStringImpl* uid, - CallLinkStatus::ExitSiteData); -#endif - static PutByIdStatus computeFromLLInt(CodeBlock*, unsigned bytecodeIndex, UniquedStringImpl* uid); - - bool appendVariant(const PutByIdVariant&); + static PutByIdStatus computeFromLLInt(CodeBlock*, unsigned bytecodeIndex, StringImpl* uid); State m_state; - Vector<PutByIdVariant, 1> m_variants; + Structure* m_oldStructure; + Structure* m_newStructure; + RefPtr<IntendedStructureChain> m_structureChain; + PropertyOffset m_offset; }; } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/PutByIdVariant.cpp b/Source/JavaScriptCore/bytecode/PutByIdVariant.cpp deleted file mode 100644 index e1b94ef13..000000000 --- a/Source/JavaScriptCore/bytecode/PutByIdVariant.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (C) 2014, 2015 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. - */ - -#include "config.h" -#include "PutByIdVariant.h" - -#include "CallLinkStatus.h" -#include "JSCInlines.h" -#include <wtf/ListDump.h> - -namespace JSC { - -PutByIdVariant::PutByIdVariant(const PutByIdVariant& other) - : PutByIdVariant() -{ - *this = other; -} - -PutByIdVariant& PutByIdVariant::operator=(const PutByIdVariant& other) -{ - m_kind = other.m_kind; - m_oldStructure = other.m_oldStructure; - m_newStructure = other.m_newStructure; - m_conditionSet = other.m_conditionSet; - m_offset = other.m_offset; - if (other.m_callLinkStatus) - m_callLinkStatus = std::make_unique<CallLinkStatus>(*other.m_callLinkStatus); - else - m_callLinkStatus = nullptr; - return *this; -} - -PutByIdVariant PutByIdVariant::replace(const StructureSet& structure, PropertyOffset offset) -{ - PutByIdVariant result; - result.m_kind = Replace; - result.m_oldStructure = structure; - result.m_offset = offset; - return result; -} - -PutByIdVariant PutByIdVariant::transition( - const StructureSet& oldStructure, Structure* newStructure, - const ObjectPropertyConditionSet& conditionSet, PropertyOffset offset) -{ - PutByIdVariant result; - result.m_kind = Transition; - result.m_oldStructure = oldStructure; - result.m_newStructure = newStructure; - result.m_conditionSet = conditionSet; - result.m_offset = offset; - return result; -} - -PutByIdVariant PutByIdVariant::setter( - const StructureSet& structure, PropertyOffset offset, - const ObjectPropertyConditionSet& conditionSet, - std::unique_ptr<CallLinkStatus> callLinkStatus) -{ - PutByIdVariant result; - result.m_kind = Setter; - result.m_oldStructure = structure; - result.m_conditionSet = conditionSet; - result.m_offset = offset; - result.m_callLinkStatus = WTF::move(callLinkStatus); - return result; -} - -Structure* PutByIdVariant::oldStructureForTransition() const -{ - ASSERT(kind() == Transition); - ASSERT(m_oldStructure.size() <= 2); - for (unsigned i = m_oldStructure.size(); i--;) { - Structure* structure = m_oldStructure[i]; - if (structure != m_newStructure) - return structure; - } - RELEASE_ASSERT_NOT_REACHED(); - - return nullptr; -} - -bool PutByIdVariant::writesStructures() const -{ - switch (kind()) { - case Transition: - case Setter: - return true; - default: - return false; - } -} - -bool PutByIdVariant::reallocatesStorage() const -{ - switch (kind()) { - case Transition: - return oldStructureForTransition()->outOfLineCapacity() != newStructure()->outOfLineCapacity(); - case Setter: - return true; - default: - return false; - } -} - -bool PutByIdVariant::makesCalls() const -{ - return kind() == Setter; -} - -bool PutByIdVariant::attemptToMerge(const PutByIdVariant& other) -{ - if (m_offset != other.m_offset) - return false; - - switch (m_kind) { - case Replace: - switch (other.m_kind) { - case Replace: { - ASSERT(m_conditionSet.isEmpty()); - ASSERT(other.m_conditionSet.isEmpty()); - - m_oldStructure.merge(other.m_oldStructure); - return true; - } - - case Transition: { - PutByIdVariant newVariant = other; - if (newVariant.attemptToMergeTransitionWithReplace(*this)) { - *this = newVariant; - return true; - } - return false; - } - - default: - return false; - } - - case Transition: - switch (other.m_kind) { - case Replace: - return attemptToMergeTransitionWithReplace(other); - - default: - return false; - } - - default: - return false; - } -} - -bool PutByIdVariant::attemptToMergeTransitionWithReplace(const PutByIdVariant& replace) -{ - ASSERT(m_kind == Transition); - ASSERT(replace.m_kind == Replace); - ASSERT(m_offset == replace.m_offset); - ASSERT(!replace.writesStructures()); - ASSERT(!replace.reallocatesStorage()); - ASSERT(replace.conditionSet().isEmpty()); - - // This sort of merging only works when we have one path along which we add a new field which - // transitions to structure S while the other path was already on structure S. This doesn't - // work if we need to reallocate anything or if the replace path is polymorphic. - - if (reallocatesStorage()) - return false; - - if (replace.m_oldStructure.onlyStructure() != m_newStructure) - return false; - - m_oldStructure.merge(m_newStructure); - return true; -} - -void PutByIdVariant::dump(PrintStream& out) const -{ - dumpInContext(out, 0); -} - -void PutByIdVariant::dumpInContext(PrintStream& out, DumpContext* context) const -{ - switch (kind()) { - case NotSet: - out.print("<empty>"); - return; - - case Replace: - out.print( - "<Replace: ", inContext(structure(), context), ", offset = ", offset(), ">"); - return; - - case Transition: - out.print( - "<Transition: ", inContext(oldStructure(), context), " -> ", - pointerDumpInContext(newStructure(), context), ", [", - inContext(m_conditionSet, context), "], offset = ", offset(), ">"); - return; - - case Setter: - out.print( - "<Setter: ", inContext(structure(), context), ", [", - inContext(m_conditionSet, context), "]"); - out.print(", offset = ", m_offset); - out.print(", call = ", *m_callLinkStatus); - out.print(">"); - return; - } - - RELEASE_ASSERT_NOT_REACHED(); -} - -} // namespace JSC - diff --git a/Source/JavaScriptCore/bytecode/PutByIdVariant.h b/Source/JavaScriptCore/bytecode/PutByIdVariant.h deleted file mode 100644 index 657cdac62..000000000 --- a/Source/JavaScriptCore/bytecode/PutByIdVariant.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2014, 2015 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 PutByIdVariant_h -#define PutByIdVariant_h - -#include "ObjectPropertyConditionSet.h" -#include "PropertyOffset.h" -#include "StructureSet.h" - -namespace JSC { - -class CallLinkStatus; - -class PutByIdVariant { -public: - enum Kind { - NotSet, - Replace, - Transition, - Setter - }; - - PutByIdVariant() - : m_kind(NotSet) - , m_newStructure(nullptr) - , m_offset(invalidOffset) - { - } - - PutByIdVariant(const PutByIdVariant&); - PutByIdVariant& operator=(const PutByIdVariant&); - - static PutByIdVariant replace(const StructureSet&, PropertyOffset); - - static PutByIdVariant transition( - const StructureSet& oldStructure, Structure* newStructure, - const ObjectPropertyConditionSet&, PropertyOffset); - - static PutByIdVariant setter( - const StructureSet&, PropertyOffset, const ObjectPropertyConditionSet&, - std::unique_ptr<CallLinkStatus>); - - Kind kind() const { return m_kind; } - - bool isSet() const { return kind() != NotSet; } - bool operator!() const { return !isSet(); } - - const StructureSet& structure() const - { - ASSERT(kind() == Replace || kind() == Setter); - return m_oldStructure; - } - - const StructureSet& structureSet() const - { - return structure(); - } - - const StructureSet& oldStructure() const - { - ASSERT(kind() == Transition || kind() == Replace || kind() == Setter); - return m_oldStructure; - } - - StructureSet& oldStructure() - { - ASSERT(kind() == Transition || kind() == Replace || kind() == Setter); - return m_oldStructure; - } - - Structure* oldStructureForTransition() const; - - Structure* newStructure() const - { - ASSERT(kind() == Transition); - return m_newStructure; - } - - bool writesStructures() const; - bool reallocatesStorage() const; - bool makesCalls() const; - - const ObjectPropertyConditionSet& conditionSet() const { return m_conditionSet; } - - PropertyOffset offset() const - { - ASSERT(isSet()); - return m_offset; - } - - CallLinkStatus* callLinkStatus() const - { - ASSERT(kind() == Setter); - return m_callLinkStatus.get(); - } - - bool attemptToMerge(const PutByIdVariant& other); - - void dump(PrintStream&) const; - void dumpInContext(PrintStream&, DumpContext*) const; - -private: - bool attemptToMergeTransitionWithReplace(const PutByIdVariant& replace); - - Kind m_kind; - StructureSet m_oldStructure; - Structure* m_newStructure; - ObjectPropertyConditionSet m_conditionSet; - PropertyOffset m_offset; - std::unique_ptr<CallLinkStatus> m_callLinkStatus; -}; - -} // namespace JSC - -#endif // PutByIdVariant_h - diff --git a/Source/JavaScriptCore/bytecode/SamplingTool.cpp b/Source/JavaScriptCore/bytecode/SamplingTool.cpp index f5bf2b72a..d18dbc1ff 100644 --- a/Source/JavaScriptCore/bytecode/SamplingTool.cpp +++ b/Source/JavaScriptCore/bytecode/SamplingTool.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,7 @@ * 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 + * 3. Neither the name of Apple Computer, 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. * @@ -32,7 +32,6 @@ #include "CodeBlock.h" #include "Interpreter.h" #include "Opcode.h" -#include "JSCInlines.h" #if !OS(WINDOWS) #include <unistd.h> @@ -285,7 +284,7 @@ void SamplingTool::doRun() #if ENABLE(CODEBLOCK_SAMPLING) if (CodeBlock* codeBlock = sample.codeBlock()) { - LockHolder locker(m_scriptSampleMapMutex); + MutexLocker locker(m_scriptSampleMapMutex); ScriptSampleRecord* record = m_scopeSampleMap->get(codeBlock->ownerExecutable()); ASSERT(record); record->sample(codeBlock, sample.vPC()); @@ -301,7 +300,7 @@ void SamplingTool::sample() void SamplingTool::notifyOfScope(VM& vm, ScriptExecutable* script) { #if ENABLE(CODEBLOCK_SAMPLING) - LockHolder locker(m_scriptSampleMapMutex); + MutexLocker locker(m_scriptSampleMapMutex); m_scopeSampleMap->set(script, adoptPtr(new ScriptSampleRecord(vm, script))); #else UNUSED_PARAM(vm); diff --git a/Source/JavaScriptCore/bytecode/SamplingTool.h b/Source/JavaScriptCore/bytecode/SamplingTool.h index 18e348377..1dfb8ecca 100644 --- a/Source/JavaScriptCore/bytecode/SamplingTool.h +++ b/Source/JavaScriptCore/bytecode/SamplingTool.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,7 @@ * 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 + * 3. Neither the name of Apple Computer, 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. * @@ -35,7 +35,6 @@ #include <wtf/Assertions.h> #include <wtf/Atomics.h> #include <wtf/HashMap.h> -#include <wtf/Lock.h> #include <wtf/MainThread.h> #include <wtf/Spectrum.h> #include <wtf/Threading.h> @@ -228,7 +227,6 @@ namespace JSC { }; class SamplingTool { - WTF_MAKE_FAST_ALLOCATED; public: friend struct CallRecord; @@ -273,7 +271,7 @@ namespace JSC { , m_sampleCount(0) , m_opcodeSampleCount(0) #if ENABLE(CODEBLOCK_SAMPLING) - , m_scopeSampleMap(std::make_unique<ScriptSampleRecordMap>) + , m_scopeSampleMap(adoptPtr(new ScriptSampleRecordMap)) #endif { memset(m_opcodeSamples, 0, sizeof(m_opcodeSamples)); @@ -339,8 +337,8 @@ namespace JSC { unsigned m_opcodeSamplesInCTIFunctions[numOpcodeIDs]; #if ENABLE(CODEBLOCK_SAMPLING) - Lock m_scriptSampleMapMutex; - std::unique_ptr<ScriptSampleRecordMap> m_scopeSampleMap; + Mutex m_scriptSampleMapMutex; + OwnPtr<ScriptSampleRecordMap> m_scopeSampleMap; #endif }; diff --git a/Source/JavaScriptCore/bytecode/SpecialPointer.cpp b/Source/JavaScriptCore/bytecode/SpecialPointer.cpp index dc5a363b6..7789653f0 100644 --- a/Source/JavaScriptCore/bytecode/SpecialPointer.cpp +++ b/Source/JavaScriptCore/bytecode/SpecialPointer.cpp @@ -28,7 +28,6 @@ #include "CodeBlock.h" #include "JSGlobalObject.h" -#include "JSCInlines.h" namespace JSC { diff --git a/Source/JavaScriptCore/bytecode/SpecialPointer.h b/Source/JavaScriptCore/bytecode/SpecialPointer.h index 64fb23fcf..c18a6e904 100644 --- a/Source/JavaScriptCore/bytecode/SpecialPointer.h +++ b/Source/JavaScriptCore/bytecode/SpecialPointer.h @@ -41,11 +41,6 @@ enum Pointer { }; } // namespace Special -enum class LinkTimeConstant { - DefinePropertyFunction, -}; -const unsigned LinkTimeConstantCount = 1; - inline bool pointerIsFunction(Special::Pointer pointer) { ASSERT_UNUSED(pointer, pointer < Special::TableSize); diff --git a/Source/JavaScriptCore/bytecode/SpeculatedType.cpp b/Source/JavaScriptCore/bytecode/SpeculatedType.cpp index ca9514c01..3917cca0f 100644 --- a/Source/JavaScriptCore/bytecode/SpeculatedType.cpp +++ b/Source/JavaScriptCore/bytecode/SpeculatedType.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,7 @@ * 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 + * 3. Neither the name of Apple Computer, 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. * @@ -29,13 +29,13 @@ #include "config.h" #include "SpeculatedType.h" -#include "DirectArguments.h" +#include "Arguments.h" #include "JSArray.h" #include "JSFunction.h" -#include "JSCInlines.h" -#include "ScopedArguments.h" +#include "Operations.h" #include "StringObject.h" #include "ValueProfile.h" +#include <wtf/BoundsCheckedPointer.h> #include <wtf/StringPrintStream.h> namespace JSC { @@ -127,13 +127,8 @@ void dumpSpeculation(PrintStream& out, SpeculatedType value) else isTop = false; - if (value & SpecDirectArguments) - myOut.print("Directarguments"); - else - isTop = false; - - if (value & SpecScopedArguments) - myOut.print("Scopedarguments"); + if (value & SpecArguments) + myOut.print("Arguments"); else isTop = false; @@ -156,32 +151,18 @@ void dumpSpeculation(PrintStream& out, SpeculatedType value) else isTop = false; } - - if (value & SpecSymbol) - myOut.print("Symbol"); - else - isTop = false; } - if (value == SpecInt32) + if (value & SpecInt32) myOut.print("Int32"); - else { - if (value & SpecBoolInt32) - myOut.print("Boolint32"); - else - isTop = false; - - if (value & SpecNonBoolInt32) - myOut.print("Nonboolint32"); - else - isTop = false; - } + else + isTop = false; if (value & SpecInt52) myOut.print("Int52"); - if ((value & SpecBytecodeDouble) == SpecBytecodeDouble) - myOut.print("Bytecodedouble"); + if ((value & SpecDouble) == SpecDouble) + myOut.print("Double"); else { if (value & SpecInt52AsDouble) myOut.print("Int52asdouble"); @@ -193,15 +174,12 @@ void dumpSpeculation(PrintStream& out, SpeculatedType value) else isTop = false; - if (value & SpecDoublePureNaN) - myOut.print("Doublepurenan"); + if (value & SpecDoubleNaN) + myOut.print("Doublenan"); else isTop = false; } - if (value & SpecDoubleImpureNaN) - out.print("Doubleimpurenan"); - if (value & SpecBoolean) myOut.print("Bool"); else @@ -251,10 +229,8 @@ static const char* speculationToAbbreviatedString(SpeculatedType prediction) return "<Float32array>"; if (isFloat64ArraySpeculation(prediction)) return "<Float64array>"; - if (isDirectArgumentsSpeculation(prediction)) - return "<DirectArguments>"; - if (isScopedArgumentsSpeculation(prediction)) - return "<ScopedArguments>"; + if (isArgumentsSpeculation(prediction)) + return "<Arguments>"; if (isStringObjectSpeculation(prediction)) return "<StringObject>"; if (isStringOrStringObjectSpeculation(prediction)) @@ -263,8 +239,6 @@ static const char* speculationToAbbreviatedString(SpeculatedType prediction) return "<Object>"; if (isCellSpeculation(prediction)) return "<Cell>"; - if (isBoolInt32Speculation(prediction)) - return "<BoolInt32>"; if (isInt32Speculation(prediction)) return "<Int32>"; if (isInt52AsDoubleSpeculation(prediction)) @@ -281,8 +255,6 @@ static const char* speculationToAbbreviatedString(SpeculatedType prediction) return "<Boolean>"; if (isOtherSpeculation(prediction)) return "<Other>"; - if (isMiscSpeculation(prediction)) - return "<Misc>"; return ""; } @@ -328,11 +300,8 @@ SpeculatedType speculationFromClassInfo(const ClassInfo* classInfo) if (classInfo == JSArray::info()) return SpecArray; - if (classInfo == DirectArguments::info()) - return SpecDirectArguments; - - if (classInfo == ScopedArguments::info()) - return SpecScopedArguments; + if (classInfo == Arguments::info()) + return SpecArguments; if (classInfo == StringObject::info()) return SpecStringObject; @@ -353,8 +322,6 @@ SpeculatedType speculationFromStructure(Structure* structure) { if (structure->typeInfo().type() == StringType) return SpecString; - if (structure->typeInfo().type() == SymbolType) - return SpecSymbol; return speculationFromClassInfo(structure->classInfo()); } @@ -362,7 +329,7 @@ SpeculatedType speculationFromCell(JSCell* cell) { if (JSString* string = jsDynamicCast<JSString*>(cell)) { if (const StringImpl* impl = string->tryGetValueImpl()) { - if (impl->isAtomic()) + if (impl->isIdentifier()) return SpecStringIdent; } return SpecStringVar; @@ -374,15 +341,12 @@ SpeculatedType speculationFromValue(JSValue value) { if (value.isEmpty()) return SpecEmpty; - if (value.isInt32()) { - if (value.asInt32() & ~1) - return SpecNonBoolInt32; - return SpecBoolInt32; - } + if (value.isInt32()) + return SpecInt32; if (value.isDouble()) { double number = value.asNumber(); if (number != number) - return SpecDoublePureNaN; + return SpecDoubleNaN; if (value.isMachineInt()) return SpecInt52AsDouble; return SpecNonIntAsDouble; @@ -427,136 +391,5 @@ TypedArrayType typedArrayTypeFromSpeculation(SpeculatedType type) return NotTypedArray; } -SpeculatedType leastUpperBoundOfStrictlyEquivalentSpeculations(SpeculatedType type) -{ - if (type & SpecInteger) - type |= SpecInteger; - if (type & SpecString) - type |= SpecString; - return type; -} - -bool valuesCouldBeEqual(SpeculatedType a, SpeculatedType b) -{ - a = leastUpperBoundOfStrictlyEquivalentSpeculations(a); - b = leastUpperBoundOfStrictlyEquivalentSpeculations(b); - - // Anything could be equal to a string. - if (a & SpecString) - return true; - if (b & SpecString) - return true; - - // If both sides are definitely only objects, then equality is fairly sane. - if (isObjectSpeculation(a) && isObjectSpeculation(b)) - return !!(a & b); - - // If either side could be an object or not, then we could call toString or - // valueOf, which could return anything. - if (a & SpecObject) - return true; - if (b & SpecObject) - return true; - - // Neither side is an object or string, so the world is relatively sane. - return !!(a & b); -} - -SpeculatedType typeOfDoubleSum(SpeculatedType a, SpeculatedType b) -{ - SpeculatedType result = a | b; - // Impure NaN could become pure NaN during addition because addition may clear bits. - if (result & SpecDoubleImpureNaN) - result |= SpecDoublePureNaN; - // Values could overflow, or fractions could become integers. - if (result & SpecDoubleReal) - result |= SpecDoubleReal; - return result; -} - -SpeculatedType typeOfDoubleDifference(SpeculatedType a, SpeculatedType b) -{ - return typeOfDoubleSum(a, b); -} - -SpeculatedType typeOfDoubleProduct(SpeculatedType a, SpeculatedType b) -{ - return typeOfDoubleSum(a, b); -} - -static SpeculatedType polluteDouble(SpeculatedType value) -{ - // Impure NaN could become pure NaN because the operation could clear some bits. - if (value & SpecDoubleImpureNaN) - value |= SpecDoubleNaN; - // Values could overflow, fractions could become integers, or an error could produce - // PureNaN. - if (value & SpecDoubleReal) - value |= SpecDoubleReal | SpecDoublePureNaN; - return value; -} - -SpeculatedType typeOfDoubleQuotient(SpeculatedType a, SpeculatedType b) -{ - return polluteDouble(a | b); -} - -SpeculatedType typeOfDoubleMinMax(SpeculatedType a, SpeculatedType b) -{ - SpeculatedType result = a | b; - // Impure NaN could become pure NaN during addition because addition may clear bits. - if (result & SpecDoubleImpureNaN) - result |= SpecDoublePureNaN; - return result; -} - -SpeculatedType typeOfDoubleNegation(SpeculatedType value) -{ - // Impure NaN could become pure NaN because bits might get cleared. - if (value & SpecDoubleImpureNaN) - value |= SpecDoublePureNaN; - // We could get negative zero, which mixes SpecInt52AsDouble and SpecNotIntAsDouble. - // We could also overflow a large negative int into something that is no longer - // representable as an int. - if (value & SpecDoubleReal) - value |= SpecDoubleReal; - return value; -} - -SpeculatedType typeOfDoubleAbs(SpeculatedType value) -{ - return typeOfDoubleNegation(value); -} - -SpeculatedType typeOfDoubleRounding(SpeculatedType value) -{ - // We might lose bits, which leads to a NaN being purified. - if (value & SpecDoubleImpureNaN) - value |= SpecDoublePureNaN; - // We might lose bits, which leads to a value becoming integer-representable. - if (value & SpecNonIntAsDouble) - value |= SpecInt52AsDouble; - return value; -} - -SpeculatedType typeOfDoublePow(SpeculatedType xValue, SpeculatedType yValue) -{ - // Math.pow() always return NaN if the exponent is NaN, unlike std::pow(). - // We always set a pure NaN in that case. - if (yValue & SpecDoubleNaN) - xValue |= SpecDoublePureNaN; - return polluteDouble(xValue); -} - -SpeculatedType typeOfDoubleBinaryOp(SpeculatedType a, SpeculatedType b) -{ - return polluteDouble(a | b); -} - -SpeculatedType typeOfDoubleUnaryOp(SpeculatedType value) -{ - return polluteDouble(value); -} - } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/SpeculatedType.h b/Source/JavaScriptCore/bytecode/SpeculatedType.h index bd045c3ac..eaf0af37a 100644 --- a/Source/JavaScriptCore/bytecode/SpeculatedType.h +++ b/Source/JavaScriptCore/bytecode/SpeculatedType.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2015 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,7 @@ * 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 + * 3. Neither the name of Apple Computer, 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. * @@ -41,52 +41,44 @@ typedef uint32_t SpeculatedType; static const SpeculatedType SpecNone = 0x00000000; // We don't know anything yet. static const SpeculatedType SpecFinalObject = 0x00000001; // It's definitely a JSFinalObject. static const SpeculatedType SpecArray = 0x00000002; // It's definitely a JSArray. -static const SpeculatedType SpecFunction = 0x00000004; // It's definitely a JSFunction. -static const SpeculatedType SpecInt8Array = 0x00000008; // It's definitely an Int8Array or one of its subclasses. -static const SpeculatedType SpecInt16Array = 0x00000010; // It's definitely an Int16Array or one of its subclasses. -static const SpeculatedType SpecInt32Array = 0x00000020; // It's definitely an Int32Array or one of its subclasses. -static const SpeculatedType SpecUint8Array = 0x00000040; // It's definitely an Uint8Array or one of its subclasses. -static const SpeculatedType SpecUint8ClampedArray = 0x00000080; // It's definitely an Uint8ClampedArray or one of its subclasses. -static const SpeculatedType SpecUint16Array = 0x00000100; // It's definitely an Uint16Array or one of its subclasses. -static const SpeculatedType SpecUint32Array = 0x00000200; // It's definitely an Uint32Array or one of its subclasses. -static const SpeculatedType SpecFloat32Array = 0x00000400; // It's definitely an Uint16Array or one of its subclasses. -static const SpeculatedType SpecFloat64Array = 0x00000800; // It's definitely an Uint16Array or one of its subclasses. +static const SpeculatedType SpecFunction = 0x00000008; // It's definitely a JSFunction or one of its subclasses. +static const SpeculatedType SpecInt8Array = 0x00000010; // It's definitely an Int8Array or one of its subclasses. +static const SpeculatedType SpecInt16Array = 0x00000020; // It's definitely an Int16Array or one of its subclasses. +static const SpeculatedType SpecInt32Array = 0x00000040; // It's definitely an Int32Array or one of its subclasses. +static const SpeculatedType SpecUint8Array = 0x00000080; // It's definitely an Uint8Array or one of its subclasses. +static const SpeculatedType SpecUint8ClampedArray = 0x00000100; // It's definitely an Uint8ClampedArray or one of its subclasses. +static const SpeculatedType SpecUint16Array = 0x00000200; // It's definitely an Uint16Array or one of its subclasses. +static const SpeculatedType SpecUint32Array = 0x00000400; // It's definitely an Uint32Array or one of its subclasses. +static const SpeculatedType SpecFloat32Array = 0x00000800; // It's definitely an Uint16Array or one of its subclasses. +static const SpeculatedType SpecFloat64Array = 0x00001000; // It's definitely an Uint16Array or one of its subclasses. static const SpeculatedType SpecTypedArrayView = SpecInt8Array | SpecInt16Array | SpecInt32Array | SpecUint8Array | SpecUint8ClampedArray | SpecUint16Array | SpecUint32Array | SpecFloat32Array | SpecFloat64Array; -static const SpeculatedType SpecDirectArguments = 0x00001000; // It's definitely a DirectArguments object. -static const SpeculatedType SpecScopedArguments = 0x00002000; // It's definitely a ScopedArguments object. +static const SpeculatedType SpecArguments = 0x00002000; // It's definitely an Arguments object. static const SpeculatedType SpecStringObject = 0x00004000; // It's definitely a StringObject. static const SpeculatedType SpecObjectOther = 0x00008000; // It's definitely an object but not JSFinalObject, JSArray, or JSFunction. static const SpeculatedType SpecObject = 0x0000ffff; // Bitmask used for testing for any kind of object prediction. static const SpeculatedType SpecStringIdent = 0x00010000; // It's definitely a JSString, and it's an identifier. static const SpeculatedType SpecStringVar = 0x00020000; // It's definitely a JSString, and it's not an identifier. static const SpeculatedType SpecString = 0x00030000; // It's definitely a JSString. -static const SpeculatedType SpecSymbol = 0x00040000; // It's definitely a Symbol. -static const SpeculatedType SpecCellOther = 0x00080000; // It's definitely a JSCell but not a subclass of JSObject and definitely not a JSString or a Symbol. FIXME: This shouldn't be part of heap-top or bytecode-top. https://bugs.webkit.org/show_bug.cgi?id=133078 -static const SpeculatedType SpecCell = 0x000fffff; // It's definitely a JSCell. -static const SpeculatedType SpecBoolInt32 = 0x00100000; // It's definitely an Int32 with value 0 or 1. -static const SpeculatedType SpecNonBoolInt32 = 0x00200000; // It's definitely an Int32 with value other than 0 or 1. -static const SpeculatedType SpecInt32 = 0x00300000; // It's definitely an Int32. -static const SpeculatedType SpecInt52 = 0x00400000; // It's definitely an Int52 and we intend it to unbox it. -static const SpeculatedType SpecMachineInt = 0x00700000; // It's something that we can do machine int arithmetic on. -static const SpeculatedType SpecInt52AsDouble = 0x00800000; // It's definitely an Int52 and it's inside a double. -static const SpeculatedType SpecInteger = 0x00f00000; // It's definitely some kind of integer. -static const SpeculatedType SpecNonIntAsDouble = 0x01000000; // It's definitely not an Int52 but it's a real number and it's a double. -static const SpeculatedType SpecDoubleReal = 0x01800000; // It's definitely a non-NaN double. -static const SpeculatedType SpecDoublePureNaN = 0x02000000; // It's definitely a NaN that is sae to tag (i.e. pure). -static const SpeculatedType SpecDoubleImpureNaN = 0x04000000; // It's definitely a NaN that is unsafe to tag (i.e. impure). -static const SpeculatedType SpecDoubleNaN = 0x06000000; // It's definitely some kind of NaN. -static const SpeculatedType SpecBytecodeDouble = 0x03800000; // It's either a non-NaN or a NaN double, but it's definitely not impure NaN. -static const SpeculatedType SpecFullDouble = 0x07800000; // It's either a non-NaN or a NaN double. -static const SpeculatedType SpecBytecodeRealNumber = 0x01b00000; // It's either an Int32 or a DoubleReal. -static const SpeculatedType SpecFullRealNumber = 0x01f00000; // It's either an Int32 or a DoubleReal, or a Int52. -static const SpeculatedType SpecBytecodeNumber = 0x03b00000; // It's either an Int32 or a Double, and the Double cannot be an impure NaN. -static const SpeculatedType SpecFullNumber = 0x07f00000; // It's either an Int32, Int52, or a Double, and the Double can be impure NaN. +static const SpeculatedType SpecCellOther = 0x00040000; // It's definitely a JSCell but not a subclass of JSObject and definitely not a JSString. +static const SpeculatedType SpecCell = 0x0007ffff; // It's definitely a JSCell. +static const SpeculatedType SpecInt32 = 0x00800000; // It's definitely an Int32. +static const SpeculatedType SpecInt52 = 0x01000000; // It's definitely an Int52 and we intend it to unbox it. +static const SpeculatedType SpecMachineInt = 0x01800000; // It's something that we can do machine int arithmetic on. +static const SpeculatedType SpecInt52AsDouble = 0x02000000; // It's definitely an Int52 and it's inside a double. +static const SpeculatedType SpecInteger = 0x03800000; // It's definitely some kind of integer. +static const SpeculatedType SpecNonIntAsDouble = 0x04000000; // It's definitely not an Int52 but it's a real number and it's a double. +static const SpeculatedType SpecDoubleReal = 0x06000000; // It's definitely a non-NaN double. +static const SpeculatedType SpecDoubleNaN = 0x08000000; // It's definitely a NaN. +static const SpeculatedType SpecDouble = 0x0e000000; // It's either a non-NaN or a NaN double. +static const SpeculatedType SpecBytecodeRealNumber = 0x06800000; // It's either an Int32 or a DoubleReal. +static const SpeculatedType SpecFullRealNumber = 0x07800000; // It's either an Int32 or a DoubleReal, or a Int52. +static const SpeculatedType SpecBytecodeNumber = 0x0e800000; // It's either an Int32 or a Double. +static const SpeculatedType SpecFullNumber = 0x0f800000; // It's either an Int32, Int52, or a Double. static const SpeculatedType SpecBoolean = 0x10000000; // It's definitely a Boolean. -static const SpeculatedType SpecOther = 0x20000000; // It's definitely either Null or Undefined. -static const SpeculatedType SpecMisc = 0x30000000; // It's definitely either a boolean, Null, or Undefined. -static const SpeculatedType SpecHeapTop = 0x3bbfffff; // It can be any of the above, except for SpecInt52. +static const SpeculatedType SpecOther = 0x20000000; // It's definitely none of the above. +static const SpeculatedType SpecHeapTop = 0x3effffff; // It can be any of the above, except for SpecInt52. static const SpeculatedType SpecEmpty = 0x40000000; // It's definitely an empty value marker. -static const SpeculatedType SpecBytecodeTop = 0x7bbfffff; // It can be any of the above, except for SpecInt52. +static const SpeculatedType SpecBytecodeTop = 0x7effffff; // It can be any of the above, except for SpecInt52. static const SpeculatedType SpecFullTop = 0x7fffffff; // It can be any of the above plus anything the DFG chooses. typedef bool (*SpeculatedTypeChecker)(SpeculatedType); @@ -102,11 +94,6 @@ inline bool isCellSpeculation(SpeculatedType value) return !!(value & SpecCell) && !(value & ~SpecCell); } -inline bool isNotCellSpeculation(SpeculatedType value) -{ - return !(value & SpecCell) && value; -} - inline bool isObjectSpeculation(SpeculatedType value) { return !!(value & SpecObject) && !(value & ~SpecObject); @@ -132,21 +119,11 @@ inline bool isStringIdentSpeculation(SpeculatedType value) return value == SpecStringIdent; } -inline bool isNotStringVarSpeculation(SpeculatedType value) -{ - return !(value & SpecStringVar); -} - inline bool isStringSpeculation(SpeculatedType value) { return !!value && (value & SpecString) == value; } -inline bool isSymbolSpeculation(SpeculatedType value) -{ - return value == SpecSymbol; -} - inline bool isArraySpeculation(SpeculatedType value) { return value == SpecArray; @@ -202,14 +179,9 @@ inline bool isFloat64ArraySpeculation(SpeculatedType value) return value == SpecFloat64Array; } -inline bool isDirectArgumentsSpeculation(SpeculatedType value) -{ - return value == SpecDirectArguments; -} - -inline bool isScopedArgumentsSpeculation(SpeculatedType value) +inline bool isArgumentsSpeculation(SpeculatedType value) { - return value == SpecScopedArguments; + return !!value && (value & SpecArguments) == value; } inline bool isActionableIntMutableArraySpeculation(SpeculatedType value) @@ -238,14 +210,13 @@ inline bool isActionableTypedMutableArraySpeculation(SpeculatedType value) inline bool isActionableMutableArraySpeculation(SpeculatedType value) { return isArraySpeculation(value) + || isArgumentsSpeculation(value) || isActionableTypedMutableArraySpeculation(value); } inline bool isActionableArraySpeculation(SpeculatedType value) { return isStringSpeculation(value) - || isDirectArgumentsSpeculation(value) - || isScopedArgumentsSpeculation(value) || isActionableMutableArraySpeculation(value); } @@ -264,44 +235,39 @@ inline bool isStringOrStringObjectSpeculation(SpeculatedType value) return !!value && !(value & ~(SpecString | SpecStringObject)); } -inline bool isBoolInt32Speculation(SpeculatedType value) -{ - return value == SpecBoolInt32; -} - inline bool isInt32Speculation(SpeculatedType value) { - return value && !(value & ~SpecInt32); + return value == SpecInt32; } -inline bool isInt32OrBooleanSpeculation(SpeculatedType value) +inline bool isInt32SpeculationForArithmetic(SpeculatedType value) { - return value && !(value & ~(SpecBoolean | SpecInt32)); + return !(value & (SpecDouble | SpecInt52)); } -inline bool isInt32SpeculationForArithmetic(SpeculatedType value) +inline bool isInt32SpeculationExpectingDefined(SpeculatedType value) { - return !(value & (SpecFullDouble | SpecInt52)); + return isInt32Speculation(value & ~SpecOther); } -inline bool isInt32OrBooleanSpeculationForArithmetic(SpeculatedType value) +inline bool isInt52Speculation(SpeculatedType value) { - return !(value & (SpecFullDouble | SpecInt52)); + return value == SpecInt52; } -inline bool isInt32OrBooleanSpeculationExpectingDefined(SpeculatedType value) +inline bool isMachineIntSpeculation(SpeculatedType value) { - return isInt32OrBooleanSpeculation(value & ~SpecOther); + return !!value && (value & SpecMachineInt) == value; } -inline bool isInt52Speculation(SpeculatedType value) +inline bool isMachineIntSpeculationExpectingDefined(SpeculatedType value) { - return value == SpecInt52; + return isMachineIntSpeculation(value & ~SpecOther); } -inline bool isMachineIntSpeculation(SpeculatedType value) +inline bool isMachineIntSpeculationForArithmetic(SpeculatedType value) { - return !!value && (value & SpecMachineInt) == value; + return !(value & SpecDouble); } inline bool isInt52AsDoubleSpeculation(SpeculatedType value) @@ -321,12 +287,12 @@ inline bool isDoubleRealSpeculation(SpeculatedType value) inline bool isDoubleSpeculation(SpeculatedType value) { - return !!value && (value & SpecFullDouble) == value; + return !!value && (value & SpecDouble) == value; } inline bool isDoubleSpeculationForArithmetic(SpeculatedType value) { - return !!(value & SpecFullDouble); + return !!(value & SpecDouble); } inline bool isBytecodeRealNumberSpeculation(SpeculatedType value) @@ -349,14 +315,14 @@ inline bool isFullNumberSpeculation(SpeculatedType value) return !!(value & SpecFullNumber) && !(value & ~SpecFullNumber); } -inline bool isFullNumberOrBooleanSpeculation(SpeculatedType value) +inline bool isBytecodeNumberSpeculationExpectingDefined(SpeculatedType value) { - return value && !(value & ~(SpecFullNumber | SpecBoolean)); + return isBytecodeNumberSpeculation(value & ~SpecOther); } -inline bool isFullNumberOrBooleanSpeculationExpectingDefined(SpeculatedType value) +inline bool isFullNumberSpeculationExpectingDefined(SpeculatedType value) { - return isFullNumberOrBooleanSpeculation(value & ~SpecOther); + return isFullNumberSpeculation(value & ~SpecOther); } inline bool isBooleanSpeculation(SpeculatedType value) @@ -369,11 +335,6 @@ inline bool isOtherSpeculation(SpeculatedType value) return value == SpecOther; } -inline bool isMiscSpeculation(SpeculatedType value) -{ - return !!value && !(value & ~SpecMisc); -} - inline bool isOtherOrEmptySpeculation(SpeculatedType value) { return !value || value == SpecOther; @@ -421,27 +382,6 @@ SpeculatedType speculationFromValue(JSValue); SpeculatedType speculationFromTypedArrayType(TypedArrayType); // only valid for typed views. TypedArrayType typedArrayTypeFromSpeculation(SpeculatedType); -SpeculatedType leastUpperBoundOfStrictlyEquivalentSpeculations(SpeculatedType); - -bool valuesCouldBeEqual(SpeculatedType, SpeculatedType); - -// Precise computation of the type of the result of a double computation after we -// already know that the inputs are doubles and that the result must be a double. Use -// the closest one of these that applies. -SpeculatedType typeOfDoubleSum(SpeculatedType, SpeculatedType); -SpeculatedType typeOfDoubleDifference(SpeculatedType, SpeculatedType); -SpeculatedType typeOfDoubleProduct(SpeculatedType, SpeculatedType); -SpeculatedType typeOfDoubleQuotient(SpeculatedType, SpeculatedType); -SpeculatedType typeOfDoubleMinMax(SpeculatedType, SpeculatedType); -SpeculatedType typeOfDoubleNegation(SpeculatedType); -SpeculatedType typeOfDoubleAbs(SpeculatedType); -SpeculatedType typeOfDoubleRounding(SpeculatedType); -SpeculatedType typeOfDoublePow(SpeculatedType, SpeculatedType); - -// This conservatively models the behavior of arbitrary double operations. -SpeculatedType typeOfDoubleBinaryOp(SpeculatedType, SpeculatedType); -SpeculatedType typeOfDoubleUnaryOp(SpeculatedType); - } // namespace JSC #endif // SpeculatedType_h diff --git a/Source/JavaScriptCore/bytecode/StructureSet.cpp b/Source/JavaScriptCore/bytecode/StructureSet.cpp deleted file mode 100644 index 40fea8da3..000000000 --- a/Source/JavaScriptCore/bytecode/StructureSet.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2014, 2015 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. - */ - -#include "config.h" -#include "StructureSet.h" - -#include "DFGAbstractValue.h" -#include "TrackedReferences.h" -#include <wtf/CommaPrinter.h> - -namespace JSC { - -#if ENABLE(DFG_JIT) - -void StructureSet::filter(const DFG::StructureAbstractValue& other) -{ - genericFilter([&] (Structure* structure) -> bool { return other.contains(structure); }); -} - -void StructureSet::filter(SpeculatedType type) -{ - genericFilter( - [&] (Structure* structure) -> bool { - return type & speculationFromStructure(structure); - }); -} - -void StructureSet::filterArrayModes(ArrayModes arrayModes) -{ - genericFilter( - [&] (Structure* structure) -> bool { - return arrayModes & arrayModeFromStructure(structure); - }); -} - -void StructureSet::filter(const DFG::AbstractValue& other) -{ - filter(other.m_structure); - filter(other.m_type); - filterArrayModes(other.m_arrayModes); -} - -#endif // ENABLE(DFG_JIT) - -SpeculatedType StructureSet::speculationFromStructures() const -{ - SpeculatedType result = SpecNone; - forEach( - [&] (Structure* structure) { - mergeSpeculation(result, speculationFromStructure(structure)); - }); - return result; -} - -ArrayModes StructureSet::arrayModesFromStructures() const -{ - ArrayModes result = 0; - forEach( - [&] (Structure* structure) { - mergeArrayModes(result, asArrayModes(structure->indexingType())); - }); - return result; -} - -void StructureSet::dumpInContext(PrintStream& out, DumpContext* context) const -{ - CommaPrinter comma; - out.print("["); - forEach([&] (Structure* structure) { out.print(comma, inContext(*structure, context)); }); - out.print("]"); -} - -void StructureSet::dump(PrintStream& out) const -{ - dumpInContext(out, nullptr); -} - -void StructureSet::validateReferences(const TrackedReferences& trackedReferences) const -{ - forEach( - [&] (Structure* structure) { - trackedReferences.check(structure); - }); -} - -} // namespace JSC - diff --git a/Source/JavaScriptCore/bytecode/StructureSet.h b/Source/JavaScriptCore/bytecode/StructureSet.h index df19ec538..4cdcd01cb 100644 --- a/Source/JavaScriptCore/bytecode/StructureSet.h +++ b/Source/JavaScriptCore/bytecode/StructureSet.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, 2013-2015 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,60 +27,159 @@ #define StructureSet_h #include "ArrayProfile.h" -#include "DumpContext.h" #include "SpeculatedType.h" #include "Structure.h" -#include <wtf/TinyPtrSet.h> +#include "DumpContext.h" +#include <wtf/CommaPrinter.h> +#include <wtf/Vector.h> namespace JSC { -class TrackedReferences; - namespace DFG { class StructureAbstractValue; -struct AbstractValue; } -class StructureSet : public TinyPtrSet<Structure*> { +class StructureSet { public: - // I really want to do this: - // using TinyPtrSet::TinyPtrSet; - // - // But I can't because Windows. + StructureSet() { } - StructureSet() + StructureSet(Structure* structure) { + m_structures.append(structure); } - StructureSet(Structure* structure) - : TinyPtrSet(structure) + void clear() + { + m_structures.clear(); + } + + void add(Structure* structure) + { + ASSERT(!contains(structure)); + m_structures.append(structure); + } + + bool addAll(const StructureSet& other) + { + bool changed = false; + for (size_t i = 0; i < other.size(); ++i) { + if (contains(other[i])) + continue; + add(other[i]); + changed = true; + } + return changed; + } + + void remove(Structure* structure) + { + for (size_t i = 0; i < m_structures.size(); ++i) { + if (m_structures[i] != structure) + continue; + + m_structures[i] = m_structures.last(); + m_structures.removeLast(); + return; + } + } + + bool contains(Structure* structure) const + { + for (size_t i = 0; i < m_structures.size(); ++i) { + if (m_structures[i] == structure) + return true; + } + return false; + } + + bool containsOnly(Structure* structure) const { + if (size() != 1) + return false; + return singletonStructure() == structure; } - ALWAYS_INLINE StructureSet(const StructureSet& other) - : TinyPtrSet(other) + bool isSubsetOf(const StructureSet& other) const { + for (size_t i = 0; i < m_structures.size(); ++i) { + if (!other.contains(m_structures[i])) + return false; + } + return true; } - Structure* onlyStructure() const + bool isSupersetOf(const StructureSet& other) const { - return onlyEntry(); + return other.isSubsetOf(*this); } -#if ENABLE(DFG_JIT) - void filter(const DFG::StructureAbstractValue&); - void filter(SpeculatedType); - void filterArrayModes(ArrayModes); - void filter(const DFG::AbstractValue&); -#endif // ENABLE(DFG_JIT) + size_t size() const { return m_structures.size(); } - SpeculatedType speculationFromStructures() const; - ArrayModes arrayModesFromStructures() const; + // Call this if you know that the structure set must consist of exactly + // one structure. + Structure* singletonStructure() const + { + ASSERT(m_structures.size() == 1); + return m_structures[0]; + } + + Structure* at(size_t i) const { return m_structures.at(i); } + + Structure* operator[](size_t i) const { return at(i); } + + Structure* last() const { return m_structures.last(); } + + SpeculatedType speculationFromStructures() const + { + SpeculatedType result = SpecNone; + + for (size_t i = 0; i < m_structures.size(); ++i) + mergeSpeculation(result, speculationFromStructure(m_structures[i])); + + return result; + } + + ArrayModes arrayModesFromStructures() const + { + ArrayModes result = 0; + + for (size_t i = 0; i < m_structures.size(); ++i) + mergeArrayModes(result, asArrayModes(m_structures[i]->indexingType())); + + return result; + } + + bool operator==(const StructureSet& other) const + { + if (m_structures.size() != other.m_structures.size()) + return false; + + for (size_t i = 0; i < m_structures.size(); ++i) { + if (!other.contains(m_structures[i])) + return false; + } + + return true; + } + + void dumpInContext(PrintStream& out, DumpContext* context) const + { + CommaPrinter comma; + out.print("["); + for (size_t i = 0; i < m_structures.size(); ++i) + out.print(comma, inContext(*m_structures[i], context)); + out.print("]"); + } + + void dump(PrintStream& out) const + { + dumpInContext(out, 0); + } - void dumpInContext(PrintStream&, DumpContext*) const; - void dump(PrintStream&) const; +private: + friend class DFG::StructureAbstractValue; - void validateReferences(const TrackedReferences&) const; + Vector<Structure*, 2> m_structures; }; } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/StructureStubClearingWatchpoint.cpp b/Source/JavaScriptCore/bytecode/StructureStubClearingWatchpoint.cpp index 59c088b54..5cfb3d1e8 100644 --- a/Source/JavaScriptCore/bytecode/StructureStubClearingWatchpoint.cpp +++ b/Source/JavaScriptCore/bytecode/StructureStubClearingWatchpoint.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2012 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,7 +29,6 @@ #if ENABLE(JIT) #include "CodeBlock.h" -#include "JSCInlines.h" #include "StructureStubInfo.h" namespace JSC { @@ -37,46 +36,33 @@ namespace JSC { StructureStubClearingWatchpoint::~StructureStubClearingWatchpoint() { } StructureStubClearingWatchpoint* StructureStubClearingWatchpoint::push( - const ObjectPropertyCondition& key, WatchpointsOnStructureStubInfo& holder, - std::unique_ptr<StructureStubClearingWatchpoint>& head) + OwnPtr<StructureStubClearingWatchpoint>& head) { - head = std::make_unique<StructureStubClearingWatchpoint>(key, holder, WTF::move(head)); + head = adoptPtr(new StructureStubClearingWatchpoint(holder, head.release())); return head.get(); } -void StructureStubClearingWatchpoint::fireInternal(const FireDetail&) +void StructureStubClearingWatchpoint::fireInternal() { - if (!m_key || !m_key.isWatchable(PropertyCondition::EnsureWatchability)) { - // This will implicitly cause my own demise: stub reset removes all watchpoints. - // That works, because deleting a watchpoint removes it from the set's list, and - // the set's list traversal for firing is robust against the set changing. - m_holder.codeBlock()->resetStub(*m_holder.stubInfo()); - return; - } - - if (m_key.kind() == PropertyCondition::Presence) { - // If this was a presence condition, let's watch the property for replacements. This is profitable - // for the DFG, which will want the replacement set to be valid in order to do constant folding. - VM& vm = *Heap::heap(m_key.object())->vm(); - m_key.object()->structure()->startWatchingPropertyForReplacements(vm, m_key.offset()); - } - - m_key.object()->structure()->addTransitionWatchpoint(this); + // This will implicitly cause my own demise: stub reset removes all watchpoints. + // That works, because deleting a watchpoint removes it from the set's list, and + // the set's list traversal for firing is robust against the set changing. + m_holder.codeBlock()->resetStub(*m_holder.stubInfo()); } WatchpointsOnStructureStubInfo::~WatchpointsOnStructureStubInfo() { } -StructureStubClearingWatchpoint* WatchpointsOnStructureStubInfo::addWatchpoint(const ObjectPropertyCondition& key) +StructureStubClearingWatchpoint* WatchpointsOnStructureStubInfo::addWatchpoint() { - return StructureStubClearingWatchpoint::push(key, *this, m_head); + return StructureStubClearingWatchpoint::push(*this, m_head); } StructureStubClearingWatchpoint* WatchpointsOnStructureStubInfo::ensureReferenceAndAddWatchpoint( RefPtr<WatchpointsOnStructureStubInfo>& holderRef, CodeBlock* codeBlock, - StructureStubInfo* stubInfo, const ObjectPropertyCondition& key) + StructureStubInfo* stubInfo) { if (!holderRef) holderRef = adoptRef(new WatchpointsOnStructureStubInfo(codeBlock, stubInfo)); @@ -85,7 +71,7 @@ StructureStubClearingWatchpoint* WatchpointsOnStructureStubInfo::ensureReference ASSERT(holderRef->m_stubInfo == stubInfo); } - return holderRef->addWatchpoint(key); + return holderRef->addWatchpoint(); } } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/StructureStubClearingWatchpoint.h b/Source/JavaScriptCore/bytecode/StructureStubClearingWatchpoint.h index abacf3159..4c6bdecf4 100644 --- a/Source/JavaScriptCore/bytecode/StructureStubClearingWatchpoint.h +++ b/Source/JavaScriptCore/bytecode/StructureStubClearingWatchpoint.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2012 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,13 +26,15 @@ #ifndef StructureStubClearingWatchpoint_h #define StructureStubClearingWatchpoint_h -#include "ObjectPropertyCondition.h" #include "Watchpoint.h" +#include <wtf/Platform.h> #if ENABLE(JIT) #include <wtf/FastMalloc.h> #include <wtf/Noncopyable.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> @@ -47,29 +49,31 @@ class StructureStubClearingWatchpoint : public Watchpoint { WTF_MAKE_FAST_ALLOCATED; public: StructureStubClearingWatchpoint( - const ObjectPropertyCondition& key, + WatchpointsOnStructureStubInfo& holder) + : m_holder(holder) + { + } + + StructureStubClearingWatchpoint( WatchpointsOnStructureStubInfo& holder, - std::unique_ptr<StructureStubClearingWatchpoint> next) - : m_key(key) - , m_holder(holder) - , m_next(WTF::move(next)) + PassOwnPtr<StructureStubClearingWatchpoint> next) + : m_holder(holder) + , m_next(next) { } virtual ~StructureStubClearingWatchpoint(); static StructureStubClearingWatchpoint* push( - const ObjectPropertyCondition& key, WatchpointsOnStructureStubInfo& holder, - std::unique_ptr<StructureStubClearingWatchpoint>& head); + OwnPtr<StructureStubClearingWatchpoint>& head); protected: - virtual void fireInternal(const FireDetail&) override; + virtual void fireInternal() override; private: - ObjectPropertyCondition m_key; WatchpointsOnStructureStubInfo& m_holder; - std::unique_ptr<StructureStubClearingWatchpoint> m_next; + OwnPtr<StructureStubClearingWatchpoint> m_next; }; class WatchpointsOnStructureStubInfo : public RefCounted<WatchpointsOnStructureStubInfo> { @@ -82,11 +86,11 @@ public: ~WatchpointsOnStructureStubInfo(); - StructureStubClearingWatchpoint* addWatchpoint(const ObjectPropertyCondition& key); + StructureStubClearingWatchpoint* addWatchpoint(); static StructureStubClearingWatchpoint* ensureReferenceAndAddWatchpoint( RefPtr<WatchpointsOnStructureStubInfo>& holderRef, - CodeBlock*, StructureStubInfo*, const ObjectPropertyCondition& key); + CodeBlock*, StructureStubInfo*); CodeBlock* codeBlock() const { return m_codeBlock; } StructureStubInfo* stubInfo() const { return m_stubInfo; } @@ -94,7 +98,7 @@ public: private: CodeBlock* m_codeBlock; StructureStubInfo* m_stubInfo; - std::unique_ptr<StructureStubClearingWatchpoint> m_head; + OwnPtr<StructureStubClearingWatchpoint> m_head; }; } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/StructureStubInfo.cpp b/Source/JavaScriptCore/bytecode/StructureStubInfo.cpp index 6db79a09e..91413dfbf 100644 --- a/Source/JavaScriptCore/bytecode/StructureStubInfo.cpp +++ b/Source/JavaScriptCore/bytecode/StructureStubInfo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2014, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,17 +27,23 @@ #include "StructureStubInfo.h" #include "JSObject.h" -#include "PolymorphicGetByIdList.h" #include "PolymorphicPutByIdList.h" + namespace JSC { #if ENABLE(JIT) void StructureStubInfo::deref() { switch (accessType) { - case access_get_by_id_list: { - delete u.getByIdList.list; + case access_get_by_id_self_list: { + PolymorphicAccessStructureList* polymorphicStructures = u.getByIdSelfList.structureList; + delete polymorphicStructures; + return; + } + case access_get_by_id_proto_list: { + PolymorphicAccessStructureList* polymorphicStructures = u.getByIdProtoList.structureList; + delete polymorphicStructures; return; } case access_put_by_id_list: @@ -48,14 +54,17 @@ void StructureStubInfo::deref() delete polymorphicStructures; return; } + case access_get_by_id_self: + case access_get_by_id_proto: + case access_get_by_id_chain: case access_put_by_id_transition_normal: case access_put_by_id_transition_direct: - ObjectPropertyConditionSet::adoptRawPointer(u.putByIdTransition.rawConditionSet); - u.putByIdTransition.rawConditionSet = nullptr; - return; - case access_get_by_id_self: case access_put_by_id_replace: case access_unset: + case access_get_by_id_generic: + case access_put_by_id_generic: + case access_get_array_length: + case access_get_string_length: // These instructions don't have to release any allocated memory return; default: @@ -63,24 +72,40 @@ void StructureStubInfo::deref() } } -bool StructureStubInfo::visitWeakReferences(RepatchBuffer& repatchBuffer) +bool StructureStubInfo::visitWeakReferences() { switch (accessType) { case access_get_by_id_self: if (!Heap::isMarked(u.getByIdSelf.baseObjectStructure.get())) return false; break; - case access_get_by_id_list: { - if (!u.getByIdList.list->visitWeak(repatchBuffer)) + case access_get_by_id_proto: + if (!Heap::isMarked(u.getByIdProto.baseObjectStructure.get()) + || !Heap::isMarked(u.getByIdProto.prototypeStructure.get())) + return false; + break; + case access_get_by_id_chain: + if (!Heap::isMarked(u.getByIdChain.baseObjectStructure.get()) + || !Heap::isMarked(u.getByIdChain.chain.get())) + return false; + break; + case access_get_by_id_self_list: { + PolymorphicAccessStructureList* polymorphicStructures = u.getByIdSelfList.structureList; + if (!polymorphicStructures->visitWeak(u.getByIdSelfList.listSize)) + return false; + break; + } + case access_get_by_id_proto_list: { + PolymorphicAccessStructureList* polymorphicStructures = u.getByIdProtoList.structureList; + if (!polymorphicStructures->visitWeak(u.getByIdProtoList.listSize)) return false; break; } case access_put_by_id_transition_normal: case access_put_by_id_transition_direct: if (!Heap::isMarked(u.putByIdTransition.previousStructure.get()) - || !Heap::isMarked(u.putByIdTransition.structure.get())) - return false; - if (!ObjectPropertyConditionSet::fromRawPointer(u.putByIdTransition.rawConditionSet).areStillLive()) + || !Heap::isMarked(u.putByIdTransition.structure.get()) + || !Heap::isMarked(u.putByIdTransition.chain.get())) return false; break; case access_put_by_id_replace: @@ -88,7 +113,7 @@ bool StructureStubInfo::visitWeakReferences(RepatchBuffer& repatchBuffer) return false; break; case access_put_by_id_list: - if (!u.putByIdList.list->visitWeak(repatchBuffer)) + if (!u.putByIdList.list->visitWeak()) return false; break; case access_in_list: { diff --git a/Source/JavaScriptCore/bytecode/StructureStubInfo.h b/Source/JavaScriptCore/bytecode/StructureStubInfo.h index e6c1ceed9..5463f3e95 100644 --- a/Source/JavaScriptCore/bytecode/StructureStubInfo.h +++ b/Source/JavaScriptCore/bytecode/StructureStubInfo.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2012-2015 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2012, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,33 +26,40 @@ #ifndef StructureStubInfo_h #define StructureStubInfo_h +#include <wtf/Platform.h> + #include "CodeOrigin.h" #include "Instruction.h" #include "JITStubRoutine.h" #include "MacroAssembler.h" -#include "ObjectPropertyConditionSet.h" #include "Opcode.h" #include "PolymorphicAccessStructureList.h" #include "RegisterSet.h" -#include "SpillRegistersMode.h" #include "Structure.h" #include "StructureStubClearingWatchpoint.h" +#include <wtf/OwnPtr.h> namespace JSC { #if ENABLE(JIT) -class PolymorphicGetByIdList; class PolymorphicPutByIdList; enum AccessType { access_get_by_id_self, - access_get_by_id_list, + access_get_by_id_proto, + access_get_by_id_chain, + access_get_by_id_self_list, + access_get_by_id_proto_list, access_put_by_id_transition_normal, access_put_by_id_transition_direct, access_put_by_id_replace, access_put_by_id_list, access_unset, + access_get_by_id_generic, + access_put_by_id_generic, + access_get_array_length, + access_get_string_length, access_in_list }; @@ -60,7 +67,13 @@ inline bool isGetByIdAccess(AccessType accessType) { switch (accessType) { case access_get_by_id_self: - case access_get_by_id_list: + case access_get_by_id_proto: + case access_get_by_id_chain: + case access_get_by_id_self_list: + case access_get_by_id_proto_list: + case access_get_by_id_generic: + case access_get_array_length: + case access_get_string_length: return true; default: return false; @@ -74,6 +87,7 @@ inline bool isPutByIdAccess(AccessType accessType) case access_put_by_id_transition_direct: case access_put_by_id_replace: case access_put_by_id_list: + case access_put_by_id_generic: return true; default: return false; @@ -95,7 +109,6 @@ struct StructureStubInfo { : accessType(access_unset) , seen(false) , resetByGC(false) - , tookSlowPath(false) { } @@ -106,15 +119,36 @@ struct StructureStubInfo { u.getByIdSelf.baseObjectStructure.set(vm, owner, baseObjectStructure); } - void initGetByIdList(PolymorphicGetByIdList* list) + void initGetByIdChain(VM& vm, JSCell* owner, Structure* baseObjectStructure, StructureChain* chain, unsigned count, bool isDirect) + { + accessType = access_get_by_id_chain; + + u.getByIdChain.baseObjectStructure.set(vm, owner, baseObjectStructure); + u.getByIdChain.chain.set(vm, owner, chain); + u.getByIdChain.count = count; + u.getByIdChain.isDirect = isDirect; + } + + void initGetByIdSelfList(PolymorphicAccessStructureList* structureList, int listSize, bool didSelfPatching = false) + { + accessType = access_get_by_id_self_list; + + u.getByIdSelfList.structureList = structureList; + u.getByIdSelfList.listSize = listSize; + u.getByIdSelfList.didSelfPatching = didSelfPatching; + } + + void initGetByIdProtoList(PolymorphicAccessStructureList* structureList, int listSize) { - accessType = access_get_by_id_list; - u.getByIdList.list = list; + accessType = access_get_by_id_proto_list; + + u.getByIdProtoList.structureList = structureList; + u.getByIdProtoList.listSize = listSize; } // PutById* - void initPutByIdTransition(VM& vm, JSCell* owner, Structure* previousStructure, Structure* structure, ObjectPropertyConditionSet conditionSet, bool isDirect) + void initPutByIdTransition(VM& vm, JSCell* owner, Structure* previousStructure, Structure* structure, StructureChain* chain, bool isDirect) { if (isDirect) accessType = access_put_by_id_transition_direct; @@ -123,7 +157,7 @@ struct StructureStubInfo { u.putByIdTransition.previousStructure.set(vm, owner, previousStructure); u.putByIdTransition.structure.set(vm, owner, structure); - u.putByIdTransition.rawConditionSet = conditionSet.releaseRawPointer(); + u.putByIdTransition.chain.set(vm, owner, chain); } void initPutByIdReplace(VM& vm, JSCell* owner, Structure* baseObjectStructure) @@ -150,21 +184,13 @@ struct StructureStubInfo { { deref(); accessType = access_unset; - stubRoutine = nullptr; - watchpoints = nullptr; + stubRoutine.clear(); + watchpoints.clear(); } void deref(); - // Check if the stub has weak references that are dead. If there are dead ones that imply - // that the stub should be entirely reset, this should return false. If there are dead ones - // that can be handled internally by the stub and don't require a full reset, then this - // should reset them and return true. If there are no dead weak references, return true. - // If this method returns true it means that it has left the stub in a state where all - // outgoing GC pointers are known to point to currently marked objects; this method is - // allowed to accomplish this by either clearing those pointers somehow or by proving that - // they have already been marked. It is not allowed to mark new objects. - bool visitWeakReferences(RepatchBuffer&); + bool visitWeakReferences(); bool seenOnce() { @@ -176,22 +202,21 @@ struct StructureStubInfo { seen = true; } - StructureStubClearingWatchpoint* addWatchpoint( - CodeBlock* codeBlock, const ObjectPropertyCondition& condition = ObjectPropertyCondition()) + StructureStubClearingWatchpoint* addWatchpoint(CodeBlock* codeBlock) { return WatchpointsOnStructureStubInfo::ensureReferenceAndAddWatchpoint( - watchpoints, codeBlock, this, condition); + watchpoints, codeBlock, this); } int8_t accessType; bool seen : 1; bool resetByGC : 1; - bool tookSlowPath : 1; CodeOrigin codeOrigin; struct { - unsigned spillMode : 8; + int8_t registersFlushed; + int8_t callFrameRegister; int8_t baseGPR; #if USE(JSVALUE32_64) int8_t valueTagGPR; @@ -219,12 +244,29 @@ struct StructureStubInfo { WriteBarrierBase<Structure> baseObjectStructure; } getByIdSelf; struct { - PolymorphicGetByIdList* list; - } getByIdList; + WriteBarrierBase<Structure> baseObjectStructure; + WriteBarrierBase<Structure> prototypeStructure; + bool isDirect; + } getByIdProto; + struct { + WriteBarrierBase<Structure> baseObjectStructure; + WriteBarrierBase<StructureChain> chain; + unsigned count : 31; + bool isDirect : 1; + } getByIdChain; + struct { + PolymorphicAccessStructureList* structureList; + int listSize : 31; + bool didSelfPatching : 1; + } getByIdSelfList; + struct { + PolymorphicAccessStructureList* structureList; + int listSize; + } getByIdProtoList; struct { WriteBarrierBase<Structure> previousStructure; WriteBarrierBase<Structure> structure; - void* rawConditionSet; + WriteBarrierBase<StructureChain> chain; } putByIdTransition; struct { WriteBarrierBase<Structure> baseObjectStructure; @@ -248,7 +290,7 @@ inline CodeOrigin getStructureStubInfoCodeOrigin(StructureStubInfo& structureStu return structureStubInfo.codeOrigin; } -typedef HashMap<CodeOrigin, StructureStubInfo*, CodeOriginApproximateHash> StubInfoMap; +typedef HashMap<CodeOrigin, StructureStubInfo*> StubInfoMap; #else diff --git a/Source/JavaScriptCore/bytecode/ToThisStatus.cpp b/Source/JavaScriptCore/bytecode/ToThisStatus.cpp deleted file mode 100644 index 23d1e0800..000000000 --- a/Source/JavaScriptCore/bytecode/ToThisStatus.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2014 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. - */ - -#include "config.h" -#include "ToThisStatus.h" - -namespace JSC { - -ToThisStatus merge(ToThisStatus a, ToThisStatus b) -{ - switch (a) { - case ToThisOK: - return b; - case ToThisConflicted: - return ToThisConflicted; - case ToThisClearedByGC: - if (b == ToThisConflicted) - return ToThisConflicted; - return ToThisClearedByGC; - } - - RELEASE_ASSERT_NOT_REACHED(); - return ToThisConflicted; -} - -} // namespace JSC - -namespace WTF { - -using namespace JSC; - -void printInternal(PrintStream& out, ToThisStatus status) -{ - switch (status) { - case ToThisOK: - out.print("OK"); - return; - case ToThisConflicted: - out.print("Conflicted"); - return; - case ToThisClearedByGC: - out.print("ClearedByGC"); - return; - } - - RELEASE_ASSERT_NOT_REACHED(); -} - -} // namespace WTF - diff --git a/Source/JavaScriptCore/bytecode/ToThisStatus.h b/Source/JavaScriptCore/bytecode/ToThisStatus.h deleted file mode 100644 index 55d707c0f..000000000 --- a/Source/JavaScriptCore/bytecode/ToThisStatus.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2014 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 ToThisStatus_h -#define ToThisStatus_h - -#include <wtf/PrintStream.h> - -namespace JSC { - -enum ToThisStatus { - ToThisOK, - ToThisConflicted, - ToThisClearedByGC -}; - -ToThisStatus merge(ToThisStatus, ToThisStatus); - -} // namespace JSC - -namespace WTF { - -void printInternal(PrintStream&, JSC::ToThisStatus); - -} // namespace WTF - -#endif // ToThisStatus_h - diff --git a/Source/JavaScriptCore/bytecode/TrackedReferences.h b/Source/JavaScriptCore/bytecode/TrackedReferences.h deleted file mode 100644 index cc15e1ee7..000000000 --- a/Source/JavaScriptCore/bytecode/TrackedReferences.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2015 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 TrackedReferences_h -#define TrackedReferences_h - -#include "JSCJSValue.h" -#include "JSCell.h" -#include <wtf/HashSet.h> -#include <wtf/PrintStream.h> - -namespace JSC { - -class TrackedReferences { -public: - TrackedReferences(); - ~TrackedReferences(); - - void add(JSCell*); - void add(JSValue); - - void check(JSCell*) const; - void check(JSValue) const; - - void dump(PrintStream&) const; - -private: - HashSet<JSCell*> m_references; -}; - -} // namespace JSC - -#endif // TrackedReferences_h - diff --git a/Source/JavaScriptCore/bytecode/TypeLocation.h b/Source/JavaScriptCore/bytecode/TypeLocation.h deleted file mode 100644 index ec07656ee..000000000 --- a/Source/JavaScriptCore/bytecode/TypeLocation.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2014 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 TypeLocation_h -#define TypeLocation_h - -#include "TypeSet.h" - -namespace JSC { - -enum TypeProfilerGlobalIDFlags { - TypeProfilerNeedsUniqueIDGeneration = -1, - TypeProfilerNoGlobalIDExists = -2, - TypeProfilerReturnStatement = -3 -}; - -typedef intptr_t GlobalVariableID; - -class TypeLocation { -public: - TypeLocation() - : m_lastSeenType(TypeNothing) - , m_divotForFunctionOffsetIfReturnStatement(UINT_MAX) - , m_instructionTypeSet(TypeSet::create()) - , m_globalTypeSet(nullptr) - { - } - - GlobalVariableID m_globalVariableID; - RuntimeType m_lastSeenType; - intptr_t m_sourceID; - unsigned m_divotStart; - unsigned m_divotEnd; - unsigned m_divotForFunctionOffsetIfReturnStatement; - RefPtr<TypeSet> m_instructionTypeSet; - RefPtr<TypeSet> m_globalTypeSet; -}; - -} //namespace JSC - -#endif //TypeLocation_h diff --git a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp index 73959cfd9..1dfb5ac6a 100644 --- a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp +++ b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2013, 2015 Apple Inc. All Rights Reserved. + * Copyright (C) 2012, 2013 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,10 +31,8 @@ #include "ClassInfo.h" #include "CodeCache.h" #include "Executable.h" -#include "ExecutableInfo.h" -#include "FunctionOverrides.h" #include "JSString.h" -#include "JSCInlines.h" +#include "Operations.h" #include "Parser.h" #include "SourceProvider.h" #include "Structure.h" @@ -44,11 +42,159 @@ namespace JSC { -const ClassInfo UnlinkedCodeBlock::s_info = { "UnlinkedCodeBlock", 0, 0, CREATE_METHOD_TABLE(UnlinkedCodeBlock) }; -const ClassInfo UnlinkedGlobalCodeBlock::s_info = { "UnlinkedGlobalCodeBlock", &Base::s_info, 0, CREATE_METHOD_TABLE(UnlinkedGlobalCodeBlock) }; -const ClassInfo UnlinkedProgramCodeBlock::s_info = { "UnlinkedProgramCodeBlock", &Base::s_info, 0, CREATE_METHOD_TABLE(UnlinkedProgramCodeBlock) }; -const ClassInfo UnlinkedEvalCodeBlock::s_info = { "UnlinkedEvalCodeBlock", &Base::s_info, 0, CREATE_METHOD_TABLE(UnlinkedEvalCodeBlock) }; -const ClassInfo UnlinkedFunctionCodeBlock::s_info = { "UnlinkedFunctionCodeBlock", &Base::s_info, 0, CREATE_METHOD_TABLE(UnlinkedFunctionCodeBlock) }; +const ClassInfo UnlinkedFunctionExecutable::s_info = { "UnlinkedFunctionExecutable", 0, 0, 0, CREATE_METHOD_TABLE(UnlinkedFunctionExecutable) }; +const ClassInfo UnlinkedCodeBlock::s_info = { "UnlinkedCodeBlock", 0, 0, 0, CREATE_METHOD_TABLE(UnlinkedCodeBlock) }; +const ClassInfo UnlinkedGlobalCodeBlock::s_info = { "UnlinkedGlobalCodeBlock", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(UnlinkedGlobalCodeBlock) }; +const ClassInfo UnlinkedProgramCodeBlock::s_info = { "UnlinkedProgramCodeBlock", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(UnlinkedProgramCodeBlock) }; +const ClassInfo UnlinkedEvalCodeBlock::s_info = { "UnlinkedEvalCodeBlock", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(UnlinkedEvalCodeBlock) }; +const ClassInfo UnlinkedFunctionCodeBlock::s_info = { "UnlinkedFunctionCodeBlock", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(UnlinkedFunctionCodeBlock) }; + +static UnlinkedFunctionCodeBlock* generateFunctionCodeBlock(VM& vm, UnlinkedFunctionExecutable* executable, const SourceCode& source, CodeSpecializationKind kind, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error) +{ + RefPtr<FunctionBodyNode> body = parse<FunctionBodyNode>(&vm, source, executable->parameters(), executable->name(), executable->isInStrictContext() ? JSParseStrict : JSParseNormal, JSParseFunctionCode, error); + + if (!body) { + ASSERT(error.m_type != ParserError::ErrorNone); + return 0; + } + + if (executable->forceUsesArguments()) + body->setUsesArguments(); + body->finishParsing(executable->parameters(), executable->name(), executable->functionNameIsInScopeToggle()); + executable->recordParse(body->features(), body->hasCapturedVariables()); + + UnlinkedFunctionCodeBlock* result = UnlinkedFunctionCodeBlock::create(&vm, FunctionCode, ExecutableInfo(body->needsActivation(), body->usesEval(), body->isStrictMode(), kind == CodeForConstruct)); + OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(vm, body.get(), result, debuggerMode, profilerMode))); + error = generator->generate(); + body->destroyData(); + if (error.m_type != ParserError::ErrorNone) + return 0; + return result; +} + +unsigned UnlinkedCodeBlock::addOrFindConstant(JSValue v) +{ + unsigned numberOfConstants = numberOfConstantRegisters(); + for (unsigned i = 0; i < numberOfConstants; ++i) { + if (getConstant(FirstConstantRegisterIndex + i) == v) + return i; + } + return addConstant(v); +} + +UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(VM* vm, Structure* structure, const SourceCode& source, FunctionBodyNode* node, bool isFromGlobalCode) + : Base(*vm, structure) + , m_numCapturedVariables(node->capturedVariableCount()) + , m_forceUsesArguments(node->usesArguments()) + , m_isInStrictContext(node->isStrictMode()) + , m_hasCapturedVariables(node->hasCapturedVariables()) + , m_isFromGlobalCode(isFromGlobalCode) + , m_name(node->ident()) + , m_inferredName(node->inferredName()) + , m_parameters(node->parameters()) + , m_firstLineOffset(node->firstLine() - source.firstLine()) + , m_lineCount(node->lastLine() - node->firstLine()) + , m_unlinkedFunctionNameStart(node->functionNameStart() - source.startOffset()) + , m_unlinkedBodyStartColumn(node->startColumn()) + , m_unlinkedBodyEndColumn(m_lineCount ? node->endColumn() : node->endColumn() - node->startColumn()) + , m_startOffset(node->source().startOffset() - source.startOffset()) + , m_sourceLength(node->source().length()) + , m_features(node->features()) + , m_functionNameIsInScopeToggle(node->functionNameIsInScopeToggle()) +{ +} + +size_t UnlinkedFunctionExecutable::parameterCount() const +{ + return m_parameters->size(); +} + +void UnlinkedFunctionExecutable::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + UnlinkedFunctionExecutable* thisObject = jsCast<UnlinkedFunctionExecutable*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + Base::visitChildren(thisObject, visitor); + visitor.append(&thisObject->m_codeBlockForCall); + visitor.append(&thisObject->m_codeBlockForConstruct); + visitor.append(&thisObject->m_nameValue); + visitor.append(&thisObject->m_symbolTableForCall); + visitor.append(&thisObject->m_symbolTableForConstruct); +} + +FunctionExecutable* UnlinkedFunctionExecutable::link(VM& vm, const SourceCode& source, size_t lineOffset, size_t sourceOffset) +{ + unsigned firstLine = lineOffset + m_firstLineOffset; + unsigned startOffset = sourceOffset + m_startOffset; + bool startColumnIsOnFirstSourceLine = !m_firstLineOffset; + unsigned startColumn = m_unlinkedBodyStartColumn + (startColumnIsOnFirstSourceLine ? source.startColumn() : 1); + bool endColumnIsOnStartLine = !m_lineCount; + unsigned endColumn = m_unlinkedBodyEndColumn + (endColumnIsOnStartLine ? startColumn : 1); + SourceCode code(source.provider(), startOffset, startOffset + m_sourceLength, firstLine, startColumn); + return FunctionExecutable::create(vm, code, this, firstLine, firstLine + m_lineCount, startColumn, endColumn); +} + +UnlinkedFunctionExecutable* UnlinkedFunctionExecutable::fromGlobalCode(const Identifier& name, ExecState* exec, Debugger*, const SourceCode& source, JSObject** exception) +{ + ParserError error; + VM& vm = exec->vm(); + CodeCache* codeCache = vm.codeCache(); + UnlinkedFunctionExecutable* executable = codeCache->getFunctionExecutableFromGlobalCode(vm, name, source, error); + + if (exec->lexicalGlobalObject()->hasDebugger()) + exec->lexicalGlobalObject()->debugger()->sourceParsed(exec, source.provider(), error.m_line, error.m_message); + + if (error.m_type != ParserError::ErrorNone) { + *exception = error.toErrorObject(exec->lexicalGlobalObject(), source); + return 0; + } + + return executable; +} + +UnlinkedFunctionCodeBlock* UnlinkedFunctionExecutable::codeBlockFor(VM& vm, const SourceCode& source, CodeSpecializationKind specializationKind, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error) +{ + switch (specializationKind) { + case CodeForCall: + if (UnlinkedFunctionCodeBlock* codeBlock = m_codeBlockForCall.get()) + return codeBlock; + break; + case CodeForConstruct: + if (UnlinkedFunctionCodeBlock* codeBlock = m_codeBlockForConstruct.get()) + return codeBlock; + break; + } + + UnlinkedFunctionCodeBlock* result = generateFunctionCodeBlock(vm, this, source, specializationKind, debuggerMode, profilerMode, error); + + if (error.m_type != ParserError::ErrorNone) + return 0; + + switch (specializationKind) { + case CodeForCall: + m_codeBlockForCall.set(vm, this, result); + m_symbolTableForCall.set(vm, this, result->symbolTable()); + break; + case CodeForConstruct: + m_codeBlockForConstruct.set(vm, this, result); + m_symbolTableForConstruct.set(vm, this, result->symbolTable()); + break; + } + return result; +} + +String UnlinkedFunctionExecutable::paramString() const +{ + FunctionParameters& parameters = *m_parameters; + StringBuilder builder; + for (size_t pos = 0; pos < parameters.size(); ++pos) { + if (!builder.isEmpty()) + builder.appendLiteral(", "); + parameters.at(pos)->toString(builder); + } + return builder.toString(); +} UnlinkedCodeBlock::UnlinkedCodeBlock(VM* vm, Structure* structure, CodeType codeType, const ExecutableInfo& info) : Base(*vm, structure) @@ -56,14 +202,14 @@ UnlinkedCodeBlock::UnlinkedCodeBlock(VM* vm, Structure* structure, CodeType code , m_numCalleeRegisters(0) , m_numParameters(0) , m_vm(vm) + , m_argumentsRegister(VirtualRegister()) , m_globalObjectRegister(VirtualRegister()) - , m_needsFullScopeChain(info.needsActivation()) - , m_usesEval(info.usesEval()) - , m_isStrictMode(info.isStrictMode()) - , m_isConstructor(info.isConstructor()) + , m_needsFullScopeChain(info.m_needsActivation) + , m_usesEval(info.m_usesEval) + , m_isNumericCompareFunction(false) + , m_isStrictMode(info.m_isStrictMode) + , m_isConstructor(info.m_isConstructor) , m_hasCapturedVariables(false) - , m_isBuiltinFunction(info.isBuiltinFunction()) - , m_constructorKind(static_cast<unsigned>(info.constructorKind())) , m_firstLine(0) , m_lineCount(0) , m_endColumn(UINT_MAX) @@ -78,15 +224,15 @@ UnlinkedCodeBlock::UnlinkedCodeBlock(VM* vm, Structure* structure, CodeType code , m_bytecodeCommentIterator(0) #endif { - for (auto& constantRegisterIndex : m_linkTimeConstants) - constantRegisterIndex = 0; - ASSERT(m_constructorKind == static_cast<unsigned>(info.constructorKind())); + } void UnlinkedCodeBlock::visitChildren(JSCell* cell, SlotVisitor& visitor) { UnlinkedCodeBlock* thisObject = jsCast<UnlinkedCodeBlock*>(cell); ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); Base::visitChildren(thisObject, visitor); visitor.append(&thisObject->m_symbolTable); for (FunctionExpressionVector::iterator ptr = thisObject->m_functionDecls.begin(), end = thisObject->m_functionDecls.end(); ptr != end; ++ptr) @@ -258,37 +404,15 @@ void UnlinkedCodeBlock::addExpressionInfo(unsigned instructionOffset, m_expressionInfo.append(info); } -bool UnlinkedCodeBlock::typeProfilerExpressionInfoForBytecodeOffset(unsigned bytecodeOffset, unsigned& startDivot, unsigned& endDivot) -{ - static const bool verbose = false; - auto iter = m_typeProfilerInfoMap.find(bytecodeOffset); - if (iter == m_typeProfilerInfoMap.end()) { - if (verbose) - dataLogF("Don't have assignment info for offset:%u\n", bytecodeOffset); - startDivot = UINT_MAX; - endDivot = UINT_MAX; - return false; - } - - TypeProfilerExpressionRange& range = iter->value; - startDivot = range.m_startDivot; - endDivot = range.m_endDivot; - return true; -} - -void UnlinkedCodeBlock::addTypeProfilerExpressionInfo(unsigned instructionOffset, unsigned startDivot, unsigned endDivot) -{ - TypeProfilerExpressionRange range; - range.m_startDivot = startDivot; - range.m_endDivot = endDivot; - m_typeProfilerInfoMap.set(instructionOffset, range); -} - void UnlinkedProgramCodeBlock::visitChildren(JSCell* cell, SlotVisitor& visitor) { UnlinkedProgramCodeBlock* thisObject = jsCast<UnlinkedProgramCodeBlock*>(cell); ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); Base::visitChildren(thisObject, visitor); + for (size_t i = 0, end = thisObject->m_functionDeclarations.size(); i != end; i++) + visitor.append(&thisObject->m_functionDeclarations[i].second); } UnlinkedCodeBlock::~UnlinkedCodeBlock() @@ -317,7 +441,7 @@ void UnlinkedFunctionExecutable::destroy(JSCell* cell) void UnlinkedCodeBlock::setInstructions(std::unique_ptr<UnlinkedInstructionStream> instructions) { - m_unlinkedInstructions = WTF::move(instructions); + m_unlinkedInstructions = std::move(instructions); } const UnlinkedInstructionStream& UnlinkedCodeBlock::instructions() const diff --git a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h index 552ed84a2..b9dae2d5c 100644 --- a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h +++ b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2015 Apple Inc. All Rights Reserved. + * Copyright (C) 2012, 2013 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,9 +29,7 @@ #include "BytecodeConventions.h" #include "CodeSpecializationKind.h" #include "CodeType.h" -#include "ConstructAbility.h" #include "ExpressionRangeInfo.h" -#include "HandlerInfo.h" #include "Identifier.h" #include "JSCell.h" #include "JSString.h" @@ -39,28 +37,27 @@ #include "RegExp.h" #include "SpecialPointer.h" #include "SymbolTable.h" -#include "UnlinkedFunctionExecutable.h" -#include "VariableEnvironment.h" #include "VirtualRegister.h" + +#include <wtf/Compression.h> #include <wtf/RefCountedArray.h> #include <wtf/Vector.h> namespace JSC { class Debugger; -class FunctionMetadataNode; +class FunctionBodyNode; class FunctionExecutable; +class FunctionParameters; class JSScope; -class ParserError; +struct ParserError; class ScriptExecutable; class SourceCode; class SourceProvider; class SymbolTable; class UnlinkedCodeBlock; class UnlinkedFunctionCodeBlock; -class UnlinkedFunctionExecutable; class UnlinkedInstructionStream; -struct ExecutableInfo; typedef unsigned UnlinkedValueProfile; typedef unsigned UnlinkedArrayProfile; @@ -68,6 +65,132 @@ typedef unsigned UnlinkedArrayAllocationProfile; typedef unsigned UnlinkedObjectAllocationProfile; typedef unsigned UnlinkedLLIntCallLinkInfo; +struct ExecutableInfo { + ExecutableInfo(bool needsActivation, bool usesEval, bool isStrictMode, bool isConstructor) + : m_needsActivation(needsActivation) + , m_usesEval(usesEval) + , m_isStrictMode(isStrictMode) + , m_isConstructor(isConstructor) + { + } + bool m_needsActivation; + bool m_usesEval; + bool m_isStrictMode; + bool m_isConstructor; +}; + +class UnlinkedFunctionExecutable : public JSCell { +public: + friend class CodeCache; + typedef JSCell Base; + static UnlinkedFunctionExecutable* create(VM* vm, const SourceCode& source, FunctionBodyNode* node, bool isFromGlobalCode = false) + { + UnlinkedFunctionExecutable* instance = new (NotNull, allocateCell<UnlinkedFunctionExecutable>(vm->heap)) UnlinkedFunctionExecutable(vm, vm->unlinkedFunctionExecutableStructure.get(), source, node, isFromGlobalCode); + instance->finishCreation(*vm); + return instance; + } + + const Identifier& name() const { return m_name; } + const Identifier& inferredName() const { return m_inferredName; } + JSString* nameValue() const { return m_nameValue.get(); } + SymbolTable* symbolTable(CodeSpecializationKind kind) + { + return (kind == CodeForCall) ? m_symbolTableForCall.get() : m_symbolTableForConstruct.get(); + } + size_t parameterCount() const; + bool isInStrictContext() const { return m_isInStrictContext; } + FunctionNameIsInScopeToggle functionNameIsInScopeToggle() const { return m_functionNameIsInScopeToggle; } + + unsigned firstLineOffset() const { return m_firstLineOffset; } + unsigned lineCount() const { return m_lineCount; } + unsigned unlinkedFunctionNameStart() const { return m_unlinkedFunctionNameStart; } + unsigned unlinkedBodyStartColumn() const { return m_unlinkedBodyStartColumn; } + unsigned unlinkedBodyEndColumn() const { return m_unlinkedBodyEndColumn; } + unsigned startOffset() const { return m_startOffset; } + unsigned sourceLength() { return m_sourceLength; } + + String paramString() const; + + UnlinkedFunctionCodeBlock* codeBlockFor(VM&, const SourceCode&, CodeSpecializationKind, DebuggerMode, ProfilerMode, ParserError&); + + static UnlinkedFunctionExecutable* fromGlobalCode(const Identifier&, ExecState*, Debugger*, const SourceCode&, JSObject** exception); + + FunctionExecutable* link(VM&, const SourceCode&, size_t lineOffset, size_t sourceOffset); + + void clearCodeForRecompilation() + { + m_symbolTableForCall.clear(); + m_symbolTableForConstruct.clear(); + m_codeBlockForCall.clear(); + m_codeBlockForConstruct.clear(); + } + + FunctionParameters* parameters() { return m_parameters.get(); } + + void recordParse(CodeFeatures features, bool hasCapturedVariables) + { + m_features = features; + m_hasCapturedVariables = hasCapturedVariables; + } + + bool forceUsesArguments() const { return m_forceUsesArguments; } + + CodeFeatures features() const { return m_features; } + bool hasCapturedVariables() const { return m_hasCapturedVariables; } + + static const bool needsDestruction = true; + static const bool hasImmortalStructure = true; + static void destroy(JSCell*); + +private: + UnlinkedFunctionExecutable(VM*, Structure*, const SourceCode&, FunctionBodyNode*, bool isFromGlobalCode); + WriteBarrier<UnlinkedFunctionCodeBlock> m_codeBlockForCall; + WriteBarrier<UnlinkedFunctionCodeBlock> m_codeBlockForConstruct; + + unsigned m_numCapturedVariables : 29; + bool m_forceUsesArguments : 1; + bool m_isInStrictContext : 1; + bool m_hasCapturedVariables : 1; + bool m_isFromGlobalCode : 1; + + Identifier m_name; + Identifier m_inferredName; + WriteBarrier<JSString> m_nameValue; + WriteBarrier<SymbolTable> m_symbolTableForCall; + WriteBarrier<SymbolTable> m_symbolTableForConstruct; + RefPtr<FunctionParameters> m_parameters; + unsigned m_firstLineOffset; + unsigned m_lineCount; + unsigned m_unlinkedFunctionNameStart; + unsigned m_unlinkedBodyStartColumn; + unsigned m_unlinkedBodyEndColumn; + unsigned m_startOffset; + unsigned m_sourceLength; + + CodeFeatures m_features; + + FunctionNameIsInScopeToggle m_functionNameIsInScopeToggle; + +protected: + void finishCreation(VM& vm) + { + Base::finishCreation(vm); + m_nameValue.set(vm, this, jsString(&vm, name().string())); + } + + static void visitChildren(JSCell*, SlotVisitor&); + +public: + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) + { + return Structure::create(vm, globalObject, proto, TypeInfo(UnlinkedFunctionExecutableType, StructureFlags), info()); + } + + static const unsigned StructureFlags = OverridesVisitChildren | JSCell::StructureFlags; + + DECLARE_EXPORT_INFO; +}; + struct UnlinkedStringJumpTable { typedef HashMap<RefPtr<StringImpl>, int32_t> StringOffsetTable; StringOffsetTable offsetTable; @@ -95,6 +218,13 @@ struct UnlinkedSimpleJumpTable { } }; +struct UnlinkedHandlerInfo { + uint32_t start; + uint32_t end; + uint32_t target; + uint32_t scopeDepth; +}; + struct UnlinkedInstruction { UnlinkedInstruction() { u.operand = 0; } UnlinkedInstruction(OpcodeID opcode) { u.opcode = opcode; } @@ -109,9 +239,8 @@ struct UnlinkedInstruction { class UnlinkedCodeBlock : public JSCell { public: typedef JSCell Base; - static const unsigned StructureFlags = Base::StructureFlags; - static const bool needsDestruction = true; + static const bool hasImmortalStructure = true; enum { CallFunction, ApplyFunction }; @@ -120,18 +249,21 @@ public: bool usesEval() const { return m_usesEval; } bool needsFullScopeChain() const { return m_needsFullScopeChain; } + void setNeedsFullScopeChain(bool needsFullScopeChain) { m_needsFullScopeChain = needsFullScopeChain; } void addExpressionInfo(unsigned instructionOffset, int divot, int startOffset, int endOffset, unsigned line, unsigned column); - void addTypeProfilerExpressionInfo(unsigned instructionOffset, unsigned startDivot, unsigned endDivot); - bool hasExpressionInfo() { return m_expressionInfo.size(); } // Special registers void setThisRegister(VirtualRegister thisRegister) { m_thisRegister = thisRegister; } - void setScopeRegister(VirtualRegister scopeRegister) { m_scopeRegister = scopeRegister; } - void setActivationRegister(VirtualRegister activationRegister) { m_lexicalEnvironmentRegister = activationRegister; } + void setActivationRegister(VirtualRegister activationRegister) { m_activationRegister = activationRegister; } + + void setArgumentsRegister(VirtualRegister argumentsRegister) { m_argumentsRegister = argumentsRegister; } + bool usesArguments() const { return m_argumentsRegister.isValid(); } + VirtualRegister argumentsRegister() const { return m_argumentsRegister; } + bool usesGlobalObject() const { return m_globalObjectRegister.isValid(); } void setGlobalObjectRegister(VirtualRegister globalObjectRegister) { m_globalObjectRegister = globalObjectRegister; } @@ -164,35 +296,19 @@ public: const Identifier& identifier(int index) const { return m_identifiers[index]; } const Vector<Identifier>& identifiers() const { return m_identifiers; } - unsigned addConstant(JSValue v, SourceCodeRepresentation sourceCodeRepresentation = SourceCodeRepresentation::Other) + size_t numberOfConstantRegisters() const { return m_constantRegisters.size(); } + unsigned addConstant(JSValue v) { unsigned result = m_constantRegisters.size(); m_constantRegisters.append(WriteBarrier<Unknown>()); m_constantRegisters.last().set(*m_vm, this, v); - m_constantsSourceCodeRepresentation.append(sourceCodeRepresentation); - return result; - } - unsigned addConstant(LinkTimeConstant type) - { - unsigned result = m_constantRegisters.size(); - ASSERT(result); - unsigned index = static_cast<unsigned>(type); - ASSERT(index < LinkTimeConstantCount); - m_linkTimeConstants[index] = result; - m_constantRegisters.append(WriteBarrier<Unknown>()); - m_constantsSourceCodeRepresentation.append(SourceCodeRepresentation::Other); return result; } - unsigned registerIndexForLinkTimeConstant(LinkTimeConstant type) - { - unsigned index = static_cast<unsigned>(type); - ASSERT(index < LinkTimeConstantCount); - return m_linkTimeConstants[index]; - } + unsigned addOrFindConstant(JSValue); const Vector<WriteBarrier<Unknown>>& constantRegisters() { return m_constantRegisters; } const WriteBarrier<Unknown>& constantRegister(int index) const { return m_constantRegisters[index - FirstConstantRegisterIndex]; } ALWAYS_INLINE bool isConstantRegisterIndex(int index) const { return index >= FirstConstantRegisterIndex; } - const Vector<SourceCodeRepresentation>& constantsSourceCodeRepresentation() { return m_constantsSourceCodeRepresentation; } + ALWAYS_INLINE JSValue getConstant(int index) const { return m_constantRegisters[index - FirstConstantRegisterIndex].get(); } // Jumps size_t numberOfJumpTargets() const { return m_jumpTargets.size(); } @@ -200,16 +316,14 @@ public: unsigned jumpTarget(int index) const { return m_jumpTargets[index]; } unsigned lastJumpTarget() const { return m_jumpTargets.last(); } - bool isBuiltinFunction() const { return m_isBuiltinFunction; } - - ConstructorKind constructorKind() const { return static_cast<ConstructorKind>(m_constructorKind); } + void setIsNumericCompareFunction(bool isNumericCompareFunction) { m_isNumericCompareFunction = isNumericCompareFunction; } + bool isNumericCompareFunction() const { return m_isNumericCompareFunction; } void shrinkToFit() { m_jumpTargets.shrinkToFit(); m_identifiers.shrinkToFit(); m_constantRegisters.shrinkToFit(); - m_constantsSourceCodeRepresentation.shrinkToFit(); m_functionDecls.shrinkToFit(); m_functionExprs.shrinkToFit(); m_propertyAccessInstructions.shrinkToFit(); @@ -266,9 +380,11 @@ public: // Exception handling support size_t numberOfExceptionHandlers() const { return m_rareData ? m_rareData->m_exceptionHandlers.size() : 0; } - void addExceptionHandler(const UnlinkedHandlerInfo& handler) { createRareDataIfNecessary(); return m_rareData->m_exceptionHandlers.append(handler); } + void addExceptionHandler(const UnlinkedHandlerInfo& hanler) { createRareDataIfNecessary(); return m_rareData->m_exceptionHandlers.append(hanler); } UnlinkedHandlerInfo& exceptionHandler(int index) { ASSERT(m_rareData); return m_rareData->m_exceptionHandlers[index]; } + SymbolTable* symbolTable() const { return m_symbolTable.get(); } + VM* vm() const { return m_vm; } UnlinkedArrayProfile addArrayProfile() { return m_arrayProfileCount++; } @@ -286,9 +402,8 @@ public: CodeType codeType() const { return m_codeType; } VirtualRegister thisRegister() const { return m_thisRegister; } - VirtualRegister scopeRegister() const { return m_scopeRegister; } - VirtualRegister activationRegister() const { return m_lexicalEnvironmentRegister; } - bool hasActivationRegister() const { return m_lexicalEnvironmentRegister.isValid(); } + VirtualRegister activationRegister() const { return m_activationRegister; } + void addPropertyAccessInstruction(unsigned propertyAccessInstruction) { @@ -321,15 +436,13 @@ public: return m_rareData->m_constantBuffers[index]; } - bool hasRareData() const { return m_rareData.get(); } + bool hasRareData() const { return m_rareData; } int lineNumberForBytecodeOffset(unsigned bytecodeOffset); void expressionRangeForBytecodeOffset(unsigned bytecodeOffset, int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column); - bool typeProfilerExpressionInfoForBytecodeOffset(unsigned bytecodeOffset, unsigned& startDivot, unsigned& endDivot); - void recordParse(CodeFeatures features, bool hasCapturedVariables, unsigned firstLine, unsigned lineCount, unsigned endColumn) { m_features = features; @@ -347,9 +460,6 @@ public: ALWAYS_INLINE unsigned startColumn() const { return 0; } unsigned endColumn() const { return m_endColumn; } - void addOpProfileControlFlowBytecodeOffset(size_t offset) { m_opProfileControlFlowBytecodeOffsets.append(offset); } - const Vector<size_t>& opProfileControlFlowBytecodeOffsets() const { return m_opProfileControlFlowBytecodeOffsets; } - void dumpExpressionRangeInfo(); // For debugging purpose only. protected: @@ -369,7 +479,7 @@ private: void createRareDataIfNecessary() { if (!m_rareData) - m_rareData = std::make_unique<RareData>(); + m_rareData = adoptPtr(new RareData); } void getLineAndColumn(ExpressionRangeInfo&, unsigned& line, unsigned& column); @@ -380,18 +490,16 @@ private: VM* m_vm; VirtualRegister m_thisRegister; - VirtualRegister m_scopeRegister; - VirtualRegister m_lexicalEnvironmentRegister; + VirtualRegister m_argumentsRegister; + VirtualRegister m_activationRegister; VirtualRegister m_globalObjectRegister; - unsigned m_needsFullScopeChain : 1; - unsigned m_usesEval : 1; - unsigned m_isStrictMode : 1; - unsigned m_isConstructor : 1; - unsigned m_hasCapturedVariables : 1; - unsigned m_isBuiltinFunction : 1; - unsigned m_constructorKind : 2; - + bool m_needsFullScopeChain : 1; + bool m_usesEval : 1; + bool m_isNumericCompareFunction : 1; + bool m_isStrictMode : 1; + bool m_isConstructor : 1; + bool m_hasCapturedVariables : 1; unsigned m_firstLine; unsigned m_lineCount; unsigned m_endColumn; @@ -404,8 +512,6 @@ private: // Constant Pools Vector<Identifier> m_identifiers; Vector<WriteBarrier<Unknown>> m_constantRegisters; - Vector<SourceCodeRepresentation> m_constantsSourceCodeRepresentation; - std::array<unsigned, LinkTimeConstantCount> m_linkTimeConstants; typedef Vector<WriteBarrier<UnlinkedFunctionExecutable>> FunctionExpressionVector; FunctionExpressionVector m_functionDecls; FunctionExpressionVector m_functionExprs; @@ -445,16 +551,12 @@ public: }; private: - std::unique_ptr<RareData> m_rareData; + OwnPtr<RareData> m_rareData; Vector<ExpressionRangeInfo> m_expressionInfo; - struct TypeProfilerExpressionRange { - unsigned m_startDivot; - unsigned m_endDivot; - }; - HashMap<unsigned, TypeProfilerExpressionRange> m_typeProfilerInfoMap; - Vector<size_t> m_opProfileControlFlowBytecodeOffsets; protected: + + static const unsigned StructureFlags = OverridesVisitChildren | Base::StructureFlags; static void visitChildren(JSCell*, SlotVisitor&); public: @@ -471,10 +573,12 @@ protected: { } + static const unsigned StructureFlags = OverridesVisitChildren | Base::StructureFlags; + DECLARE_INFO; }; -class UnlinkedProgramCodeBlock final : public UnlinkedGlobalCodeBlock { +class UnlinkedProgramCodeBlock : public UnlinkedGlobalCodeBlock { private: friend class CodeCache; static UnlinkedProgramCodeBlock* create(VM* vm, const ExecutableInfo& info) @@ -486,12 +590,23 @@ private: public: typedef UnlinkedGlobalCodeBlock Base; - static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; - static void destroy(JSCell*); - void setVariableDeclarations(const VariableEnvironment& environment) { m_varDeclarations = environment; } - const VariableEnvironment& variableDeclarations() const { return m_varDeclarations; } + void addFunctionDeclaration(VM& vm, const Identifier& name, UnlinkedFunctionExecutable* functionExecutable) + { + m_functionDeclarations.append(std::make_pair(name, WriteBarrier<UnlinkedFunctionExecutable>(vm, this, functionExecutable))); + } + + void addVariableDeclaration(const Identifier& name, bool isConstant) + { + m_varDeclarations.append(std::make_pair(name, isConstant)); + } + + typedef Vector<std::pair<Identifier, bool>> VariableDeclations; + typedef Vector<std::pair<Identifier, WriteBarrier<UnlinkedFunctionExecutable>> > FunctionDeclations; + + const VariableDeclations& variableDeclarations() const { return m_varDeclarations; } + const FunctionDeclations& functionDeclarations() const { return m_functionDeclarations; } static void visitChildren(JSCell*, SlotVisitor&); @@ -501,7 +616,8 @@ private: { } - VariableEnvironment m_varDeclarations; + VariableDeclations m_varDeclarations; + FunctionDeclations m_functionDeclarations; public: static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) @@ -509,10 +625,12 @@ public: return Structure::create(vm, globalObject, proto, TypeInfo(UnlinkedProgramCodeBlockType, StructureFlags), info()); } + static const unsigned StructureFlags = OverridesVisitChildren | Base::StructureFlags; + DECLARE_INFO; }; -class UnlinkedEvalCodeBlock final : public UnlinkedGlobalCodeBlock { +class UnlinkedEvalCodeBlock : public UnlinkedGlobalCodeBlock { private: friend class CodeCache; @@ -525,8 +643,6 @@ private: public: typedef UnlinkedGlobalCodeBlock Base; - static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; - static void destroy(JSCell*); const Identifier& variable(unsigned index) { return m_variables[index]; } @@ -551,14 +667,13 @@ public: return Structure::create(vm, globalObject, proto, TypeInfo(UnlinkedEvalCodeBlockType, StructureFlags), info()); } + static const unsigned StructureFlags = OverridesVisitChildren | Base::StructureFlags; + DECLARE_INFO; }; -class UnlinkedFunctionCodeBlock final : public UnlinkedCodeBlock { +class UnlinkedFunctionCodeBlock : public UnlinkedCodeBlock { public: - typedef UnlinkedCodeBlock Base; - static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; - static UnlinkedFunctionCodeBlock* create(VM* vm, CodeType codeType, const ExecutableInfo& info) { UnlinkedFunctionCodeBlock* instance = new (NotNull, allocateCell<UnlinkedFunctionCodeBlock>(vm->heap)) UnlinkedFunctionCodeBlock(vm, vm->unlinkedFunctionCodeBlockStructure.get(), codeType, info); @@ -566,6 +681,7 @@ public: return instance; } + typedef UnlinkedCodeBlock Base; static void destroy(JSCell*); private: @@ -580,6 +696,8 @@ public: return Structure::create(vm, globalObject, proto, TypeInfo(UnlinkedFunctionCodeBlockType, StructureFlags), info()); } + static const unsigned StructureFlags = OverridesVisitChildren | Base::StructureFlags; + DECLARE_INFO; }; diff --git a/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp b/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp deleted file mode 100644 index 1891cb8c5..000000000 --- a/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (C) 2012, 2013, 2015 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. - */ - -#include "config.h" -#include "UnlinkedFunctionExecutable.h" - -#include "BytecodeGenerator.h" -#include "ClassInfo.h" -#include "CodeCache.h" -#include "Executable.h" -#include "ExecutableInfo.h" -#include "FunctionOverrides.h" -#include "JSCInlines.h" -#include "JSString.h" -#include "Parser.h" -#include "SourceProvider.h" -#include "Structure.h" -#include "SymbolTable.h" -#include "UnlinkedInstructionStream.h" -#include <wtf/DataLog.h> - -namespace JSC { - -static_assert(sizeof(UnlinkedFunctionExecutable) <= 256, "UnlinkedFunctionExecutable should fit in a 256-byte cell."); - -const ClassInfo UnlinkedFunctionExecutable::s_info = { "UnlinkedFunctionExecutable", 0, 0, CREATE_METHOD_TABLE(UnlinkedFunctionExecutable) }; - -static UnlinkedFunctionCodeBlock* generateFunctionCodeBlock( - VM& vm, UnlinkedFunctionExecutable* executable, const SourceCode& source, - CodeSpecializationKind kind, DebuggerMode debuggerMode, ProfilerMode profilerMode, - UnlinkedFunctionKind functionKind, ParserError& error) -{ - JSParserBuiltinMode builtinMode = executable->isBuiltinFunction() ? JSParserBuiltinMode::Builtin : JSParserBuiltinMode::NotBuiltin; - JSParserStrictMode strictMode = executable->isInStrictContext() ? JSParserStrictMode::Strict : JSParserStrictMode::NotStrict; - ASSERT(isFunctionParseMode(executable->parseMode())); - std::unique_ptr<FunctionNode> function = parse<FunctionNode>( - &vm, source, executable->name(), builtinMode, strictMode, executable->parseMode(), error, nullptr); - - if (!function) { - ASSERT(error.isValid()); - return nullptr; - } - - function->finishParsing(executable->name(), executable->functionMode()); - executable->recordParse(function->features(), function->hasCapturedVariables()); - - UnlinkedFunctionCodeBlock* result = UnlinkedFunctionCodeBlock::create(&vm, FunctionCode, - ExecutableInfo(function->needsActivation(), function->usesEval(), function->isStrictMode(), kind == CodeForConstruct, functionKind == UnlinkedBuiltinFunction, executable->constructorKind())); - auto generator(std::make_unique<BytecodeGenerator>(vm, function.get(), result, debuggerMode, profilerMode, executable->parentScopeTDZVariables())); - error = generator->generate(); - if (error.isValid()) - return nullptr; - return result; -} - -UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(VM* vm, Structure* structure, const SourceCode& source, RefPtr<SourceProvider>&& sourceOverride, FunctionMetadataNode* node, UnlinkedFunctionKind kind, ConstructAbility constructAbility, VariableEnvironment& parentScopeTDZVariables) - : Base(*vm, structure) - , m_name(node->ident()) - , m_inferredName(node->inferredName()) - , m_sourceOverride(WTF::move(sourceOverride)) - , m_firstLineOffset(node->firstLine() - source.firstLine()) - , m_lineCount(node->lastLine() - node->firstLine()) - , m_unlinkedFunctionNameStart(node->functionNameStart() - source.startOffset()) - , m_unlinkedBodyStartColumn(node->startColumn()) - , m_unlinkedBodyEndColumn(m_lineCount ? node->endColumn() : node->endColumn() - node->startColumn()) - , m_startOffset(node->source().startOffset() - source.startOffset()) - , m_sourceLength(node->source().length()) - , m_parametersStartOffset(node->parametersStart()) - , m_typeProfilingStartOffset(node->functionKeywordStart()) - , m_typeProfilingEndOffset(node->startStartOffset() + node->source().length() - 1) - , m_parameterCount(node->parameterCount()) - , m_parseMode(node->parseMode()) - , m_features(0) - , m_isInStrictContext(node->isInStrictContext()) - , m_hasCapturedVariables(false) - , m_isBuiltinFunction(kind == UnlinkedBuiltinFunction) - , m_constructAbility(static_cast<unsigned>(constructAbility)) - , m_constructorKind(static_cast<unsigned>(node->constructorKind())) - , m_functionMode(node->functionMode()) -{ - ASSERT(m_constructorKind == static_cast<unsigned>(node->constructorKind())); - m_parentScopeTDZVariables.swap(parentScopeTDZVariables); -} - -void UnlinkedFunctionExecutable::visitChildren(JSCell* cell, SlotVisitor& visitor) -{ - UnlinkedFunctionExecutable* thisObject = jsCast<UnlinkedFunctionExecutable*>(cell); - ASSERT_GC_OBJECT_INHERITS(thisObject, info()); - Base::visitChildren(thisObject, visitor); - visitor.append(&thisObject->m_codeBlockForCall); - visitor.append(&thisObject->m_codeBlockForConstruct); - visitor.append(&thisObject->m_nameValue); -} - -FunctionExecutable* UnlinkedFunctionExecutable::link(VM& vm, const SourceCode& ownerSource, int overrideLineNumber) -{ - SourceCode source = m_sourceOverride ? SourceCode(m_sourceOverride) : ownerSource; - unsigned firstLine = source.firstLine() + m_firstLineOffset; - unsigned startOffset = source.startOffset() + m_startOffset; - unsigned lineCount = m_lineCount; - - // Adjust to one-based indexing. - bool startColumnIsOnFirstSourceLine = !m_firstLineOffset; - unsigned startColumn = m_unlinkedBodyStartColumn + (startColumnIsOnFirstSourceLine ? source.startColumn() : 1); - bool endColumnIsOnStartLine = !lineCount; - unsigned endColumn = m_unlinkedBodyEndColumn + (endColumnIsOnStartLine ? startColumn : 1); - - SourceCode code(source.provider(), startOffset, startOffset + m_sourceLength, firstLine, startColumn); - FunctionOverrides::OverrideInfo overrideInfo; - bool hasFunctionOverride = false; - - if (UNLIKELY(Options::functionOverrides())) { - hasFunctionOverride = FunctionOverrides::initializeOverrideFor(code, overrideInfo); - if (hasFunctionOverride) { - firstLine = overrideInfo.firstLine; - lineCount = overrideInfo.lineCount; - startColumn = overrideInfo.startColumn; - endColumn = overrideInfo.endColumn; - code = overrideInfo.sourceCode; - } - } - - FunctionExecutable* result = FunctionExecutable::create(vm, code, this, firstLine, firstLine + lineCount, startColumn, endColumn); - if (overrideLineNumber != -1) - result->setOverrideLineNumber(overrideLineNumber); - - if (UNLIKELY(hasFunctionOverride)) { - result->overrideParameterAndTypeProfilingStartEndOffsets( - overrideInfo.parametersStartOffset, - overrideInfo.typeProfilingStartOffset, - overrideInfo.typeProfilingEndOffset); - } - - return result; -} - -UnlinkedFunctionExecutable* UnlinkedFunctionExecutable::fromGlobalCode( - const Identifier& name, ExecState& exec, const SourceCode& source, - JSObject*& exception, int overrideLineNumber) -{ - ParserError error; - VM& vm = exec.vm(); - CodeCache* codeCache = vm.codeCache(); - UnlinkedFunctionExecutable* executable = codeCache->getFunctionExecutableFromGlobalCode(vm, name, source, error); - - auto& globalObject = *exec.lexicalGlobalObject(); - if (globalObject.hasDebugger()) - globalObject.debugger()->sourceParsed(&exec, source.provider(), error.line(), error.message()); - - if (error.isValid()) { - exception = error.toErrorObject(&globalObject, source, overrideLineNumber); - return nullptr; - } - - return executable; -} - -UnlinkedFunctionCodeBlock* UnlinkedFunctionExecutable::codeBlockFor( - VM& vm, const SourceCode& source, CodeSpecializationKind specializationKind, - DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error) -{ - switch (specializationKind) { - case CodeForCall: - if (UnlinkedFunctionCodeBlock* codeBlock = m_codeBlockForCall.get()) - return codeBlock; - break; - case CodeForConstruct: - if (UnlinkedFunctionCodeBlock* codeBlock = m_codeBlockForConstruct.get()) - return codeBlock; - break; - } - - UnlinkedFunctionCodeBlock* result = generateFunctionCodeBlock( - vm, this, source, specializationKind, debuggerMode, profilerMode, - isBuiltinFunction() ? UnlinkedBuiltinFunction : UnlinkedNormalFunction, - error); - - if (error.isValid()) - return nullptr; - - switch (specializationKind) { - case CodeForCall: - m_codeBlockForCall.set(vm, this, result); - break; - case CodeForConstruct: - m_codeBlockForConstruct.set(vm, this, result); - break; - } - return result; -} - -} // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h b/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h deleted file mode 100644 index 6eae9f0a2..000000000 --- a/Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2012-2015 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 UnlinkedFunctionExecutable_h -#define UnlinkedFunctionExecutable_h - -#include "BytecodeConventions.h" -#include "CodeSpecializationKind.h" -#include "CodeType.h" -#include "ConstructAbility.h" -#include "ExpressionRangeInfo.h" -#include "HandlerInfo.h" -#include "Identifier.h" -#include "JSCell.h" -#include "JSString.h" -#include "ParserModes.h" -#include "RegExp.h" -#include "SpecialPointer.h" -#include "VariableEnvironment.h" -#include "VirtualRegister.h" -#include <wtf/RefCountedArray.h> -#include <wtf/Vector.h> - -namespace JSC { - -class FunctionMetadataNode; -class FunctionExecutable; -class ParserError; -class SourceCode; -class SourceProvider; -class UnlinkedFunctionCodeBlock; - -enum UnlinkedFunctionKind { - UnlinkedNormalFunction, - UnlinkedBuiltinFunction, -}; - -class UnlinkedFunctionExecutable final : public JSCell { -public: - friend class BuiltinExecutables; - friend class CodeCache; - friend class VM; - - typedef JSCell Base; - static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; - - static UnlinkedFunctionExecutable* create(VM* vm, const SourceCode& source, FunctionMetadataNode* node, UnlinkedFunctionKind unlinkedFunctionKind, ConstructAbility constructAbility, VariableEnvironment& parentScopeTDZVariables, RefPtr<SourceProvider>&& sourceOverride = nullptr) - { - UnlinkedFunctionExecutable* instance = new (NotNull, allocateCell<UnlinkedFunctionExecutable>(vm->heap)) - UnlinkedFunctionExecutable(vm, vm->unlinkedFunctionExecutableStructure.get(), source, WTF::move(sourceOverride), node, unlinkedFunctionKind, constructAbility, parentScopeTDZVariables); - instance->finishCreation(*vm); - return instance; - } - - const Identifier& name() const { return m_name; } - const Identifier& inferredName() const { return m_inferredName; } - JSString* nameValue() const { return m_nameValue.get(); } - unsigned parameterCount() const { return m_parameterCount; }; - SourceParseMode parseMode() const { return m_parseMode; }; - bool isInStrictContext() const { return m_isInStrictContext; } - FunctionMode functionMode() const { return static_cast<FunctionMode>(m_functionMode); } - ConstructorKind constructorKind() const { return static_cast<ConstructorKind>(m_constructorKind); } - - unsigned unlinkedFunctionNameStart() const { return m_unlinkedFunctionNameStart; } - unsigned unlinkedBodyStartColumn() const { return m_unlinkedBodyStartColumn; } - unsigned unlinkedBodyEndColumn() const { return m_unlinkedBodyEndColumn; } - unsigned startOffset() const { return m_startOffset; } - unsigned sourceLength() { return m_sourceLength; } - unsigned parametersStartOffset() const { return m_parametersStartOffset; } - unsigned typeProfilingStartOffset() const { return m_typeProfilingStartOffset; } - unsigned typeProfilingEndOffset() const { return m_typeProfilingEndOffset; } - - UnlinkedFunctionCodeBlock* codeBlockFor( - VM&, const SourceCode&, CodeSpecializationKind, DebuggerMode, ProfilerMode, - ParserError&); - - static UnlinkedFunctionExecutable* fromGlobalCode( - const Identifier&, ExecState&, const SourceCode&, JSObject*& exception, - int overrideLineNumber); - - FunctionExecutable* link(VM&, const SourceCode&, int overrideLineNumber = -1); - - void clearCodeForRecompilation() - { - m_codeBlockForCall.clear(); - m_codeBlockForConstruct.clear(); - } - - void recordParse(CodeFeatures features, bool hasCapturedVariables) - { - m_features = features; - m_hasCapturedVariables = hasCapturedVariables; - } - - CodeFeatures features() const { return m_features; } - bool hasCapturedVariables() const { return m_hasCapturedVariables; } - - static const bool needsDestruction = true; - static void destroy(JSCell*); - - bool isBuiltinFunction() const { return m_isBuiltinFunction; } - ConstructAbility constructAbility() const { return static_cast<ConstructAbility>(m_constructAbility); } - bool isClassConstructorFunction() const { return constructorKind() != ConstructorKind::None; } - const VariableEnvironment* parentScopeTDZVariables() const { return &m_parentScopeTDZVariables; } - -private: - UnlinkedFunctionExecutable(VM*, Structure*, const SourceCode&, RefPtr<SourceProvider>&& sourceOverride, FunctionMetadataNode*, UnlinkedFunctionKind, ConstructAbility, VariableEnvironment&); - WriteBarrier<UnlinkedFunctionCodeBlock> m_codeBlockForCall; - WriteBarrier<UnlinkedFunctionCodeBlock> m_codeBlockForConstruct; - - Identifier m_name; - Identifier m_inferredName; - WriteBarrier<JSString> m_nameValue; - RefPtr<SourceProvider> m_sourceOverride; - VariableEnvironment m_parentScopeTDZVariables; - unsigned m_firstLineOffset; - unsigned m_lineCount; - unsigned m_unlinkedFunctionNameStart; - unsigned m_unlinkedBodyStartColumn; - unsigned m_unlinkedBodyEndColumn; - unsigned m_startOffset; - unsigned m_sourceLength; - unsigned m_parametersStartOffset; - unsigned m_typeProfilingStartOffset; - unsigned m_typeProfilingEndOffset; - unsigned m_parameterCount; - SourceParseMode m_parseMode; - - CodeFeatures m_features; - - unsigned m_isInStrictContext : 1; - unsigned m_hasCapturedVariables : 1; - unsigned m_isBuiltinFunction : 1; - unsigned m_constructAbility: 1; - unsigned m_constructorKind : 2; - unsigned m_functionMode : 1; // FunctionMode - -protected: - void finishCreation(VM& vm) - { - Base::finishCreation(vm); - m_nameValue.set(vm, this, jsString(&vm, name().string())); - } - - static void visitChildren(JSCell*, SlotVisitor&); - -public: - static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) - { - return Structure::create(vm, globalObject, proto, TypeInfo(UnlinkedFunctionExecutableType, StructureFlags), info()); - } - - DECLARE_EXPORT_INFO; -}; - -} // namespace JSC - -#endif // UnlinkedFunctionExecutable_h diff --git a/Source/JavaScriptCore/bytecode/UnlinkedInstructionStream.cpp b/Source/JavaScriptCore/bytecode/UnlinkedInstructionStream.cpp index 568dbb682..2e07f4f47 100644 --- a/Source/JavaScriptCore/bytecode/UnlinkedInstructionStream.cpp +++ b/Source/JavaScriptCore/bytecode/UnlinkedInstructionStream.cpp @@ -28,6 +28,81 @@ namespace JSC { +// Unlinked instructions are packed in a simple stream format. +// +// The first byte is always the opcode. +// It's followed by an opcode-dependent number of argument values. +// The first 3 bits of each value determines the format: +// +// 5-bit positive integer (1 byte total) +// 5-bit negative integer (1 byte total) +// 13-bit positive integer (2 bytes total) +// 13-bit negative integer (2 bytes total) +// 5-bit constant register index, based at 0x40000000 (1 byte total) +// 13-bit constant register index, based at 0x40000000 (2 bytes total) +// 32-bit raw value (5 bytes total) + +enum PackedValueType { + Positive5Bit = 0, + Negative5Bit, + Positive13Bit, + Negative13Bit, + ConstantRegister5Bit, + ConstantRegister13Bit, + Full32Bit +}; + +UnlinkedInstructionStream::Reader::Reader(const UnlinkedInstructionStream& stream) + : m_stream(stream) + , m_index(0) +{ +} + +inline unsigned char UnlinkedInstructionStream::Reader::read8() +{ + return m_stream.m_data.data()[m_index++]; +} + +inline unsigned UnlinkedInstructionStream::Reader::read32() +{ + const unsigned char* data = &m_stream.m_data.data()[m_index]; + unsigned char type = data[0] >> 5; + + switch (type) { + case Positive5Bit: + m_index++; + return data[0]; + case Negative5Bit: + m_index++; + return 0xffffffe0 | data[0]; + case Positive13Bit: + m_index += 2; + return ((data[0] & 0x1F) << 8) | data[1]; + case Negative13Bit: + m_index += 2; + return 0xffffe000 | ((data[0] & 0x1F) << 8) | data[1]; + case ConstantRegister5Bit: + m_index++; + return 0x40000000 | (data[0] & 0x1F); + case ConstantRegister13Bit: + m_index += 2; + return 0x40000000 | ((data[0] & 0x1F) << 8) | data[1]; + default: + ASSERT(type == Full32Bit); + m_index += 5; + return data[1] | data[2] << 8 | data[3] << 16 | data[4] << 24; + } +} + +const UnlinkedInstruction* UnlinkedInstructionStream::Reader::next() +{ + m_unpackedBuffer[0].u.opcode = static_cast<OpcodeID>(read8()); + unsigned opLength = opcodeLength(m_unpackedBuffer[0].u.opcode); + for (unsigned i = 1; i < opLength; ++i) + m_unpackedBuffer[i].u.index = read32(); + return m_unpackedBuffer; +} + static void append8(unsigned char*& ptr, unsigned char value) { *(ptr++) = value; @@ -75,7 +150,7 @@ static void append32(unsigned char*& ptr, unsigned value) *(ptr++) = (value >> 24) & 0xff; } -UnlinkedInstructionStream::UnlinkedInstructionStream(const Vector<UnlinkedInstruction, 0, UnsafeVectorOverflow>& instructions) +UnlinkedInstructionStream::UnlinkedInstructionStream(const Vector<UnlinkedInstruction>& instructions) : m_instructionCount(instructions.size()) { Vector<unsigned char> buffer; diff --git a/Source/JavaScriptCore/bytecode/UnlinkedInstructionStream.h b/Source/JavaScriptCore/bytecode/UnlinkedInstructionStream.h index 6323c444b..5a919a29e 100644 --- a/Source/JavaScriptCore/bytecode/UnlinkedInstructionStream.h +++ b/Source/JavaScriptCore/bytecode/UnlinkedInstructionStream.h @@ -33,9 +33,8 @@ namespace JSC { class UnlinkedInstructionStream { - WTF_MAKE_FAST_ALLOCATED; public: - explicit UnlinkedInstructionStream(const Vector<UnlinkedInstruction, 0, UnsafeVectorOverflow>&); + explicit UnlinkedInstructionStream(const Vector<UnlinkedInstruction>&); unsigned count() const { return m_instructionCount; } @@ -70,81 +69,6 @@ private: unsigned m_instructionCount; }; -// Unlinked instructions are packed in a simple stream format. -// -// The first byte is always the opcode. -// It's followed by an opcode-dependent number of argument values. -// The first 3 bits of each value determines the format: -// -// 5-bit positive integer (1 byte total) -// 5-bit negative integer (1 byte total) -// 13-bit positive integer (2 bytes total) -// 13-bit negative integer (2 bytes total) -// 5-bit constant register index, based at 0x40000000 (1 byte total) -// 13-bit constant register index, based at 0x40000000 (2 bytes total) -// 32-bit raw value (5 bytes total) - -enum PackedValueType { - Positive5Bit = 0, - Negative5Bit, - Positive13Bit, - Negative13Bit, - ConstantRegister5Bit, - ConstantRegister13Bit, - Full32Bit -}; - -ALWAYS_INLINE UnlinkedInstructionStream::Reader::Reader(const UnlinkedInstructionStream& stream) - : m_stream(stream) - , m_index(0) -{ -} - -ALWAYS_INLINE unsigned char UnlinkedInstructionStream::Reader::read8() -{ - return m_stream.m_data.data()[m_index++]; -} - -ALWAYS_INLINE unsigned UnlinkedInstructionStream::Reader::read32() -{ - const unsigned char* data = &m_stream.m_data.data()[m_index]; - unsigned char type = data[0] >> 5; - - switch (type) { - case Positive5Bit: - m_index++; - return data[0]; - case Negative5Bit: - m_index++; - return 0xffffffe0 | data[0]; - case Positive13Bit: - m_index += 2; - return ((data[0] & 0x1F) << 8) | data[1]; - case Negative13Bit: - m_index += 2; - return 0xffffe000 | ((data[0] & 0x1F) << 8) | data[1]; - case ConstantRegister5Bit: - m_index++; - return 0x40000000 | (data[0] & 0x1F); - case ConstantRegister13Bit: - m_index += 2; - return 0x40000000 | ((data[0] & 0x1F) << 8) | data[1]; - default: - ASSERT(type == Full32Bit); - m_index += 5; - return data[1] | data[2] << 8 | data[3] << 16 | data[4] << 24; - } -} - -ALWAYS_INLINE const UnlinkedInstruction* UnlinkedInstructionStream::Reader::next() -{ - m_unpackedBuffer[0].u.opcode = static_cast<OpcodeID>(read8()); - unsigned opLength = opcodeLength(m_unpackedBuffer[0].u.opcode); - for (unsigned i = 1; i < opLength; ++i) - m_unpackedBuffer[i].u.index = read32(); - return m_unpackedBuffer; -} - } // namespace JSC #endif // UnlinkedInstructionStream_h diff --git a/Source/JavaScriptCore/bytecode/ValueProfile.h b/Source/JavaScriptCore/bytecode/ValueProfile.h index 99a9516c9..0790f79da 100644 --- a/Source/JavaScriptCore/bytecode/ValueProfile.h +++ b/Source/JavaScriptCore/bytecode/ValueProfile.h @@ -10,7 +10,7 @@ * 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 + * 3. Neither the name of Apple Computer, 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. * diff --git a/Source/JavaScriptCore/bytecode/ValueRecovery.cpp b/Source/JavaScriptCore/bytecode/ValueRecovery.cpp index 996fd3bfe..5032684dd 100644 --- a/Source/JavaScriptCore/bytecode/ValueRecovery.cpp +++ b/Source/JavaScriptCore/bytecode/ValueRecovery.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, 2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,7 +27,7 @@ #include "ValueRecovery.h" #include "CodeBlock.h" -#include "JSCInlines.h" +#include "Operations.h" namespace JSC { @@ -92,31 +92,28 @@ void ValueRecovery::dumpInContext(PrintStream& out, DumpContext* context) const return; #endif case DisplacedInJSStack: - out.print("*", virtualRegister()); + out.printf("*%d", virtualRegister().offset()); return; case Int32DisplacedInJSStack: - out.print("*int32(", virtualRegister(), ")"); + out.printf("*int32(%d)", virtualRegister().offset()); return; case Int52DisplacedInJSStack: - out.print("*int52(", virtualRegister(), ")"); + out.printf("*int52(%d)", virtualRegister().offset()); return; case StrictInt52DisplacedInJSStack: - out.print("*strictInt52(", virtualRegister(), ")"); + out.printf("*strictInt52(%d)", virtualRegister().offset()); return; case DoubleDisplacedInJSStack: - out.print("*double(", virtualRegister(), ")"); + out.printf("*double(%d)", virtualRegister().offset()); return; case CellDisplacedInJSStack: - out.print("*cell(", virtualRegister(), ")"); + out.printf("*cell(%d)", virtualRegister().offset()); return; case BooleanDisplacedInJSStack: - out.print("*bool(", virtualRegister(), ")"); + out.printf("*bool(%d)", virtualRegister().offset()); return; - case DirectArgumentsThatWereNotCreated: - out.print("DirectArguments(", nodeID(), ")"); - return; - case ClonedArgumentsThatWereNotCreated: - out.print("ClonedArguments(", nodeID(), ")"); + case ArgumentsThatWereNotCreated: + out.printf("arguments"); return; case Constant: out.print("[", inContext(constant(), context), "]"); diff --git a/Source/JavaScriptCore/bytecode/ValueRecovery.h b/Source/JavaScriptCore/bytecode/ValueRecovery.h index 42651e2c7..3af2c3409 100644 --- a/Source/JavaScriptCore/bytecode/ValueRecovery.h +++ b/Source/JavaScriptCore/bytecode/ValueRecovery.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, 2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,7 +26,6 @@ #ifndef ValueRecovery_h #define ValueRecovery_h -#include "DFGMinifiedID.h" #include "DataFormat.h" #if ENABLE(JIT) #include "GPRInfo.h" @@ -35,11 +34,11 @@ #include "JSCJSValue.h" #include "MacroAssembler.h" #include "VirtualRegister.h" +#include <wtf/Platform.h> namespace JSC { struct DumpContext; -struct InlineCallFrame; // Describes how to recover a given bytecode virtual register at a given // code point. @@ -64,9 +63,8 @@ enum ValueRecoveryTechnique { DoubleDisplacedInJSStack, CellDisplacedInJSStack, BooleanDisplacedInJSStack, - // It's an Arguments object. This arises because of the simplified arguments simplification done by the DFG. - DirectArgumentsThatWereNotCreated, - ClonedArgumentsThatWereNotCreated, + // It's an Arguments object. + ArgumentsThatWereNotCreated, // It's a constant. Constant, // Don't know how to recover it. @@ -170,19 +168,10 @@ public: return result; } - static ValueRecovery directArgumentsThatWereNotCreated(DFG::MinifiedID id) + static ValueRecovery argumentsThatWereNotCreated() { ValueRecovery result; - result.m_technique = DirectArgumentsThatWereNotCreated; - result.m_source.nodeID = id.bits(); - return result; - } - - static ValueRecovery outOfBandArgumentsThatWereNotCreated(DFG::MinifiedID id) - { - ValueRecovery result; - result.m_technique = ClonedArgumentsThatWereNotCreated; - result.m_source.nodeID = id.bits(); + result.m_technique = ArgumentsThatWereNotCreated; return result; } @@ -241,39 +230,12 @@ public: return VirtualRegister(m_source.virtualReg); } - ValueRecovery withLocalsOffset(int offset) const - { - switch (m_technique) { - case DisplacedInJSStack: - case Int32DisplacedInJSStack: - case DoubleDisplacedInJSStack: - case CellDisplacedInJSStack: - case BooleanDisplacedInJSStack: - case Int52DisplacedInJSStack: - case StrictInt52DisplacedInJSStack: { - ValueRecovery result; - result.m_technique = m_technique; - result.m_source.virtualReg = m_source.virtualReg + offset; - return result; - } - - default: - return *this; - } - } - JSValue constant() const { ASSERT(m_technique == Constant); return JSValue::decode(m_source.constant); } - DFG::MinifiedID nodeID() const - { - ASSERT(m_technique == DirectArgumentsThatWereNotCreated || m_technique == ClonedArgumentsThatWereNotCreated); - return DFG::MinifiedID::fromBits(m_source.nodeID); - } - JSValue recover(ExecState*) const; #if ENABLE(JIT) @@ -294,7 +256,6 @@ private: #endif int virtualReg; EncodedJSValue constant; - uintptr_t nodeID; } m_source; }; diff --git a/Source/JavaScriptCore/bytecode/VariableWatchpointSet.h b/Source/JavaScriptCore/bytecode/VariableWatchpointSet.h new file mode 100644 index 000000000..4dec40495 --- /dev/null +++ b/Source/JavaScriptCore/bytecode/VariableWatchpointSet.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012, 2013 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 VariableWatchpointSet_h +#define VariableWatchpointSet_h + +#include "Watchpoint.h" +#include "WriteBarrier.h" + +namespace JSC { + +class VariableWatchpointSet : public WatchpointSet { + friend class LLIntOffsetsExtractor; +public: + VariableWatchpointSet() + : WatchpointSet(ClearWatchpoint) + { + } + + ~VariableWatchpointSet() { } + + // For the purpose of deciding whether or not to watch this variable, you only need + // to inspect inferredValue(). If this returns something other than the empty + // value, then it means that at all future safepoints, this watchpoint set will be + // in one of these states: + // + // IsWatched: in this case, the variable's value must still be the + // inferredValue. + // + // IsInvalidated: in this case the variable's value may be anything but you'll + // either notice that it's invalidated and not install the watchpoint, or + // you will have been notified that the watchpoint was fired. + JSValue inferredValue() const { return m_inferredValue; } + + void notifyWrite(JSValue value) + { + ASSERT(!!value); + switch (state()) { + case ClearWatchpoint: + m_inferredValue = value; + startWatching(); + return; + + case IsWatched: + ASSERT(!!m_inferredValue); + if (value == m_inferredValue) + return; + invalidate(); + return; + + case IsInvalidated: + ASSERT(!m_inferredValue); + return; + } + + ASSERT_NOT_REACHED(); + } + + void invalidate() + { + m_inferredValue = JSValue(); + WatchpointSet::invalidate(); + } + + void finalizeUnconditionally() + { + ASSERT(!!m_inferredValue == (state() == IsWatched)); + if (!m_inferredValue) + return; + if (!m_inferredValue.isCell()) + return; + JSCell* cell = m_inferredValue.asCell(); + if (Heap::isMarked(cell)) + return; + invalidate(); + } + + JSValue* addressOfInferredValue() { return &m_inferredValue; } + +private: + JSValue m_inferredValue; +}; + +} // namespace JSC + +#endif // VariableWatchpointSet_h + diff --git a/Source/JavaScriptCore/bytecode/VariableWriteFireDetail.cpp b/Source/JavaScriptCore/bytecode/VariableWriteFireDetail.cpp deleted file mode 100644 index b483ab21c..000000000 --- a/Source/JavaScriptCore/bytecode/VariableWriteFireDetail.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -#include "config.h" -#include "VariableWriteFireDetail.h" - -#include "JSCInlines.h" - -namespace JSC { - -void VariableWriteFireDetail::dump(PrintStream& out) const -{ - out.print("Write to ", m_name, " in ", JSValue(m_object)); -} - -void VariableWriteFireDetail::touch(WatchpointSet* set, JSObject* object, const PropertyName& name) -{ - set->touch(VariableWriteFireDetail(object, name)); -} - -} // namespace JSC - diff --git a/Source/JavaScriptCore/bytecode/VariableWriteFireDetail.h b/Source/JavaScriptCore/bytecode/VariableWriteFireDetail.h deleted file mode 100644 index 664f69cbb..000000000 --- a/Source/JavaScriptCore/bytecode/VariableWriteFireDetail.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2015 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 VariableWriteFireDetail_h -#define VariableWriteFireDetail_h - -#include "Watchpoint.h" - -namespace JSC { - -class JSObject; -class PropertyName; - -class VariableWriteFireDetail : public FireDetail { -public: - VariableWriteFireDetail(JSObject* object, const PropertyName& name) - : m_object(object) - , m_name(name) - { - } - - virtual void dump(PrintStream&) const override; - - JS_EXPORT_PRIVATE static void touch(WatchpointSet*, JSObject*, const PropertyName&); - -private: - JSObject* m_object; - const PropertyName& m_name; -}; - -} // namespace JSC - -#endif // VariableWriteFireDetail_h diff --git a/Source/JavaScriptCore/bytecode/VirtualRegister.cpp b/Source/JavaScriptCore/bytecode/VirtualRegister.cpp deleted file mode 100644 index 57cdb62c9..000000000 --- a/Source/JavaScriptCore/bytecode/VirtualRegister.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2011, 2015 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. - */ - -#include "config.h" -#include "VirtualRegister.h" - -namespace JSC { - -void VirtualRegister::dump(PrintStream& out) const -{ - if (!isValid()) { - out.print("<invalid>"); - return; - } - - if (isHeader()) { - out.print("head", m_virtualRegister); - return; - } - - if (isConstant()) { - out.print("const", toConstantIndex()); - return; - } - - if (isArgument()) { - if (!toArgument()) - out.print("this"); - else - out.print("arg", toArgument()); - return; - } - - if (isLocal()) { - out.print("loc", toLocal()); - return; - } - - RELEASE_ASSERT_NOT_REACHED(); -} - -} // namespace JSC - diff --git a/Source/JavaScriptCore/bytecode/VirtualRegister.h b/Source/JavaScriptCore/bytecode/VirtualRegister.h index 613088ef6..c63aee85f 100644 --- a/Source/JavaScriptCore/bytecode/VirtualRegister.h +++ b/Source/JavaScriptCore/bytecode/VirtualRegister.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2011 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,6 +28,7 @@ #include "CallFrame.h" +#include <wtf/Platform.h> #include <wtf/PrintStream.h> namespace JSC { @@ -59,47 +60,14 @@ public: bool isValid() const { return (m_virtualRegister != s_invalidVirtualRegister); } bool isLocal() const { return operandIsLocal(m_virtualRegister); } bool isArgument() const { return operandIsArgument(m_virtualRegister); } - bool isHeader() const { return m_virtualRegister >= 0 && m_virtualRegister < JSStack::ThisArgument; } bool isConstant() const { return m_virtualRegister >= s_firstConstantRegisterIndex; } int toLocal() const { ASSERT(isLocal()); return operandToLocal(m_virtualRegister); } int toArgument() const { ASSERT(isArgument()); return operandToArgument(m_virtualRegister); } int toConstantIndex() const { ASSERT(isConstant()); return m_virtualRegister - s_firstConstantRegisterIndex; } int offset() const { return m_virtualRegister; } - int offsetInBytes() const { return m_virtualRegister * sizeof(Register); } - - bool operator==(VirtualRegister other) const { return m_virtualRegister == other.m_virtualRegister; } - bool operator!=(VirtualRegister other) const { return m_virtualRegister != other.m_virtualRegister; } - bool operator<(VirtualRegister other) const { return m_virtualRegister < other.m_virtualRegister; } - bool operator>(VirtualRegister other) const { return m_virtualRegister > other.m_virtualRegister; } - bool operator<=(VirtualRegister other) const { return m_virtualRegister <= other.m_virtualRegister; } - bool operator>=(VirtualRegister other) const { return m_virtualRegister >= other.m_virtualRegister; } - - VirtualRegister operator+(int value) const - { - return VirtualRegister(offset() + value); - } - VirtualRegister operator-(int value) const - { - return VirtualRegister(offset() - value); - } - VirtualRegister operator+(VirtualRegister value) const - { - return VirtualRegister(offset() + value.offset()); - } - VirtualRegister operator-(VirtualRegister value) const - { - return VirtualRegister(offset() - value.offset()); - } - VirtualRegister& operator+=(int value) - { - return *this = *this + value; - } - VirtualRegister& operator-=(int value) - { - return *this = *this - value; - } - - void dump(PrintStream& out) const; + + bool operator==(const VirtualRegister other) const { return m_virtualRegister == other.m_virtualRegister; } + bool operator!=(const VirtualRegister other) const { return m_virtualRegister != other.m_virtualRegister; } private: static const int s_invalidVirtualRegister = 0x3fffffff; @@ -127,4 +95,13 @@ inline VirtualRegister virtualRegisterForArgument(int argument, int offset = 0) } // namespace JSC +namespace WTF { + +inline void printInternal(PrintStream& out, JSC::VirtualRegister value) +{ + out.print(value.offset()); +} + +} // namespace WTF + #endif // VirtualRegister_h diff --git a/Source/JavaScriptCore/bytecode/Watchpoint.cpp b/Source/JavaScriptCore/bytecode/Watchpoint.cpp index 761c06744..f29c2141c 100644 --- a/Source/JavaScriptCore/bytecode/Watchpoint.cpp +++ b/Source/JavaScriptCore/bytecode/Watchpoint.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2015 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,32 +26,16 @@ #include "config.h" #include "Watchpoint.h" +#include "LinkBuffer.h" #include <wtf/CompilationThread.h> #include <wtf/PassRefPtr.h> namespace JSC { -void StringFireDetail::dump(PrintStream& out) const -{ - out.print(m_string); -} - Watchpoint::~Watchpoint() { - if (isOnList()) { - // This will happen if we get destroyed before the set fires. That's totally a valid - // possibility. For example: - // - // CodeBlock has a Watchpoint on transition from structure S1. The transition never - // happens, but the CodeBlock gets destroyed because of GC. + if (isOnList()) remove(); - } -} - -void Watchpoint::fire(const FireDetail& detail) -{ - RELEASE_ASSERT(!isOnList()); - fireInternal(detail); } WatchpointSet::WatchpointSet(WatchpointState state) @@ -81,48 +65,20 @@ void WatchpointSet::add(Watchpoint* watchpoint) m_state = IsWatched; } -void WatchpointSet::fireAllSlow(const FireDetail& detail) +void WatchpointSet::fireAllSlow() { ASSERT(state() == IsWatched); WTF::storeStoreFence(); - m_state = IsInvalidated; // Do this first. Needed for adaptive watchpoints. - fireAllWatchpoints(detail); + fireAllWatchpoints(); + m_state = IsInvalidated; WTF::storeStoreFence(); } -void WatchpointSet::fireAllSlow(const char* reason) -{ - fireAllSlow(StringFireDetail(reason)); -} - -void WatchpointSet::fireAllWatchpoints(const FireDetail& detail) +void WatchpointSet::fireAllWatchpoints() { - // In case there are any adaptive watchpoints, we need to make sure that they see that this - // watchpoint has been already invalidated. - RELEASE_ASSERT(hasBeenInvalidated()); - - while (!m_set.isEmpty()) { - Watchpoint* watchpoint = m_set.begin(); - ASSERT(watchpoint->isOnList()); - - // Removing the Watchpoint before firing it makes it possible to implement watchpoints - // that add themselves to a different set when they fire. This kind of "adaptive" - // watchpoint can be used to track some semantic property that is more fine-graiend than - // what the set can convey. For example, we might care if a singleton object ever has a - // property called "foo". We can watch for this by checking if its Structure has "foo" and - // then watching its transitions. But then the watchpoint fires if any property is added. - // So, before the watchpoint decides to invalidate any code, it can check if it is - // possible to add itself to the transition watchpoint set of the singleton object's new - // Structure. - watchpoint->remove(); - ASSERT(m_set.begin() != watchpoint); - ASSERT(!watchpoint->isOnList()); - - watchpoint->fire(detail); - // After we fire the watchpoint, the watchpoint pointer may be a dangling pointer. That's - // fine, because we have no use for the pointer anymore. - } + while (!m_set.isEmpty()) + m_set.begin()->fire(); } void InlineWatchpointSet::add(Watchpoint* watchpoint) @@ -130,11 +86,6 @@ void InlineWatchpointSet::add(Watchpoint* watchpoint) inflate()->add(watchpoint); } -void InlineWatchpointSet::fireAll(const char* reason) -{ - fireAll(StringFireDetail(reason)); -} - WatchpointSet* InlineWatchpointSet::inflateSlow() { ASSERT(isThin()); diff --git a/Source/JavaScriptCore/bytecode/Watchpoint.h b/Source/JavaScriptCore/bytecode/Watchpoint.h index c8f628d33..8790f4e62 100644 --- a/Source/JavaScriptCore/bytecode/Watchpoint.h +++ b/Source/JavaScriptCore/bytecode/Watchpoint.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2015 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,47 +27,12 @@ #define Watchpoint_h #include <wtf/Atomics.h> -#include <wtf/FastMalloc.h> -#include <wtf/Noncopyable.h> -#include <wtf/PrintStream.h> #include <wtf/SentinelLinkedList.h> #include <wtf/ThreadSafeRefCounted.h> namespace JSC { -class FireDetail { - void* operator new(size_t) = delete; - -public: - FireDetail() - { - } - - virtual ~FireDetail() - { - } - - virtual void dump(PrintStream&) const = 0; -}; - -class StringFireDetail : public FireDetail { -public: - StringFireDetail(const char* string) - : m_string(string) - { - } - - virtual void dump(PrintStream& out) const override; - -private: - const char* m_string; -}; - -class WatchpointSet; - class Watchpoint : public BasicRawSentinelNode<Watchpoint> { - WTF_MAKE_NONCOPYABLE(Watchpoint); - WTF_MAKE_FAST_ALLOCATED; public: Watchpoint() { @@ -75,12 +40,10 @@ public: virtual ~Watchpoint(); + void fire() { fireInternal(); } + protected: - virtual void fireInternal(const FireDetail&) = 0; - -private: - friend class WatchpointSet; - void fire(const FireDetail&); + virtual void fireInternal() = 0; }; enum WatchpointState { @@ -94,14 +57,8 @@ class InlineWatchpointSet; class WatchpointSet : public ThreadSafeRefCounted<WatchpointSet> { friend class LLIntOffsetsExtractor; public: - JS_EXPORT_PRIVATE WatchpointSet(WatchpointState); - JS_EXPORT_PRIVATE ~WatchpointSet(); // Note that this will not fire any of the watchpoints; if you need to know when a WatchpointSet dies then you need a separate mechanism for this. - - // Fast way of getting the state, which only works from the main thread. - WatchpointState stateOnJSThread() const - { - return static_cast<WatchpointState>(m_state); - } + WatchpointSet(WatchpointState); + ~WatchpointSet(); // Note that this will not fire any of the watchpoints; if you need to know when a WatchpointSet dies then you need a separate mechanism for this. // It is safe to call this from another thread. It may return an old // state. Guarantees that if *first* read the state() of the thing being @@ -141,66 +98,39 @@ public: // set watchpoints that we believe will actually be fired. void startWatching() { - ASSERT(m_state != IsInvalidated); - if (m_state == IsWatched) - return; - WTF::storeStoreFence(); + ASSERT(state() != IsInvalidated); m_state = IsWatched; - WTF::storeStoreFence(); } - void fireAll(const FireDetail& detail) + void fireAll() { - if (LIKELY(m_state != IsWatched)) + if (state() != IsWatched) return; - fireAllSlow(detail); + fireAllSlow(); } - void fireAll(const char* reason) - { - if (LIKELY(m_state != IsWatched)) - return; - fireAllSlow(reason); - } - - void touch(const FireDetail& detail) + void touch() { if (state() == ClearWatchpoint) startWatching(); else - fireAll(detail); + fireAll(); } - void touch(const char* reason) - { - touch(StringFireDetail(reason)); - } - - void invalidate(const FireDetail& detail) + void invalidate() { if (state() == IsWatched) - fireAll(detail); + fireAll(); m_state = IsInvalidated; } - - void invalidate(const char* reason) - { - invalidate(StringFireDetail(reason)); - } - - bool isBeingWatched() const - { - return m_setIsNotEmpty; - } - + int8_t* addressOfState() { return &m_state; } int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty; } - JS_EXPORT_PRIVATE void fireAllSlow(const FireDetail&); // Call only if you've checked isWatched. - JS_EXPORT_PRIVATE void fireAllSlow(const char* reason); // Ditto. + JS_EXPORT_PRIVATE void fireAllSlow(); // Call only if you've checked isWatched. private: - void fireAllWatchpoints(const FireDetail&); + void fireAllWatchpoints(); friend class InlineWatchpointSet; @@ -244,34 +174,18 @@ public: freeFat(); } - // Fast way of getting the state, which only works from the main thread. - WatchpointState stateOnJSThread() const - { - uintptr_t data = m_data; - if (isFat(data)) - return fat(data)->stateOnJSThread(); - return decodeState(data); - } - - // It is safe to call this from another thread. It may return a prior state, - // but that should be fine since you should only perform actions based on the - // state if you also add a watchpoint. - WatchpointState state() const - { - WTF::loadLoadFence(); - uintptr_t data = m_data; - WTF::loadLoadFence(); - if (isFat(data)) - return fat(data)->state(); - return decodeState(data); - } - // It is safe to call this from another thread. It may return false // even if the set actually had been invalidated, but that ought to happen // only in the case of races, and should be rare. bool hasBeenInvalidated() const { - return state() == IsInvalidated; + WTF::loadLoadFence(); + uintptr_t data = m_data; + if (isFat(data)) { + WTF::loadLoadFence(); + return fat(data)->hasBeenInvalidated(); + } + return decodeState(data) == IsInvalidated; } // Like hasBeenInvalidated(), may be called from another thread. @@ -292,10 +206,10 @@ public: m_data = encodeState(IsWatched); } - void fireAll(const FireDetail& detail) + void fireAll() { if (isFat()) { - fat()->fireAll(detail); + fat()->fireAll(); return; } if (decodeState(m_data) == ClearWatchpoint) @@ -304,45 +218,19 @@ public: WTF::storeStoreFence(); } - void invalidate(const FireDetail& detail) - { - if (isFat()) - fat()->invalidate(detail); - else - m_data = encodeState(IsInvalidated); - } - - JS_EXPORT_PRIVATE void fireAll(const char* reason); - - void touch(const FireDetail& detail) + void touch() { if (isFat()) { - fat()->touch(detail); + fat()->touch(); return; } - uintptr_t data = m_data; - if (decodeState(data) == IsInvalidated) - return; - WTF::storeStoreFence(); - if (decodeState(data) == ClearWatchpoint) + if (decodeState(m_data) == ClearWatchpoint) m_data = encodeState(IsWatched); else m_data = encodeState(IsInvalidated); WTF::storeStoreFence(); } - void touch(const char* reason) - { - touch(StringFireDetail(reason)); - } - - bool isBeingWatched() const - { - if (isFat()) - return fat()->isBeingWatched(); - return false; - } - private: static const uintptr_t IsThinFlag = 1; static const uintptr_t StateMask = 6; @@ -359,7 +247,7 @@ private: static uintptr_t encodeState(WatchpointState state) { - return (static_cast<uintptr_t>(state) << StateShift) | IsThinFlag; + return (state << StateShift) | IsThinFlag; } bool isThin() const { return isThin(m_data); } |
