[JSC] Compress Watchpoint size by using enum type and Packed<> data structure
authorysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 12 May 2019 22:50:21 +0000 (22:50 +0000)
committerysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 12 May 2019 22:50:21 +0000 (22:50 +0000)
https://bugs.webkit.org/show_bug.cgi?id=197730

Reviewed by Filip Pizlo.

Source/JavaScriptCore:

Watchpoint takes 5~ MB memory in Gmail (total memory starts with 400 - 500 MB), so 1~%. Since it is allocated massively,
reducing each size of Watchpoint reduces memory footprint significantly.

As a first step, this patch uses Packed<> and enum to reduce the size of Watchpoint.

1. Watchpoint should have enum type and should not use vtable. vtable takes one pointer, and it is too costly for such a
   memory sensitive objects. We perform downcast and dispatch the method of the derived classes based on this enum. Since
   the # of derived Watchpoint classes are limited (Only 8), we can list up them easily. One unfortunate thing is that
   we cannot do this for destructor so long as we use "delete" for deleting objects. If we dispatch the destructor of derived
   class in the destructor of the base class, we call the destructor of the base class multiple times. delete operator override
   does not help since custom delete operator is called after the destructor is called. While we can fix this issue by always
   using custom deleter, currently we do not since all the watchpoints do not have members which have non trivial destructor.
   Once it is strongly required, we can start using custom deleter, but for now, we do not need to do this.

2. We use Packed<> to compact pointers in Watchpoint. Since Watchpoint is a node of doubly linked list, each one has two
   pointers for prev and next. This is also too costly. PackedPtr reduces the size and makes alignment 1.S

3. We use PackedCellPtr<> for JSCells in Watchpoint. This leverages alignment information and makes pointers smaller in
   Darwin ARM64. One important thing to note here is that since this pointer is packed, it cannot be found by conservative
   GC scan. It is OK for watchpoint since they are allocated in the heap anyway.

We applied this change to Watchpoint and get the following memory reduction. The highlight is that CodeBlockJettisoningWatchpoint in
ARM64 only takes 2 pointers size.

                                                                      ORIGINAL    X86_64   ARM64
    WatchpointSet:                                                    40          32       28
    CodeBlockJettisoningWatchpoint:                                   32          19       15
    StructureStubClearingWatchpoint:                                  56          48       40
    AdaptiveInferredPropertyValueWatchpointBase::StructureWatchpoint: 24          13       11
    AdaptiveInferredPropertyValueWatchpointBase::PropertyWatchpoint:  24          13       11
    FunctionRareData::AllocationProfileClearingWatchpoint:            32          19       15
    ObjectToStringAdaptiveStructureWatchpoint:                        56          48       40
    LLIntPrototypeLoadAdaptiveStructureWatchpoint:                    64          48       48
    DFG::AdaptiveStructureWatchpoint:                                 56          48       40

While we will re-architect the mechanism of Watchpoint, anyway Packed<> mechanism and enum types will be used too.

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* Sources.txt:
* bytecode/AdaptiveInferredPropertyValueWatchpointBase.h:
* bytecode/CodeBlockJettisoningWatchpoint.h:
* bytecode/CodeOrigin.h:
* bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp:
(JSC::LLIntPrototypeLoadAdaptiveStructureWatchpoint::LLIntPrototypeLoadAdaptiveStructureWatchpoint):
(JSC::LLIntPrototypeLoadAdaptiveStructureWatchpoint::fireInternal):
* bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.h:
* bytecode/StructureStubClearingWatchpoint.cpp:
(JSC::StructureStubClearingWatchpoint::fireInternal):
* bytecode/StructureStubClearingWatchpoint.h:
* bytecode/Watchpoint.cpp:
(JSC::Watchpoint::fire):
* bytecode/Watchpoint.h:
(JSC::Watchpoint::Watchpoint):
* dfg/DFGAdaptiveStructureWatchpoint.cpp:
(JSC::DFG::AdaptiveStructureWatchpoint::AdaptiveStructureWatchpoint):
* dfg/DFGAdaptiveStructureWatchpoint.h:
* heap/PackedCellPtr.h: Added.
* runtime/FunctionRareData.h:
* runtime/ObjectToStringAdaptiveStructureWatchpoint.cpp: Added.
(JSC::ObjectToStringAdaptiveStructureWatchpoint::ObjectToStringAdaptiveStructureWatchpoint):
(JSC::ObjectToStringAdaptiveStructureWatchpoint::install):
(JSC::ObjectToStringAdaptiveStructureWatchpoint::fireInternal):
* runtime/ObjectToStringAdaptiveStructureWatchpoint.h: Added.
* runtime/StructureRareData.cpp:
(JSC::StructureRareData::clearObjectToStringValue):
(JSC::ObjectToStringAdaptiveStructureWatchpoint::ObjectToStringAdaptiveStructureWatchpoint): Deleted.
(JSC::ObjectToStringAdaptiveStructureWatchpoint::install): Deleted.
(JSC::ObjectToStringAdaptiveStructureWatchpoint::fireInternal): Deleted.
* runtime/StructureRareData.h:

Source/WTF:

This patch introduces a new data structures, WTF::Packed, WTF::PackedPtr, and WTF::PackedAlignedPtr.

- WTF::Packed

    WTF::Packed is data storage. We can read and write trivial (in C++ term [1]) data to this storage. The difference to
    the usual storage is that the alignment of this storage is always 1. We access the underlying data by using unalignedLoad/unalignedStore.
    This class offers alignment = 1 data structure instead of missing the following characteristics.

        1. Load / Store are non atomic even if the data size is within a pointer width. We should not use this for a member which can be accessed
           in a racy way. (e.g. fields accessed optimistically from the concurrent compilers).

        2. We cannot take reference / pointer to the underlying storage since they are unaligned.

        3. Access to this storage is unaligned access. The code is using memcpy, and the compiler will convert to an appropriate unaligned access
           in certain architectures (x86_64 / ARM64). It could be slow. So use it for non performance sensitive & memory sensitive places.

- WTF::PackedPtr

    WTF::PackedPtr is a specialization of WTF::Packed<T*>. And it is basically WTF::PackedAlignedPtr with alignment = 1. We further compact
    the pointer by leveraging the platform specific knowledge. In 64bit architectures, the effective width of pointers are less than 64 bit.
    In x86_64, it is 48 bits. And Darwin ARM64 is further smaller, 36 bits. This information allows us to compact the pointer to 6 bytes in
    x86_64 and 5 bytes in Darwin ARM64.

- WTF::PackedAlignedPtr

    WTF::PackedAlignedPtr is the WTF::PackedPtr with alignment information of the T. If we use this alignment information, we could reduce the
    size of packed pointer further in some cases. For example, since we guarantee that JSCells are 16 byte aligned, low 4 bits are empty. Leveraging
    this information in Darwin ARM64 platform allows us to make packed JSCell pointer 4 bytes (36 - 4 bits). We do not use passed alignment
    information if it is not profitable.

We also have PackedPtrTraits. This is new PtrTraits and use it for various data structures such as Bag<>.

[1]: https://en.cppreference.com/w/cpp/types/is_trivial

* WTF.xcodeproj/project.pbxproj:
* wtf/Bag.h:
(WTF::Bag::clear):
(WTF::Bag::iterator::operator++):
* wtf/CMakeLists.txt:
* wtf/DumbPtrTraits.h:
* wtf/DumbValueTraits.h:
* wtf/MathExtras.h:
(WTF::clzConstexpr):
(WTF::clz):
(WTF::ctzConstexpr):
(WTF::ctz):
(WTF::getLSBSetConstexpr):
(WTF::getMSBSetConstexpr):
* wtf/Packed.h: Added.
(WTF::Packed::Packed):
(WTF::Packed::get const):
(WTF::Packed::set):
(WTF::Packed::operator=):
(WTF::Packed::exchange):
(WTF::Packed::swap):
(WTF::alignof):
(WTF::PackedPtrTraits::exchange):
(WTF::PackedPtrTraits::swap):
(WTF::PackedPtrTraits::unwrap):
* wtf/Platform.h:
* wtf/SentinelLinkedList.h:
(WTF::BasicRawSentinelNode::BasicRawSentinelNode):
(WTF::BasicRawSentinelNode::prev):
(WTF::BasicRawSentinelNode::next):
(WTF::PtrTraits>::remove):
(WTF::PtrTraits>::prepend):
(WTF::PtrTraits>::append):
(WTF::RawNode>::SentinelLinkedList):
(WTF::RawNode>::remove):
(WTF::BasicRawSentinelNode<T>::remove): Deleted.
(WTF::BasicRawSentinelNode<T>::prepend): Deleted.
(WTF::BasicRawSentinelNode<T>::append): Deleted.
* wtf/StdLibExtras.h:
(WTF::roundUpToMultipleOfImpl):
(WTF::roundUpToMultipleOfImpl0): Deleted.
* wtf/UnalignedAccess.h:
(WTF::unalignedLoad):
(WTF::unalignedStore):

Tools:

* TestWebKitAPI/CMakeLists.txt:
* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WTF/MathExtras.cpp:
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WTF/Packed.cpp: Added.
(TestWebKitAPI::TEST):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@245214 268f45cc-cd09-0410-ab3c-d52691b4dbfc

38 files changed:
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/Sources.txt
Source/JavaScriptCore/bytecode/AdaptiveInferredPropertyValueWatchpointBase.h
Source/JavaScriptCore/bytecode/CodeBlockJettisoningWatchpoint.h
Source/JavaScriptCore/bytecode/CodeOrigin.h
Source/JavaScriptCore/bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp
Source/JavaScriptCore/bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.h
Source/JavaScriptCore/bytecode/StructureStubClearingWatchpoint.cpp
Source/JavaScriptCore/bytecode/StructureStubClearingWatchpoint.h
Source/JavaScriptCore/bytecode/Watchpoint.cpp
Source/JavaScriptCore/bytecode/Watchpoint.h
Source/JavaScriptCore/dfg/DFGAdaptiveStructureWatchpoint.cpp
Source/JavaScriptCore/dfg/DFGAdaptiveStructureWatchpoint.h
Source/JavaScriptCore/heap/PackedCellPtr.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/FunctionRareData.h
Source/JavaScriptCore/runtime/ObjectToStringAdaptiveStructureWatchpoint.cpp [new file with mode: 0644]
Source/JavaScriptCore/runtime/ObjectToStringAdaptiveStructureWatchpoint.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/StructureRareData.cpp
Source/JavaScriptCore/runtime/StructureRareData.h
Source/WTF/ChangeLog
Source/WTF/WTF.xcodeproj/project.pbxproj
Source/WTF/wtf/Bag.h
Source/WTF/wtf/CMakeLists.txt
Source/WTF/wtf/DumbPtrTraits.h
Source/WTF/wtf/DumbValueTraits.h
Source/WTF/wtf/MathExtras.h
Source/WTF/wtf/Packed.h [new file with mode: 0644]
Source/WTF/wtf/Platform.h
Source/WTF/wtf/SentinelLinkedList.h
Source/WTF/wtf/StdLibExtras.h
Source/WTF/wtf/UnalignedAccess.h
Tools/ChangeLog
Tools/TestWebKitAPI/CMakeLists.txt
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WTF/MathExtras.cpp
Tools/TestWebKitAPI/Tests/WTF/Packed.cpp [new file with mode: 0644]

index fde79e2..5d6d170 100644 (file)
@@ -606,6 +606,7 @@ set(JavaScriptCore_PRIVATE_FRAMEWORK_HEADERS
     heap/MarkedSpace.h
     heap/MarkingConstraint.h
     heap/MutatorState.h
+    heap/PackedCellPtr.h
     heap/RegisterState.h
     heap/RunningScope.h
     heap/SimpleMarkingConstraint.h
index adea1fc..eeb5b5c 100644 (file)
@@ -1,5 +1,83 @@
 2019-05-12  Yusuke Suzuki  <ysuzuki@apple.com>
 
+        [JSC] Compress Watchpoint size by using enum type and Packed<> data structure
+        https://bugs.webkit.org/show_bug.cgi?id=197730
+
+        Reviewed by Filip Pizlo.
+
+        Watchpoint takes 5~ MB memory in Gmail (total memory starts with 400 - 500 MB), so 1~%. Since it is allocated massively,
+        reducing each size of Watchpoint reduces memory footprint significantly.
+
+        As a first step, this patch uses Packed<> and enum to reduce the size of Watchpoint.
+
+        1. Watchpoint should have enum type and should not use vtable. vtable takes one pointer, and it is too costly for such a
+           memory sensitive objects. We perform downcast and dispatch the method of the derived classes based on this enum. Since
+           the # of derived Watchpoint classes are limited (Only 8), we can list up them easily. One unfortunate thing is that
+           we cannot do this for destructor so long as we use "delete" for deleting objects. If we dispatch the destructor of derived
+           class in the destructor of the base class, we call the destructor of the base class multiple times. delete operator override
+           does not help since custom delete operator is called after the destructor is called. While we can fix this issue by always
+           using custom deleter, currently we do not since all the watchpoints do not have members which have non trivial destructor.
+           Once it is strongly required, we can start using custom deleter, but for now, we do not need to do this.
+
+        2. We use Packed<> to compact pointers in Watchpoint. Since Watchpoint is a node of doubly linked list, each one has two
+           pointers for prev and next. This is also too costly. PackedPtr reduces the size and makes alignment 1.S
+
+        3. We use PackedCellPtr<> for JSCells in Watchpoint. This leverages alignment information and makes pointers smaller in
+           Darwin ARM64. One important thing to note here is that since this pointer is packed, it cannot be found by conservative
+           GC scan. It is OK for watchpoint since they are allocated in the heap anyway.
+
+        We applied this change to Watchpoint and get the following memory reduction. The highlight is that CodeBlockJettisoningWatchpoint in
+        ARM64 only takes 2 pointers size.
+
+                                                                              ORIGINAL    X86_64   ARM64
+            WatchpointSet:                                                    40          32       28
+            CodeBlockJettisoningWatchpoint:                                   32          19       15
+            StructureStubClearingWatchpoint:                                  56          48       40
+            AdaptiveInferredPropertyValueWatchpointBase::StructureWatchpoint: 24          13       11
+            AdaptiveInferredPropertyValueWatchpointBase::PropertyWatchpoint:  24          13       11
+            FunctionRareData::AllocationProfileClearingWatchpoint:            32          19       15
+            ObjectToStringAdaptiveStructureWatchpoint:                        56          48       40
+            LLIntPrototypeLoadAdaptiveStructureWatchpoint:                    64          48       48
+            DFG::AdaptiveStructureWatchpoint:                                 56          48       40
+
+        While we will re-architect the mechanism of Watchpoint, anyway Packed<> mechanism and enum types will be used too.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * Sources.txt:
+        * bytecode/AdaptiveInferredPropertyValueWatchpointBase.h:
+        * bytecode/CodeBlockJettisoningWatchpoint.h:
+        * bytecode/CodeOrigin.h:
+        * bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp:
+        (JSC::LLIntPrototypeLoadAdaptiveStructureWatchpoint::LLIntPrototypeLoadAdaptiveStructureWatchpoint):
+        (JSC::LLIntPrototypeLoadAdaptiveStructureWatchpoint::fireInternal):
+        * bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.h:
+        * bytecode/StructureStubClearingWatchpoint.cpp:
+        (JSC::StructureStubClearingWatchpoint::fireInternal):
+        * bytecode/StructureStubClearingWatchpoint.h:
+        * bytecode/Watchpoint.cpp:
+        (JSC::Watchpoint::fire):
+        * bytecode/Watchpoint.h:
+        (JSC::Watchpoint::Watchpoint):
+        * dfg/DFGAdaptiveStructureWatchpoint.cpp:
+        (JSC::DFG::AdaptiveStructureWatchpoint::AdaptiveStructureWatchpoint):
+        * dfg/DFGAdaptiveStructureWatchpoint.h:
+        * heap/PackedCellPtr.h: Added.
+        * runtime/FunctionRareData.h:
+        * runtime/ObjectToStringAdaptiveStructureWatchpoint.cpp: Added.
+        (JSC::ObjectToStringAdaptiveStructureWatchpoint::ObjectToStringAdaptiveStructureWatchpoint):
+        (JSC::ObjectToStringAdaptiveStructureWatchpoint::install):
+        (JSC::ObjectToStringAdaptiveStructureWatchpoint::fireInternal):
+        * runtime/ObjectToStringAdaptiveStructureWatchpoint.h: Added.
+        * runtime/StructureRareData.cpp:
+        (JSC::StructureRareData::clearObjectToStringValue):
+        (JSC::ObjectToStringAdaptiveStructureWatchpoint::ObjectToStringAdaptiveStructureWatchpoint): Deleted.
+        (JSC::ObjectToStringAdaptiveStructureWatchpoint::install): Deleted.
+        (JSC::ObjectToStringAdaptiveStructureWatchpoint::fireInternal): Deleted.
+        * runtime/StructureRareData.h:
+
+2019-05-12  Yusuke Suzuki  <ysuzuki@apple.com>
+
         [JSC] Compact generator code's bytecode size
         https://bugs.webkit.org/show_bug.cgi?id=197822
 
index fa0c844..7e21e0b 100644 (file)
                E354622B1B6065D100545386 /* ConstructAbility.h in Headers */ = {isa = PBXBuildFile; fileRef = E354622A1B6065D100545386 /* ConstructAbility.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E3555B8A1DAE03A500F36921 /* DOMJITCallDOMGetterSnippet.h in Headers */ = {isa = PBXBuildFile; fileRef = E3555B891DAE03A200F36921 /* DOMJITCallDOMGetterSnippet.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E355D38F22446877008F1AD6 /* GlobalExecutable.h in Headers */ = {isa = PBXBuildFile; fileRef = E355D38D2244686B008F1AD6 /* GlobalExecutable.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               E356987222841187008CDCCB /* PackedCellPtr.h in Headers */ = {isa = PBXBuildFile; fileRef = E356987122841183008CDCCB /* PackedCellPtr.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E35A0B9D220AD87A00AC4474 /* ExecutableBaseInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = E35A0B9C220AD87A00AC4474 /* ExecutableBaseInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E35CA1541DBC3A5C00F83516 /* DOMJITHeapRange.h in Headers */ = {isa = PBXBuildFile; fileRef = E35CA1521DBC3A5600F83516 /* DOMJITHeapRange.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E35CA1561DBC3A5F00F83516 /* DOMJITAbstractHeap.h in Headers */ = {isa = PBXBuildFile; fileRef = E35CA1501DBC3A5600F83516 /* DOMJITAbstractHeap.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E393ADD81FE702D00022D681 /* WeakMapImplInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = E393ADD71FE702CC0022D681 /* WeakMapImplInlines.h */; };
                E39D45F51D39005600B3B377 /* InterpreterInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = E39D9D841D39000600667282 /* InterpreterInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E39DA4A71B7E8B7C0084F33A /* JSModuleRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = E39DA4A51B7E8B7C0084F33A /* JSModuleRecord.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               E39EEAF322812450008474F4 /* ObjectToStringAdaptiveStructureWatchpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = E39EEAF22281244C008474F4 /* ObjectToStringAdaptiveStructureWatchpoint.h */; };
                E3A0531A21342B680022EC14 /* WasmStreamingParser.h in Headers */ = {isa = PBXBuildFile; fileRef = E3A0531621342B660022EC14 /* WasmStreamingParser.h */; };
                E3A0531C21342B680022EC14 /* WasmSectionParser.h in Headers */ = {isa = PBXBuildFile; fileRef = E3A0531821342B670022EC14 /* WasmSectionParser.h */; };
                E3A32BC71FC83147007D7E76 /* WeakMapImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = E3A32BC61FC8312E007D7E76 /* WeakMapImpl.h */; };
                E3555B891DAE03A200F36921 /* DOMJITCallDOMGetterSnippet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMJITCallDOMGetterSnippet.h; sourceTree = "<group>"; };
                E355D38D2244686B008F1AD6 /* GlobalExecutable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GlobalExecutable.h; sourceTree = "<group>"; };
                E355D38E2244686C008F1AD6 /* GlobalExecutable.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = GlobalExecutable.cpp; sourceTree = "<group>"; };
+               E356987122841183008CDCCB /* PackedCellPtr.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PackedCellPtr.h; sourceTree = "<group>"; };
                E35A0B9C220AD87A00AC4474 /* ExecutableBaseInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExecutableBaseInlines.h; sourceTree = "<group>"; };
                E35CA14F1DBC3A5600F83516 /* DOMJITAbstractHeap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DOMJITAbstractHeap.cpp; sourceTree = "<group>"; };
                E35CA1501DBC3A5600F83516 /* DOMJITAbstractHeap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMJITAbstractHeap.h; sourceTree = "<group>"; };
                E39D9D841D39000600667282 /* InterpreterInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InterpreterInlines.h; sourceTree = "<group>"; };
                E39DA4A41B7E8B7C0084F33A /* JSModuleRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSModuleRecord.cpp; sourceTree = "<group>"; };
                E39DA4A51B7E8B7C0084F33A /* JSModuleRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSModuleRecord.h; sourceTree = "<group>"; };
+               E39EEAF12281244C008474F4 /* ObjectToStringAdaptiveStructureWatchpoint.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ObjectToStringAdaptiveStructureWatchpoint.cpp; sourceTree = "<group>"; };
+               E39EEAF22281244C008474F4 /* ObjectToStringAdaptiveStructureWatchpoint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ObjectToStringAdaptiveStructureWatchpoint.h; sourceTree = "<group>"; };
                E3A0531621342B660022EC14 /* WasmStreamingParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmStreamingParser.h; sourceTree = "<group>"; };
                E3A0531721342B660022EC14 /* WasmSectionParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmSectionParser.cpp; sourceTree = "<group>"; };
                E3A0531821342B670022EC14 /* WasmSectionParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmSectionParser.h; sourceTree = "<group>"; };
                                0F1FB3981E1F65F900A9BE50 /* MutatorScheduler.h */,
                                0FA762021DB9242300B7A2FD /* MutatorState.cpp */,
                                0FA762031DB9242300B7A2FD /* MutatorState.h */,
+                               E356987122841183008CDCCB /* PackedCellPtr.h */,
                                0F9DAA081FD1C3C80079C5B2 /* ParallelSourceAdapter.h */,
                                0FBB73B61DEF3AAC002C009E /* PreventCollectionScope.h */,
                                0FD0E5EF1E46BF230006AB08 /* RegisterState.h */,
                                E3C295DC1ED2CBAA00D3016F /* ObjectPropertyChangeAdaptiveWatchpoint.h */,
                                BC2680C80E16D4E900A06E92 /* ObjectPrototype.cpp */,
                                BC2680C90E16D4E900A06E92 /* ObjectPrototype.h */,
+                               E39EEAF12281244C008474F4 /* ObjectToStringAdaptiveStructureWatchpoint.cpp */,
+                               E39EEAF22281244C008474F4 /* ObjectToStringAdaptiveStructureWatchpoint.h */,
                                F692A8770255597D01FF60F7 /* Operations.cpp */,
                                F692A8780255597D01FF60F7 /* Operations.h */,
                                0FE228EA1436AB2300196C48 /* Options.cpp */,
                                0FD3E40A1B618B6600C80E1E /* ObjectPropertyCondition.h in Headers */,
                                0FD3E40C1B618B6600C80E1E /* ObjectPropertyConditionSet.h in Headers */,
                                BC18C4460E16F5CD00B34460 /* ObjectPrototype.h in Headers */,
+                               E39EEAF322812450008474F4 /* ObjectToStringAdaptiveStructureWatchpoint.h in Headers */,
                                E124A8F70E555775003091F1 /* OpaqueJSString.h in Headers */,
                                14F79F70216EAFD200046D39 /* Opcode.h in Headers */,
                                FE64872E2141D04800AB0D3E /* OpcodeInlines.h in Headers */,
                                A70447EA17A0BD4600F5898E /* OperandsInlines.h in Headers */,
                                BC18C4480E16F5CD00B34460 /* Operations.h in Headers */,
                                0FE228ED1436AB2700196C48 /* Options.h in Headers */,
+                               E356987222841187008CDCCB /* PackedCellPtr.h in Headers */,
                                0F9DAA0A1FD1C3D30079C5B2 /* ParallelSourceAdapter.h in Headers */,
                                E34E657520668EAA00FB81AC /* ParseHash.h in Headers */,
                                37C738D21EDB56E4003F2B0B /* ParseInt.h in Headers */,
index f553444..2196c97 100644 (file)
@@ -899,6 +899,7 @@ runtime/NumberPrototype.cpp
 runtime/ObjectConstructor.cpp
 runtime/ObjectInitializationScope.cpp
 runtime/ObjectPrototype.cpp
+runtime/ObjectToStringAdaptiveStructureWatchpoint.cpp
 runtime/Operations.cpp
 runtime/Options.cpp
 runtime/ProgramExecutable.cpp
index 5d4e02a..ace8a09 100644 (file)
@@ -45,24 +45,33 @@ public:
 
     virtual ~AdaptiveInferredPropertyValueWatchpointBase() = default;
 
-protected:
-    virtual bool isValid() const;
-    virtual void handleFire(VM&, const FireDetail&) = 0;
-
-private:
     class StructureWatchpoint final : public Watchpoint {
     public:
-        StructureWatchpoint() { }
-    protected:
-        void fireInternal(VM&, const FireDetail&) override;
+        StructureWatchpoint()
+            : Watchpoint(Watchpoint::Type::AdaptiveInferredPropertyValueStructure)
+        { }
+
+        void fireInternal(VM&, const FireDetail&);
     };
+    // Own destructor may not be called. Keep members trivially destructible.
+    static_assert(sizeof(StructureWatchpoint) == sizeof(Watchpoint), "");
+
     class PropertyWatchpoint final : public Watchpoint {
     public:
-        PropertyWatchpoint() { }
-    protected:
-        void fireInternal(VM&, const FireDetail&) override;
+        PropertyWatchpoint()
+            : Watchpoint(Watchpoint::Type::AdaptiveInferredPropertyValueProperty)
+        { }
+
+        void fireInternal(VM&, const FireDetail&);
     };
+    // Own destructor may not be called. Keep members trivially destructible.
+    static_assert(sizeof(PropertyWatchpoint) == sizeof(Watchpoint), "");
+
+protected:
+    virtual bool isValid() const;
+    virtual void handleFire(VM&, const FireDetail&) = 0;
 
+private:
     void fire(VM&, const FireDetail&);
 
     ObjectPropertyCondition m_key;
index af24ea3..35a4164 100644 (file)
@@ -25,6 +25,7 @@
 
 #pragma once
 
+#include "PackedCellPtr.h"
 #include "Watchpoint.h"
 
 namespace JSC {
@@ -34,15 +35,15 @@ class CodeBlock;
 class CodeBlockJettisoningWatchpoint final : public Watchpoint {
 public:
     CodeBlockJettisoningWatchpoint(CodeBlock* codeBlock)
-        : m_codeBlock(codeBlock)
+        : Watchpoint(Watchpoint::Type::CodeBlockJettisoning)
+        , m_codeBlock(codeBlock)
     {
     }
     
-protected:
-    void fireInternal(VM&, const FireDetail&) override;
+    void fireInternal(VM&, const FireDetail&);
 
 private:
-    CodeBlock* m_codeBlock;
+    JSC_WATCHPOINT_FIELD(PackedCellPtr<CodeBlock>, m_codeBlock);
 };
 
 } // namespace JSC
index 551fbec..b6954e7 100644 (file)
@@ -232,14 +232,9 @@ private:
         return bitwise_cast<InlineCallFrame*>(value);
     }
 
-#if CPU(ARM64) && CPU(ADDRESS64)
-    static constexpr unsigned s_freeBitsAtTop = 28;
-    static constexpr uintptr_t s_maskCompositeValueForPointer = 0x0000000ffffffff8;
-#elif CPU(ADDRESS64)
-    static constexpr unsigned s_freeBitsAtTop = 16;
-    static constexpr uintptr_t s_maskCompositeValueForPointer = 0x0000fffffffffff8;
-#endif
 #if CPU(ADDRESS64)
+    static constexpr unsigned s_freeBitsAtTop = 64 - WTF_CPU_EFFECTIVE_ADDRESS_WIDTH;
+    static constexpr uintptr_t s_maskCompositeValueForPointer = ((1ULL << WTF_CPU_EFFECTIVE_ADDRESS_WIDTH) - 1) & ~(8ULL - 1);
     static uintptr_t buildCompositeValue(InlineCallFrame* inlineCallFrame, unsigned bytecodeIndex)
     {
         if (bytecodeIndex == s_invalidBytecodeIndex)
index 086b513..0ef6a55 100644 (file)
 namespace JSC {
 
 LLIntPrototypeLoadAdaptiveStructureWatchpoint::LLIntPrototypeLoadAdaptiveStructureWatchpoint(CodeBlock* owner, const ObjectPropertyCondition& key, unsigned bytecodeOffset)
-    : m_owner(owner)
-    , m_key(key)
+    : Watchpoint(Watchpoint::Type::LLIntPrototypeLoadAdaptiveStructure)
+    , m_owner(owner)
     , m_bytecodeOffset(bytecodeOffset)
+    , m_key(key)
 {
     RELEASE_ASSERT(key.watchingRequiresStructureTransitionWatchpoint());
     RELEASE_ASSERT(!key.watchingRequiresReplacementWatchpoint());
@@ -58,8 +59,8 @@ void LLIntPrototypeLoadAdaptiveStructureWatchpoint::fireInternal(VM& vm, const F
         return;
     }
 
-    auto& instruction = m_owner->instructions().at(m_bytecodeOffset);
-    clearLLIntGetByIdCache(instruction->as<OpGetById>().metadata(m_owner));
+    auto& instruction = m_owner->instructions().at(m_bytecodeOffset.get());
+    clearLLIntGetByIdCache(instruction->as<OpGetById>().metadata(m_owner.get()));
 }
 
 void LLIntPrototypeLoadAdaptiveStructureWatchpoint::clearLLIntGetByIdCache(OpGetById::Metadata& metadata)
index a9b1fcd..dd557cd 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "BytecodeStructs.h"
 #include "ObjectPropertyCondition.h"
+#include "PackedCellPtr.h"
 #include "Watchpoint.h"
 
 namespace JSC {
@@ -41,13 +42,13 @@ public:
 
     const ObjectPropertyCondition& key() const { return m_key; }
 
-protected:
-    void fireInternal(VM&, const FireDetail&) override;
+    void fireInternal(VM&, const FireDetail&);
 
 private:
-    CodeBlock* m_owner;
-    ObjectPropertyCondition m_key;
-    unsigned m_bytecodeOffset;
+    // Own destructor may not be called. Keep members trivially destructible.
+    JSC_WATCHPOINT_FIELD(PackedCellPtr<CodeBlock>, m_owner);
+    JSC_WATCHPOINT_FIELD(Packed<unsigned>, m_bytecodeOffset);
+    JSC_WATCHPOINT_FIELD(ObjectPropertyCondition, m_key);
 };
 
 } // namespace JSC
index 9416d30..30a1f05 100644 (file)
@@ -36,15 +36,15 @@ namespace JSC {
 
 void StructureStubClearingWatchpoint::fireInternal(VM& vm, const FireDetail&)
 {
-    if (!m_holder.isValid())
+    if (!m_holder->isValid())
         return;
 
     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.
-        ConcurrentJSLocker locker(m_holder.codeBlock()->m_lock);
-        m_holder.stubInfo()->reset(m_holder.codeBlock());
+        ConcurrentJSLocker locker(m_holder->codeBlock()->m_lock);
+        m_holder->stubInfo()->reset(m_holder->codeBlock());
         return;
     }
 
index 70b697f..31f6aa4 100644 (file)
@@ -44,20 +44,19 @@ class StructureStubClearingWatchpoint final : public Watchpoint {
     WTF_MAKE_NONCOPYABLE(StructureStubClearingWatchpoint);
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    StructureStubClearingWatchpoint(
-        const ObjectPropertyCondition& key,
-        WatchpointsOnStructureStubInfo& holder)
-        : m_key(key)
-        , m_holder(holder)
+    StructureStubClearingWatchpoint(const ObjectPropertyCondition& key, WatchpointsOnStructureStubInfo& holder)
+        : Watchpoint(Watchpoint::Type::StructureStubClearing)
+        , m_holder(&holder)
+        , m_key(key)
     {
     }
 
-protected:
-    void fireInternal(VM&, const FireDetail&) override;
+    void fireInternal(VM&, const FireDetail&);
 
 private:
-    ObjectPropertyCondition m_key;
-    WatchpointsOnStructureStubInfo& m_holder;
+    // Own destructor may not be called. Keep members trivially destructible.
+    JSC_WATCHPOINT_FIELD(PackedPtr<WatchpointsOnStructureStubInfo>, m_holder);
+    JSC_WATCHPOINT_FIELD(ObjectPropertyCondition, m_key);
 };
 
 class WatchpointsOnStructureStubInfo {
index 7fa5616..3d70df4 100644 (file)
 #include "config.h"
 #include "Watchpoint.h"
 
+#include "AdaptiveInferredPropertyValueWatchpointBase.h"
+#include "CodeBlockJettisoningWatchpoint.h"
+#include "DFGAdaptiveStructureWatchpoint.h"
+#include "FunctionRareData.h"
 #include "HeapInlines.h"
+#include "LLIntPrototypeLoadAdaptiveStructureWatchpoint.h"
+#include "ObjectToStringAdaptiveStructureWatchpoint.h"
+#include "StructureStubClearingWatchpoint.h"
 #include "VM.h"
 #include <wtf/CompilationThread.h>
 
@@ -52,7 +59,14 @@ Watchpoint::~Watchpoint()
 void Watchpoint::fire(VM& vm, const FireDetail& detail)
 {
     RELEASE_ASSERT(!isOnList());
-    fireInternal(vm, detail);
+    switch (m_type) {
+#define JSC_DEFINE_WATCHPOINT_DISPATCH(type, cast) \
+    case Type::type: \
+        static_cast<cast*>(this)->fireInternal(vm, detail); \
+        break;
+    JSC_WATCHPOINT_TYPES(JSC_DEFINE_WATCHPOINT_DISPATCH)
+#undef JSC_DEFINE_WATCHPOINT_DISPATCH
+    }
 }
 
 WatchpointSet::WatchpointSet(WatchpointState state)
index 77fba3e..ee8dd75 100644 (file)
@@ -90,21 +90,75 @@ LazyFireDetail<Types...> createLazyFireDetail(const Types&... types)
 
 class WatchpointSet;
 
-class Watchpoint : public BasicRawSentinelNode<Watchpoint> {
+// Really unfortunately, we do not have the way to dispatch appropriate destructor in base class' destructor
+// based on enum type. If we call destructor explicitly in the base class, it ends up calling the base destructor
+// twice. C++20 allows this by using std::std::destroying_delete_t. But we are not using C++20 right now.
+//
+// Because we cannot dispatch destructors of derived classes in the destructor of the base class, what it means is,
+// 1. Calling Watchpoint::~Watchpoint directly is illegal.
+// 2. `delete watchpoint` where watchpoint is non-final derived class is illegal. If watchpoint is final derived class, it works.
+// 3. If we really want to do (2), we need to call `watchpoint->destroy()` instead, and dispatch an appropriate destructor in Watchpoint::destroy.
+//
+// Luckily, none of our derived watchpoint classes have members which require destructors. So we do not dispatch
+// the destructor call to the drived class in the base class. If it becomes really required, we can introduce
+// a custom deleter for some classes which directly call "delete" to the allocated non-final Watchpoint class
+// (e.g. std::unique_ptr<Watchpoint>, RefPtr<Watchpoint>), and call Watchpoint::destroy instead of "delete"
+// operator. But since we do not require it for now, we are doing the simplest thing.
+#define JSC_WATCHPOINT_TYPES_WITHOUT_JIT(macro) \
+    macro(AdaptiveInferredPropertyValueStructure, AdaptiveInferredPropertyValueWatchpointBase::StructureWatchpoint) \
+    macro(AdaptiveInferredPropertyValueProperty, AdaptiveInferredPropertyValueWatchpointBase::PropertyWatchpoint) \
+    macro(CodeBlockJettisoning, CodeBlockJettisoningWatchpoint) \
+    macro(LLIntPrototypeLoadAdaptiveStructure, LLIntPrototypeLoadAdaptiveStructureWatchpoint) \
+    macro(FunctionRareDataAllocationProfileClearing, FunctionRareData::AllocationProfileClearingWatchpoint) \
+    macro(ObjectToStringAdaptiveStructure, ObjectToStringAdaptiveStructureWatchpoint)
+
+#if ENABLE(JIT)
+#define JSC_WATCHPOINT_TYPES_WITHOUT_DFG(macro) \
+    JSC_WATCHPOINT_TYPES_WITHOUT_JIT(macro) \
+    macro(StructureStubClearing, StructureStubClearingWatchpoint)
+
+#if ENABLE(DFG_JIT)
+#define JSC_WATCHPOINT_TYPES(macro) \
+    JSC_WATCHPOINT_TYPES_WITHOUT_DFG(macro) \
+    macro(AdaptiveStructure, DFG::AdaptiveStructureWatchpoint)
+#else
+#define JSC_WATCHPOINT_TYPES(macro) \
+    JSC_WATCHPOINT_TYPES_WITHOUT_DFG(macro)
+#endif
+
+#else
+#define JSC_WATCHPOINT_TYPES(macro) \
+    JSC_WATCHPOINT_TYPES_WITHOUT_JIT(macro)
+#endif
+
+#define JSC_WATCHPOINT_FIELD(type, member) \
+    type member; \
+    static_assert(std::is_trivially_destructible<type>::value, ""); \
+
+
+class Watchpoint : public PackedRawSentinelNode<Watchpoint> {
     WTF_MAKE_NONCOPYABLE(Watchpoint);
     WTF_MAKE_NONMOVABLE(Watchpoint);
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    Watchpoint() = default;
-    
-    virtual ~Watchpoint();
+#define JSC_DEFINE_WATCHPOINT_TYPES(type, _) type,
+    enum class Type : uint8_t {
+        JSC_WATCHPOINT_TYPES(JSC_DEFINE_WATCHPOINT_TYPES)
+    };
+#undef JSC_DEFINE_WATCHPOINT_TYPES
+
+    Watchpoint(Type type)
+        : m_type(type)
+    { }
 
 protected:
-    virtual void fireInternal(VM&, const FireDetail&) = 0;
+    ~Watchpoint();
 
 private:
     friend class WatchpointSet;
     void fire(VM&, const FireDetail&);
+
+    Type m_type;
 };
 
 enum WatchpointState {
@@ -239,7 +293,7 @@ private:
     int8_t m_state;
     int8_t m_setIsNotEmpty;
 
-    SentinelLinkedList<Watchpoint, BasicRawSentinelNode<Watchpoint>> m_set;
+    SentinelLinkedList<Watchpoint, PackedRawSentinelNode<Watchpoint>> m_set;
 };
 
 // InlineWatchpointSet is a low-overhead, non-copyable watchpoint set in which
index 5b9dda6..00dc531 100644 (file)
 
 namespace JSC { namespace DFG {
 
-AdaptiveStructureWatchpoint::AdaptiveStructureWatchpoint(
-    const ObjectPropertyCondition& key,
-    CodeBlock* codeBlock)
-    : m_key(key)
+AdaptiveStructureWatchpoint::AdaptiveStructureWatchpoint(const ObjectPropertyCondition& key, CodeBlock* codeBlock)
+    : Watchpoint(Watchpoint::Type::AdaptiveStructure)
     , m_codeBlock(codeBlock)
+    , m_key(key)
 {
     RELEASE_ASSERT(key.watchingRequiresStructureTransitionWatchpoint());
     RELEASE_ASSERT(!key.watchingRequiresReplacementWatchpoint());
index 94013dc..6ab07b4 100644 (file)
@@ -28,6 +28,7 @@
 #if ENABLE(DFG_JIT)
 
 #include "ObjectPropertyCondition.h"
+#include "PackedCellPtr.h"
 #include "Watchpoint.h"
 
 namespace JSC { namespace DFG {
@@ -40,12 +41,12 @@ public:
     
     void install(VM&);
 
-protected:
-    void fireInternal(VM&, const FireDetail&) override;
+    void fireInternal(VM&, const FireDetail&);
 
 private:
-    ObjectPropertyCondition m_key;
-    CodeBlock* m_codeBlock;
+    // Own destructor may not be called. Keep members trivially destructible.
+    JSC_WATCHPOINT_FIELD(PackedCellPtr<CodeBlock>, m_codeBlock);
+    JSC_WATCHPOINT_FIELD(ObjectPropertyCondition, m_key);
 };
 
 } } // namespace JSC::DFG
diff --git a/Source/JavaScriptCore/heap/PackedCellPtr.h b/Source/JavaScriptCore/heap/PackedCellPtr.h
new file mode 100644 (file)
index 0000000..fe04c0d
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include "MarkedBlock.h"
+#include <wtf/Packed.h>
+
+namespace JSC {
+
+template<typename T>
+using PackedCellPtr = PackedAlignedPtr<T, MarkedBlock::atomSize>;
+
+} // namespace JSC
index 132c279..a6a6df0 100644 (file)
@@ -28,6 +28,7 @@
 #include "InternalFunctionAllocationProfile.h"
 #include "JSCast.h"
 #include "ObjectAllocationProfile.h"
+#include "PackedCellPtr.h"
 #include "Watchpoint.h"
 
 namespace JSC {
@@ -110,23 +111,25 @@ public:
         return m_allocationProfileClearingWatchpoint.get();
     }
 
-protected:
-    FunctionRareData(VM&);
-    ~FunctionRareData();
-
-private:
-
     class AllocationProfileClearingWatchpoint final : public Watchpoint {
     public:
         AllocationProfileClearingWatchpoint(FunctionRareData* rareData)
-            : m_rareData(rareData)
+            : Watchpoint(Watchpoint::Type::FunctionRareDataAllocationProfileClearing)
+            , m_rareData(rareData)
         { }
-    protected:
-        void fireInternal(VM&, const FireDetail&) override;
+
+        void fireInternal(VM&, const FireDetail&);
+
     private:
-        FunctionRareData* m_rareData;
+        // Own destructor may not be called. Keep members trivially destructible.
+        JSC_WATCHPOINT_FIELD(PackedCellPtr<FunctionRareData>, m_rareData);
     };
 
+protected:
+    FunctionRareData(VM&);
+    ~FunctionRareData();
+
+private:
     friend class LLIntOffsetsExtractor;
 
     // Ideally, there would only be one allocation profile for subclassing but due to Reflect.construct we
diff --git a/Source/JavaScriptCore/runtime/ObjectToStringAdaptiveStructureWatchpoint.cpp b/Source/JavaScriptCore/runtime/ObjectToStringAdaptiveStructureWatchpoint.cpp
new file mode 100644 (file)
index 0000000..7d0e842
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "config.h"
+#include "ObjectToStringAdaptiveStructureWatchpoint.h"
+
+#include "ObjectPropertyConditionSet.h"
+#include "StructureRareData.h"
+
+namespace JSC {
+
+ObjectToStringAdaptiveStructureWatchpoint::ObjectToStringAdaptiveStructureWatchpoint(const ObjectPropertyCondition& key, StructureRareData* structureRareData)
+    : Watchpoint(Watchpoint::Type::ObjectToStringAdaptiveStructure)
+    , m_structureRareData(structureRareData)
+    , m_key(key)
+{
+    RELEASE_ASSERT(key.watchingRequiresStructureTransitionWatchpoint());
+    RELEASE_ASSERT(!key.watchingRequiresReplacementWatchpoint());
+}
+
+void ObjectToStringAdaptiveStructureWatchpoint::install(VM& vm)
+{
+    RELEASE_ASSERT(m_key.isWatchable());
+
+    m_key.object()->structure(vm)->addTransitionWatchpoint(this);
+}
+
+void ObjectToStringAdaptiveStructureWatchpoint::fireInternal(VM& vm, const FireDetail&)
+{
+    if (!m_structureRareData->isLive())
+        return;
+
+    if (m_key.isWatchable(PropertyCondition::EnsureWatchability)) {
+        install(vm);
+        return;
+    }
+
+    m_structureRareData->clearObjectToStringValue();
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/ObjectToStringAdaptiveStructureWatchpoint.h b/Source/JavaScriptCore/runtime/ObjectToStringAdaptiveStructureWatchpoint.h
new file mode 100644 (file)
index 0000000..60ee802
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include "ObjectPropertyCondition.h"
+#include "PackedCellPtr.h"
+#include "Watchpoint.h"
+
+namespace JSC {
+
+class StructureRareData;
+
+class ObjectToStringAdaptiveStructureWatchpoint final : public Watchpoint {
+public:
+    ObjectToStringAdaptiveStructureWatchpoint(const ObjectPropertyCondition&, StructureRareData*);
+
+    void install(VM&);
+
+    const ObjectPropertyCondition& key() const { return m_key; }
+
+    void fireInternal(VM&, const FireDetail&);
+    
+private:
+    // Own destructor may not be called. Keep members trivially destructible.
+    JSC_WATCHPOINT_FIELD(PackedCellPtr<StructureRareData>, m_structureRareData);
+    JSC_WATCHPOINT_FIELD(ObjectPropertyCondition, m_key);
+};
+
+}
index 5c1c5f7..99a8d9f 100644 (file)
@@ -32,6 +32,7 @@
 #include "JSString.h"
 #include "JSCInlines.h"
 #include "ObjectPropertyConditionSet.h"
+#include "ObjectToStringAdaptiveStructureWatchpoint.h"
 
 namespace JSC {
 
@@ -90,22 +91,6 @@ private:
     StructureRareData* m_structureRareData;
 };
 
-class ObjectToStringAdaptiveStructureWatchpoint final : public Watchpoint {
-public:
-    ObjectToStringAdaptiveStructureWatchpoint(const ObjectPropertyCondition&, StructureRareData*);
-
-    void install(VM&);
-
-    const ObjectPropertyCondition& key() const { return m_key; }
-
-protected:
-    void fireInternal(VM&, const FireDetail&) override;
-    
-private:
-    ObjectPropertyCondition m_key;
-    StructureRareData* m_structureRareData;
-};
-
 void StructureRareData::setObjectToStringValue(ExecState* exec, VM& vm, Structure* ownStructure, JSString* value, PropertySlot toStringTagSymbolSlot)
 {
     if (m_giveUpOnObjectToStringValueCache)
@@ -164,7 +149,7 @@ void StructureRareData::setObjectToStringValue(ExecState* exec, VM& vm, Structur
     m_objectToStringValue.set(vm, this, value);
 }
 
-inline void StructureRareData::clearObjectToStringValue()
+void StructureRareData::clearObjectToStringValue()
 {
     m_objectToStringAdaptiveWatchpointSet.clear();
     m_objectToStringAdaptiveInferredValueWatchpoint.reset();
@@ -189,34 +174,6 @@ void StructureRareData::finalizeUnconditionally(VM& vm)
 
 // ------------- Methods for Object.prototype.toString() helper watchpoint classes --------------
 
-ObjectToStringAdaptiveStructureWatchpoint::ObjectToStringAdaptiveStructureWatchpoint(const ObjectPropertyCondition& key, StructureRareData* structureRareData)
-    : m_key(key)
-    , m_structureRareData(structureRareData)
-{
-    RELEASE_ASSERT(key.watchingRequiresStructureTransitionWatchpoint());
-    RELEASE_ASSERT(!key.watchingRequiresReplacementWatchpoint());
-}
-
-void ObjectToStringAdaptiveStructureWatchpoint::install(VM& vm)
-{
-    RELEASE_ASSERT(m_key.isWatchable());
-
-    m_key.object()->structure(vm)->addTransitionWatchpoint(this);
-}
-
-void ObjectToStringAdaptiveStructureWatchpoint::fireInternal(VM& vm, const FireDetail&)
-{
-    if (!m_structureRareData->isLive())
-        return;
-
-    if (m_key.isWatchable(PropertyCondition::EnsureWatchability)) {
-        install(vm);
-        return;
-    }
-
-    m_structureRareData->clearObjectToStringValue();
-}
-
 ObjectToStringAdaptiveInferredPropertyValueWatchpoint::ObjectToStringAdaptiveInferredPropertyValueWatchpoint(const ObjectPropertyCondition& key, StructureRareData* structureRareData)
     : Base(key)
     , m_structureRareData(structureRareData)
index e03b23f..9ca7fc4 100644 (file)
@@ -35,8 +35,8 @@ namespace JSC {
 
 class JSPropertyNameEnumerator;
 class Structure;
-class ObjectToStringAdaptiveStructureWatchpoint;
 class ObjectToStringAdaptiveInferredPropertyValueWatchpoint;
+class ObjectToStringAdaptiveStructureWatchpoint;
 
 class StructureRareData final : public JSCell {
 public:
index a5f9d96..3742b18 100644 (file)
@@ -1,3 +1,89 @@
+2019-05-12  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] Compress Watchpoint size by using enum type and Packed<> data structure
+        https://bugs.webkit.org/show_bug.cgi?id=197730
+
+        Reviewed by Filip Pizlo.
+
+        This patch introduces a new data structures, WTF::Packed, WTF::PackedPtr, and WTF::PackedAlignedPtr.
+
+        - WTF::Packed
+
+            WTF::Packed is data storage. We can read and write trivial (in C++ term [1]) data to this storage. The difference to
+            the usual storage is that the alignment of this storage is always 1. We access the underlying data by using unalignedLoad/unalignedStore.
+            This class offers alignment = 1 data structure instead of missing the following characteristics.
+
+                1. Load / Store are non atomic even if the data size is within a pointer width. We should not use this for a member which can be accessed
+                   in a racy way. (e.g. fields accessed optimistically from the concurrent compilers).
+
+                2. We cannot take reference / pointer to the underlying storage since they are unaligned.
+
+                3. Access to this storage is unaligned access. The code is using memcpy, and the compiler will convert to an appropriate unaligned access
+                   in certain architectures (x86_64 / ARM64). It could be slow. So use it for non performance sensitive & memory sensitive places.
+
+        - WTF::PackedPtr
+
+            WTF::PackedPtr is a specialization of WTF::Packed<T*>. And it is basically WTF::PackedAlignedPtr with alignment = 1. We further compact
+            the pointer by leveraging the platform specific knowledge. In 64bit architectures, the effective width of pointers are less than 64 bit.
+            In x86_64, it is 48 bits. And Darwin ARM64 is further smaller, 36 bits. This information allows us to compact the pointer to 6 bytes in
+            x86_64 and 5 bytes in Darwin ARM64.
+
+        - WTF::PackedAlignedPtr
+
+            WTF::PackedAlignedPtr is the WTF::PackedPtr with alignment information of the T. If we use this alignment information, we could reduce the
+            size of packed pointer further in some cases. For example, since we guarantee that JSCells are 16 byte aligned, low 4 bits are empty. Leveraging
+            this information in Darwin ARM64 platform allows us to make packed JSCell pointer 4 bytes (36 - 4 bits). We do not use passed alignment
+            information if it is not profitable.
+
+        We also have PackedPtrTraits. This is new PtrTraits and use it for various data structures such as Bag<>.
+
+        [1]: https://en.cppreference.com/w/cpp/types/is_trivial
+
+        * WTF.xcodeproj/project.pbxproj:
+        * wtf/Bag.h:
+        (WTF::Bag::clear):
+        (WTF::Bag::iterator::operator++):
+        * wtf/CMakeLists.txt:
+        * wtf/DumbPtrTraits.h:
+        * wtf/DumbValueTraits.h:
+        * wtf/MathExtras.h:
+        (WTF::clzConstexpr):
+        (WTF::clz):
+        (WTF::ctzConstexpr):
+        (WTF::ctz):
+        (WTF::getLSBSetConstexpr):
+        (WTF::getMSBSetConstexpr):
+        * wtf/Packed.h: Added.
+        (WTF::Packed::Packed):
+        (WTF::Packed::get const):
+        (WTF::Packed::set):
+        (WTF::Packed::operator=):
+        (WTF::Packed::exchange):
+        (WTF::Packed::swap):
+        (WTF::alignof):
+        (WTF::PackedPtrTraits::exchange):
+        (WTF::PackedPtrTraits::swap):
+        (WTF::PackedPtrTraits::unwrap):
+        * wtf/Platform.h:
+        * wtf/SentinelLinkedList.h:
+        (WTF::BasicRawSentinelNode::BasicRawSentinelNode):
+        (WTF::BasicRawSentinelNode::prev):
+        (WTF::BasicRawSentinelNode::next):
+        (WTF::PtrTraits>::remove):
+        (WTF::PtrTraits>::prepend):
+        (WTF::PtrTraits>::append):
+        (WTF::RawNode>::SentinelLinkedList):
+        (WTF::RawNode>::remove):
+        (WTF::BasicRawSentinelNode<T>::remove): Deleted.
+        (WTF::BasicRawSentinelNode<T>::prepend): Deleted.
+        (WTF::BasicRawSentinelNode<T>::append): Deleted.
+        * wtf/StdLibExtras.h:
+        (WTF::roundUpToMultipleOfImpl):
+        (WTF::roundUpToMultipleOfImpl0): Deleted.
+        * wtf/UnalignedAccess.h:
+        (WTF::unalignedLoad):
+        (WTF::unalignedStore):
+
 2019-05-10  Saam barati  <sbarati@apple.com>
 
         Bag's move operator= leaks memory
index 8d5efaf..30204fc 100644 (file)
                E311FB161F0A568B003C08DE /* ThreadGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadGroup.h; sourceTree = "<group>"; };
                E3200AB41E9A536D003B59D2 /* PlatformRegisters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformRegisters.h; sourceTree = "<group>"; };
                E33D5F871FBED66700BF625E /* RecursableLambda.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RecursableLambda.h; sourceTree = "<group>"; };
+               E34CD0D022810A020020D299 /* Packed.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Packed.h; sourceTree = "<group>"; };
                E360C7642127B85B00C90F0E /* UnalignedAccess.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UnalignedAccess.h; sourceTree = "<group>"; };
                E360C7652127B85C00C90F0E /* Unexpected.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Unexpected.h; sourceTree = "<group>"; };
                E388886D20C9095100E632BC /* WorkerPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WorkerPool.cpp; sourceTree = "<group>"; };
                                7CBBA07319BB7FDC00BBF025 /* OSObjectPtr.h */,
                                A8A472DA151A825B004123FF /* OSRandomSource.cpp */,
                                A8A472DB151A825B004123FF /* OSRandomSource.h */,
+                               E34CD0D022810A020020D299 /* Packed.h */,
                                A8A472DF151A825B004123FF /* PackedIntVector.h */,
                                A8A472E0151A825B004123FF /* PageAllocation.h */,
                                A8A472E3151A825B004123FF /* PageBlock.cpp */,
index 1e585b0..7490ddd 100644 (file)
 #include <wtf/DumbPtrTraits.h>
 #include <wtf/FastMalloc.h>
 #include <wtf/Noncopyable.h>
+#include <wtf/Packed.h>
 
 namespace WTF {
 
 namespace Private {
 
-template<typename T>
+template<typename T, typename PassedPtrTraits = DumbPtrTraits<T>>
 class BagNode {
     WTF_MAKE_FAST_ALLOCATED;
 public:
+    using PtrTraits = typename PassedPtrTraits::template RebindTraits<BagNode>;
+
     template<typename... Args>
     BagNode(Args&&... args)
         : m_item(std::forward<Args>(args)...)
     { }
     
     T m_item;
-    BagNode* m_next { nullptr };
+    typename PtrTraits::StorageType m_next { nullptr };
 };
 
 } // namespace Private
 
-template<typename T, typename PtrTraits = DumbPtrTraits<Private::BagNode<T>>>
+template<typename T, typename PassedPtrTraits = DumbPtrTraits<T>>
 class Bag {
     WTF_MAKE_NONCOPYABLE(Bag);
     WTF_MAKE_FAST_ALLOCATED;
-    using Node = Private::BagNode<T>;
+    using Node = Private::BagNode<T, PassedPtrTraits>;
+    using PtrTraits = typename PassedPtrTraits::template RebindTraits<Node>;
 
 public:
     Bag() = default;
@@ -75,7 +79,7 @@ public:
         Node* head = this->unwrappedHead();
         while (head) {
             Node* current = head;
-            head = current->m_next;
+            head = Node::PtrTraits::unwrap(current->m_next);
             delete current;
         }
         m_head = nullptr;
@@ -104,7 +108,7 @@ public:
         
         iterator& operator++()
         {
-            m_node = m_node->m_next;
+            m_node = Node::PtrTraits::unwrap(m_node->m_next);
             return *this;
         }
         
@@ -148,6 +152,10 @@ private:
     typename PtrTraits::StorageType m_head { nullptr };
 };
 
+template<typename T>
+using PackedBag = Bag<T, PackedPtrTraits<T>>;
+
 } // namespace WTF
 
 using WTF::Bag;
+using WTF::PackedBag;
index e60ec0b..6d64080 100644 (file)
@@ -150,6 +150,7 @@ set(WTF_PUBLIC_HEADERS
     OptionSet.h
     Optional.h
     OrderMaker.h
+    Packed.h
     PackedIntVector.h
     PageAllocation.h
     PageBlock.h
index 982a368..51deba7 100644 (file)
@@ -32,6 +32,8 @@ namespace WTF {
     
 template<typename T>
 struct DumbPtrTraits {
+    template<typename U> using RebindTraits = DumbPtrTraits<U>;
+
     using StorageType = T*;
 
     template<typename U>
index fbc6322..77c53e9 100644 (file)
@@ -32,6 +32,8 @@ namespace WTF {
 
 template<typename T>
 struct DumbValueTraits {
+    template<typename U> using RebindTraits = DumbValueTraits<U>;
+
     using StorageType = T;
 
     template<typename U>
index 8080533..fddcbb6 100644 (file)
@@ -615,6 +615,23 @@ void shuffleVector(VectorType& vector, const RandomFunc& randomFunc)
     shuffleVector(vector, vector.size(), randomFunc);
 }
 
+template <typename T>
+constexpr unsigned clzConstexpr(T value)
+{
+    constexpr unsigned bitSize = sizeof(T) * CHAR_BIT;
+
+    using UT = typename std::make_unsigned<T>::type;
+    UT uValue = value;
+
+    unsigned zeroCount = 0;
+    for (int i = bitSize - 1; i >= 0; i--) {
+        if (uValue >> i)
+            break;
+        zeroCount++;
+    }
+    return zeroCount;
+}
+
 template<typename T>
 inline unsigned clz(T value)
 {
@@ -637,14 +654,29 @@ inline unsigned clz(T value)
         return bitSize - 1 - ret;
     return bitSize;
 #else
+    UNUSED_PARAM(bitSize);
+    UNUSED_PARAM(uValue);
+    return clzConstexpr(value);
+#endif
+}
+
+template <typename T>
+constexpr unsigned ctzConstexpr(T value)
+{
+    constexpr unsigned bitSize = sizeof(T) * CHAR_BIT;
+
+    using UT = typename std::make_unsigned<T>::type;
+    UT uValue = value;
+
     unsigned zeroCount = 0;
-    for (int i = bitSize - 1; i >= 0; i--) {
-        if (uValue >> i)
+    for (unsigned i = 0; i < bitSize; i++) {
+        if (uValue & 1)
             break;
+
         zeroCount++;
+        uValue >>= 1;
     }
     return zeroCount;
-#endif
 }
 
 template<typename T>
@@ -665,15 +697,9 @@ inline unsigned ctz(T value)
         return ret;
     return bitSize;
 #else
-    unsigned zeroCount = 0;
-    for (unsigned i = 0; i < bitSize; i++) {
-        if (uValue & 1)
-            break;
-
-        zeroCount++;
-        uValue >>= 1;
-    }
-    return zeroCount;
+    UNUSED_PARAM(bitSize);
+    UNUSED_PARAM(uValue);
+    return ctzConstexpr(value);
 #endif
 }
 
@@ -685,6 +711,13 @@ inline unsigned getLSBSet(T t)
 }
 
 template<typename T>
+constexpr unsigned getLSBSetConstexpr(T t)
+{
+    ASSERT_UNDER_CONSTEXPR_CONTEXT(t);
+    return ctzConstexpr(t);
+}
+
+template<typename T>
 inline unsigned getMSBSet(T t)
 {
     constexpr unsigned bitSize = sizeof(T) * CHAR_BIT;
@@ -692,6 +725,14 @@ inline unsigned getMSBSet(T t)
     return bitSize - 1 - clz(t);
 }
 
+template<typename T>
+constexpr unsigned getMSBSetConstexpr(T t)
+{
+    constexpr unsigned bitSize = sizeof(T) * CHAR_BIT;
+    ASSERT_UNDER_CONSTEXPR_CONTEXT(t);
+    return bitSize - 1 - clzConstexpr(t);
+}
+
 } // namespace WTF
 
 using WTF::opaque;
diff --git a/Source/WTF/wtf/Packed.h b/Source/WTF/wtf/Packed.h
new file mode 100644 (file)
index 0000000..d67b4ac
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <array>
+#include <wtf/MathExtras.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/UnalignedAccess.h>
+
+namespace WTF {
+
+template<typename T>
+class Packed {
+public:
+    static constexpr bool isPackedType = true;
+
+    Packed()
+        : Packed(T { })
+    {
+    }
+
+    Packed(const T& value)
+    {
+        unalignedStore<T>(m_storage.data(), value);
+    }
+
+    T get() const
+    {
+        return unalignedLoad<T>(m_storage.data());
+    }
+
+    void set(const T& value)
+    {
+        unalignedStore<T>(m_storage.data(), value);
+    }
+
+    Packed<T>& operator=(const T& value)
+    {
+        set(value);
+        return *this;
+    }
+
+    template<class U>
+    T exchange(U&& newValue)
+    {
+        T oldValue = get();
+        set(std::forward<U>(newValue));
+        return oldValue;
+    }
+
+    void swap(Packed& other)
+    {
+        m_storage.swap(other.m_storage);
+    }
+
+    template<typename Other, typename = std::enable_if_t<Other::isPackedType>>
+    void swap(Other& other)
+    {
+        T t1 = get();
+        T t2 = other.get();
+        set(t2);
+        other.set(t1);
+    }
+
+    void swap(T& t2)
+    {
+        T t1 = get();
+        std::swap(t1, t2);
+        set(t1);
+    }
+
+private:
+    std::array<uint8_t, sizeof(T)> m_storage;
+};
+
+// PackedAlignedPtr can take alignment parameter too. PackedAlignedPtr only uses this alignment information if it is profitable: we use
+// alignment information only when we can reduce the size of the storage. Since the pointer width is 36 bits and JSCells are aligned to 16 bytes,
+// we can use 4 bits in Darwin ARM64, we can compact cell pointer into 4 bytes (32 bits).
+template<typename T, size_t alignment = alignof(T)>
+class PackedAlignedPtr {
+public:
+    static_assert(hasOneBitSet(alignment), "Alignment needs to be power-of-two");
+    static constexpr bool isPackedType = true;
+    static constexpr unsigned alignmentShiftSizeIfProfitable = getLSBSetConstexpr(alignment);
+    static constexpr unsigned storageSizeWithoutAlignmentShift = roundUpToMultipleOf<8>(WTF_CPU_EFFECTIVE_ADDRESS_WIDTH) / 8;
+    static constexpr unsigned storageSizeWithAlignmentShift = roundUpToMultipleOf<8>(WTF_CPU_EFFECTIVE_ADDRESS_WIDTH - alignmentShiftSizeIfProfitable) / 8;
+    static constexpr bool isAlignmentShiftProfitable = storageSizeWithoutAlignmentShift > storageSizeWithAlignmentShift;
+    static constexpr unsigned alignmentShiftSize = isAlignmentShiftProfitable ? alignmentShiftSizeIfProfitable : 0;
+    static constexpr unsigned storageSize = storageSizeWithAlignmentShift;
+
+    constexpr PackedAlignedPtr()
+        : m_storage()
+    {
+    }
+
+    constexpr PackedAlignedPtr(std::nullptr_t)
+        : m_storage()
+    {
+    }
+
+    PackedAlignedPtr(T* value)
+    {
+        set(value);
+    }
+
+    T* get() const
+    {
+        // FIXME: PackedPtr<> can load memory with one mov by checking page boundary.
+        // https://bugs.webkit.org/show_bug.cgi?id=197754
+        uintptr_t value = 0;
+#if CPU(LITTLE_ENDIAN)
+        memcpy(&value, m_storage.data(), storageSize);
+#else
+        memcpy(bitwise_cast<uint8_t*>(&value) + (sizeof(void*) - storageSize), m_storage.data(), storageSize);
+#endif
+        if (isAlignmentShiftProfitable)
+            value <<= alignmentShiftSize;
+        return bitwise_cast<T*>(value);
+    }
+
+    void set(T* passedValue)
+    {
+        uintptr_t value = bitwise_cast<uintptr_t>(passedValue);
+        if (isAlignmentShiftProfitable)
+            value >>= alignmentShiftSize;
+#if CPU(LITTLE_ENDIAN)
+        memcpy(m_storage.data(), &value, storageSize);
+#else
+        memcpy(m_storage.data(), bitwise_cast<uint8_t*>(&value) + (sizeof(void*) - storageSize));
+#endif
+    }
+
+    void clear()
+    {
+        set(nullptr);
+    }
+
+    T* operator->() const { return get(); }
+    T& operator*() const { return *get(); }
+    bool operator!() const { return !get(); }
+
+    PackedAlignedPtr& operator=(T* value)
+    {
+        set(value);
+        return *this;
+    }
+
+    template<class U>
+    T exchange(U&& newValue)
+    {
+        T oldValue = get();
+        set(std::forward<U>(newValue));
+        return oldValue;
+    }
+
+    void swap(std::nullptr_t) { clear(); }
+
+    void swap(PackedAlignedPtr& other)
+    {
+        m_storage.swap(other.m_storage);
+    }
+
+    template<typename Other, typename = std::enable_if_t<Other::isPackedType>>
+    void swap(Other& other)
+    {
+        T t1 = get();
+        T t2 = other.get();
+        set(t2);
+        other.set(t1);
+    }
+
+    void swap(T& t2)
+    {
+        T t1 = get();
+        std::swap(t1, t2);
+        set(t1);
+    }
+
+private:
+    std::array<uint8_t, storageSize> m_storage;
+};
+
+template<typename T>
+class Packed<T*> : public PackedAlignedPtr<T, 1> {
+public:
+    using Base = PackedAlignedPtr<T, 1>;
+    using Base::Base;
+};
+
+template<typename T>
+using PackedPtr = Packed<T*>;
+
+template<typename T>
+struct PackedPtrTraits {
+    template<typename U> using RebindTraits = PackedPtrTraits<U>;
+
+    using StorageType = PackedPtr<T>;
+
+    template<class U> static ALWAYS_INLINE T* exchange(StorageType& ptr, U&& newValue) { return ptr.exchange(newValue); }
+
+    template<typename Other> static ALWAYS_INLINE void swap(PackedPtr<T>& a, Other& b) { a.swap(b); }
+
+    static ALWAYS_INLINE T* unwrap(const StorageType& ptr) { return ptr.get(); }
+};
+
+} // namespace WTF
+
+using WTF::Packed;
+using WTF::PackedAlignedPtr;
+using WTF::PackedPtr;
index 6e5e02f..90d9891 100644 (file)
 #define USE_SYSTEM_MALLOC 1
 #endif
 
+#if CPU(ADDRESS64)
+#if OS(DARWIN) && CPU(ARM64)
+#define WTF_CPU_EFFECTIVE_ADDRESS_WIDTH 36
+#else
+/* We strongly assume that effective address width is <= 48 in 64bit architectures (e.g. NaN boxing). */
+#define WTF_CPU_EFFECTIVE_ADDRESS_WIDTH 48
+#endif
+#else
+#define WTF_CPU_EFFECTIVE_ADDRESS_WIDTH 32
+#endif
+
 #if !defined(USE_JSVALUE64) && !defined(USE_JSVALUE32_64)
 #if CPU(ADDRESS64) || CPU(ARM64)
 #define USE_JSVALUE64 1
index ce79890..75b467a 100644 (file)
 
 #pragma once
 
+#include <wtf/Packed.h>
+
 namespace WTF {
 
 enum SentinelTag { Sentinel };
 
-template<typename T>
+template<typename T, typename PassedPtrTraits = DumbPtrTraits<T>>
 class BasicRawSentinelNode {
     WTF_MAKE_FAST_ALLOCATED;
 public:
+    using PtrTraits = typename PassedPtrTraits::template RebindTraits<BasicRawSentinelNode>;
+
     BasicRawSentinelNode(SentinelTag)
-        : m_next(0)
-        , m_prev(0)
     {
     }
     
-    BasicRawSentinelNode()
-        : m_next(0)
-        , m_prev(0)
-    {
-    }
+    BasicRawSentinelNode() = default;
     
     void setPrev(BasicRawSentinelNode* prev) { m_prev = prev; }
     void setNext(BasicRawSentinelNode* next) { m_next = next; }
     
-    T* prev() { return static_cast<T*>(m_prev); }
-    T* next() { return static_cast<T*>(m_next); }
+    T* prev() { return static_cast<T*>(PtrTraits::unwrap(m_prev)); }
+    T* next() { return static_cast<T*>(PtrTraits::unwrap(m_next)); }
     
     bool isOnList() const
     {
@@ -74,8 +72,8 @@ public:
     void append(BasicRawSentinelNode*);
     
 private:
-    BasicRawSentinelNode* m_next;
-    BasicRawSentinelNode* m_prev;
+    typename PtrTraits::StorageType m_next { nullptr };
+    typename PtrTraits::StorageType m_prev { nullptr };
 };
 
 template <typename T, typename RawNode = T> class SentinelLinkedList {
@@ -118,20 +116,20 @@ private:
     RawNode m_tailSentinel;
 };
 
-template <typename T> void BasicRawSentinelNode<T>::remove()
+template <typename T, typename PtrTraits> void BasicRawSentinelNode<T, PtrTraits>::remove()
 {
-    SentinelLinkedList<T, BasicRawSentinelNode<T>>::remove(static_cast<T*>(this));
+    SentinelLinkedList<T, BasicRawSentinelNode>::remove(static_cast<T*>(this));
 }
 
-template <typename T> void BasicRawSentinelNode<T>::prepend(BasicRawSentinelNode* node)
+template <typename T, typename PtrTraits> void BasicRawSentinelNode<T, PtrTraits>::prepend(BasicRawSentinelNode* node)
 {
-    SentinelLinkedList<T, BasicRawSentinelNode<T>>::prepend(
+    SentinelLinkedList<T, BasicRawSentinelNode>::prepend(
         static_cast<T*>(this), static_cast<T*>(node));
 }
 
-template <typename T> void BasicRawSentinelNode<T>::append(BasicRawSentinelNode* node)
+template <typename T, typename PtrTraits> void BasicRawSentinelNode<T, PtrTraits>::append(BasicRawSentinelNode* node)
 {
-    SentinelLinkedList<T, BasicRawSentinelNode<T>>::append(
+    SentinelLinkedList<T, BasicRawSentinelNode>::append(
         static_cast<T*>(this), static_cast<T*>(node));
 }
 
@@ -140,10 +138,10 @@ template <typename T, typename RawNode> inline SentinelLinkedList<T, RawNode>::S
     , m_tailSentinel(Sentinel)
 {
     m_headSentinel.setNext(&m_tailSentinel);
-    m_headSentinel.setPrev(0);
+    m_headSentinel.setPrev(nullptr);
 
     m_tailSentinel.setPrev(&m_headSentinel);
-    m_tailSentinel.setNext(0);
+    m_tailSentinel.setNext(nullptr);
 }
 
 template <typename T, typename RawNode> inline typename SentinelLinkedList<T, RawNode>::iterator SentinelLinkedList<T, RawNode>::begin()
@@ -200,8 +198,8 @@ template <typename T, typename RawNode> inline void SentinelLinkedList<T, RawNod
     prev->setNext(next);
     next->setPrev(prev);
     
-    node->setPrev(0);
-    node->setNext(0);
+    node->setPrev(nullptr);
+    node->setNext(nullptr);
 }
 
 template <typename T, typename RawNode>
@@ -271,7 +269,11 @@ inline void SentinelLinkedList<T, RawNode>::takeFrom(SentinelLinkedList<T, RawNo
     other.m_tailSentinel.setPrev(&other.m_headSentinel);
 }
 
+template<typename T>
+using PackedRawSentinelNode = BasicRawSentinelNode<T, PackedPtrTraits<T>>;
+
 }
 
 using WTF::BasicRawSentinelNode;
+using WTF::PackedRawSentinelNode;
 using WTF::SentinelLinkedList;
index 35ec44e..af65e08 100644 (file)
@@ -172,14 +172,10 @@ template<typename T> char (&ArrayLengthHelperFunction(T (&)[0]))[0];
 #endif
 #define WTF_ARRAY_LENGTH(array) sizeof(::WTF::ArrayLengthHelperFunction(array))
 
-ALWAYS_INLINE constexpr size_t roundUpToMultipleOfImpl0(size_t remainderMask, size_t x)
-{
-    return (x + remainderMask) & ~remainderMask;
-}
-
 ALWAYS_INLINE constexpr size_t roundUpToMultipleOfImpl(size_t divisor, size_t x)
 {
-    return roundUpToMultipleOfImpl0(divisor - 1, x);
+    size_t remainderMask = divisor - 1;
+    return (x + remainderMask) & ~remainderMask;
 }
 
 // Efficient implementation that takes advantage of powers of two.
index 1a37b4e..5a4e9ef 100644 (file)
 
 namespace WTF {
 
-template<typename IntegralType>
-inline IntegralType unalignedLoad(const void* pointer)
+template<typename Type>
+inline Type unalignedLoad(const void* pointer)
 {
-    static_assert(std::is_integral<IntegralType>::value || std::is_pointer<IntegralType>::value, "");
-    IntegralType result { };
-    memcpy(&result, pointer, sizeof(IntegralType));
+    static_assert(std::is_trivial<Type>::value, "");
+    Type result { };
+    memcpy(&result, pointer, sizeof(Type));
     return result;
 }
 
-template<typename IntegralType>
-inline void unalignedStore(void* pointer, IntegralType value)
+template<typename Type>
+inline void unalignedStore(void* pointer, Type value)
 {
-    static_assert(std::is_integral<IntegralType>::value || std::is_pointer<IntegralType>::value, "");
-    memcpy(pointer, &value, sizeof(IntegralType));
+    static_assert(std::is_trivial<Type>::value, "");
+    memcpy(pointer, &value, sizeof(Type));
 }
 
 } // namespace WTF
index aadfaca..17d6ca0 100644 (file)
@@ -1,3 +1,17 @@
+2019-05-12  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] Compress Watchpoint size by using enum type and Packed<> data structure
+        https://bugs.webkit.org/show_bug.cgi?id=197730
+
+        Reviewed by Filip Pizlo.
+
+        * TestWebKitAPI/CMakeLists.txt:
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WTF/MathExtras.cpp:
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WTF/Packed.cpp: Added.
+        (TestWebKitAPI::TEST):
+
 2019-05-10  Chris Dumez  <cdumez@apple.com>
 
         Unreviewed, fix ProcessSwap.OpenerLinkAfterAPIControlledProcessSwappingOfOpener API test
index bea6a69..c78fc07 100644 (file)
@@ -60,6 +60,7 @@ set(TestWTF_SOURCES
     Tests/WTF/NeverDestroyed.cpp
     Tests/WTF/OptionSet.cpp
     Tests/WTF/Optional.cpp
+    Tests/WTF/Packed.cpp
     Tests/WTF/ParkingLot.cpp
     Tests/WTF/PriorityQueue.cpp
     Tests/WTF/RedBlackTree.cpp
index 578b61a..1e8eb8c 100644 (file)
                E1220DCA155B28AA0013E2FC /* MemoryCacheDisableWithinResourceLoadDelegate.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = E1220DC9155B287D0013E2FC /* MemoryCacheDisableWithinResourceLoadDelegate.html */; };
                E194E1BD177E53C7009C4D4E /* StopLoadingFromDidReceiveResponse.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = E194E1BC177E534A009C4D4E /* StopLoadingFromDidReceiveResponse.html */; };
                E324A6F02041C82000A76593 /* UniqueArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E398BC0F2041C76300387136 /* UniqueArray.cpp */; };
+               E32B549222810AC4008AD702 /* Packed.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E32B549122810AC0008AD702 /* Packed.cpp */; };
                E373D7911F2CF35200C6FAAF /* Signals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3953F951F2CF32100A76A2E /* Signals.cpp */; };
                E38A0D351FD50CC300E98C8B /* Threading.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38A0D341FD50CBC00E98C8B /* Threading.cpp */; };
                E3A1E77F21B25B39008C6007 /* URLParserTextEncoding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3A1E77E21B25B39008C6007 /* URLParserTextEncoding.cpp */; };
                E194E1BA177E5145009C4D4E /* StopLoadingFromDidReceiveResponse.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = StopLoadingFromDidReceiveResponse.mm; sourceTree = "<group>"; };
                E194E1BC177E534A009C4D4E /* StopLoadingFromDidReceiveResponse.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = StopLoadingFromDidReceiveResponse.html; sourceTree = "<group>"; };
                E19DB9781B32137C00DB38D4 /* NavigatorLanguage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = NavigatorLanguage.mm; sourceTree = "<group>"; };
+               E32B549122810AC0008AD702 /* Packed.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Packed.cpp; sourceTree = "<group>"; };
                E388887020C9098100E632BC /* WorkerPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WorkerPool.cpp; sourceTree = "<group>"; };
                E38A0D341FD50CBC00E98C8B /* Threading.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Threading.cpp; sourceTree = "<group>"; };
                E3953F951F2CF32100A76A2E /* Signals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Signals.cpp; sourceTree = "<group>"; };
                                A57D54F21F338C3600A97AA7 /* NeverDestroyed.cpp */,
                                1AFDE6541953B2C000C48FFA /* Optional.cpp */,
                                CE50D8C81C8665CE0072EA5A /* OptionSet.cpp */,
+                               E32B549122810AC0008AD702 /* Packed.cpp */,
                                0FE447971B76F1E3009498EB /* ParkingLot.cpp */,
                                53EC253F1E96BC80000831B9 /* PriorityQueue.cpp */,
                                0FC6C4CB141027E0005B7F0C /* RedBlackTree.cpp */,
                                7C83DF011D0A590C00FEBCF3 /* Optional.cpp in Sources */,
                                1A77BAA31D9AFFFC005FC568 /* OptionSet.cpp in Sources */,
                                7C83DF021D0A590C00FEBCF3 /* OSObjectPtr.cpp in Sources */,
+                               E32B549222810AC4008AD702 /* Packed.cpp in Sources */,
                                7C83DF591D0A590C00FEBCF3 /* ParkingLot.cpp in Sources */,
                                53EC25411E96FD87000831B9 /* PriorityQueue.cpp in Sources */,
                                7C83DF131D0A590C00FEBCF3 /* RedBlackTree.cpp in Sources */,
index 7e66266..676d375 100644 (file)
@@ -507,4 +507,78 @@ TEST(WTF, getMSBSet)
     EXPECT_EQ(WTF::getMSBSet<uint64_t>(42), 5U);
 }
 
+TEST(WTF, clzConstexpr)
+{
+    EXPECT_EQ(WTF::clzConstexpr<int32_t>(1), 31U);
+    EXPECT_EQ(WTF::clzConstexpr<int32_t>(42), 26U);
+    EXPECT_EQ(WTF::clzConstexpr<uint32_t>(static_cast<uint32_t>(-1)), 0U);
+    EXPECT_EQ(WTF::clzConstexpr<uint32_t>(static_cast<uint32_t>(std::numeric_limits<int32_t>::min()) >> 1), 1U);
+    EXPECT_EQ(WTF::clzConstexpr<uint32_t>(0), 32U);
+
+    EXPECT_EQ(WTF::clzConstexpr<int8_t>(42), 2U);
+    EXPECT_EQ(WTF::clzConstexpr<int8_t>(3), 6U);
+    EXPECT_EQ(WTF::clzConstexpr<uint8_t>(static_cast<uint8_t>(-1)), 0U);
+    EXPECT_EQ(WTF::clzConstexpr<uint8_t>(0), 8U);
+
+    EXPECT_EQ(WTF::clzConstexpr<int64_t>(-1), 0U);
+    EXPECT_EQ(WTF::clzConstexpr<int64_t>(1), 63U);
+    EXPECT_EQ(WTF::clzConstexpr<int64_t>(3), 62U);
+    EXPECT_EQ(WTF::clzConstexpr<uint64_t>(42), 58U);
+    EXPECT_EQ(WTF::clzConstexpr<uint64_t>(0), 64U);
+}
+
+TEST(WTF, ctzConstexpr)
+{
+    EXPECT_EQ(WTF::ctzConstexpr<int32_t>(1), 0U);
+    EXPECT_EQ(WTF::ctzConstexpr<int32_t>(42), 1U);
+    EXPECT_EQ(WTF::ctzConstexpr<uint32_t>(static_cast<uint32_t>(-1)), 0U);
+    EXPECT_EQ(WTF::ctzConstexpr<uint32_t>(static_cast<uint32_t>(std::numeric_limits<int32_t>::min()) >> 1), 30U);
+    EXPECT_EQ(WTF::ctzConstexpr<uint32_t>(0), 32U);
+
+    EXPECT_EQ(WTF::ctzConstexpr<int8_t>(42), 1U);
+    EXPECT_EQ(WTF::ctzConstexpr<int8_t>(3), 0U);
+    EXPECT_EQ(WTF::ctzConstexpr<uint8_t>(static_cast<uint8_t>(-1)), 0U);
+    EXPECT_EQ(WTF::ctzConstexpr<uint8_t>(0), 8U);
+
+    EXPECT_EQ(WTF::ctzConstexpr<int64_t>(static_cast<uint32_t>(-1)), 0U);
+    EXPECT_EQ(WTF::ctzConstexpr<int64_t>(1), 0U);
+    EXPECT_EQ(WTF::ctzConstexpr<int64_t>(3), 0U);
+    EXPECT_EQ(WTF::ctzConstexpr<uint64_t>(42), 1U);
+    EXPECT_EQ(WTF::ctzConstexpr<uint64_t>(0), 64U);
+}
+
+TEST(WTF, getLSBSetConstexpr)
+{
+    EXPECT_EQ(WTF::getLSBSetConstexpr<int32_t>(1), 0U);
+    EXPECT_EQ(WTF::getLSBSetConstexpr<int32_t>(42), 1U);
+    EXPECT_EQ(WTF::getLSBSetConstexpr<uint32_t>(static_cast<uint32_t>(-1)), 0U);
+    EXPECT_EQ(WTF::getLSBSetConstexpr<uint32_t>(static_cast<uint32_t>(std::numeric_limits<int32_t>::min()) >> 1), 30U);
+
+    EXPECT_EQ(WTF::getLSBSetConstexpr<int8_t>(42), 1U);
+    EXPECT_EQ(WTF::getLSBSetConstexpr<int8_t>(3), 0U);
+    EXPECT_EQ(WTF::getLSBSetConstexpr<uint8_t>(static_cast<uint8_t>(-1)), 0U);
+
+    EXPECT_EQ(WTF::getLSBSetConstexpr<int64_t>(-1), 0U);
+    EXPECT_EQ(WTF::getLSBSetConstexpr<int64_t>(1), 0U);
+    EXPECT_EQ(WTF::getLSBSetConstexpr<int64_t>(3), 0U);
+    EXPECT_EQ(WTF::getLSBSetConstexpr<uint64_t>(42), 1U);
+}
+
+TEST(WTF, getMSBSetConstexpr)
+{
+    EXPECT_EQ(WTF::getMSBSetConstexpr<int32_t>(1), 0U);
+    EXPECT_EQ(WTF::getMSBSetConstexpr<int32_t>(42), 5U);
+    EXPECT_EQ(WTF::getMSBSetConstexpr<uint32_t>(static_cast<uint32_t>(-1)), 31U);
+    EXPECT_EQ(WTF::getMSBSetConstexpr<uint32_t>(static_cast<uint32_t>(std::numeric_limits<int32_t>::min()) >> 1), 30U);
+
+    EXPECT_EQ(WTF::getMSBSetConstexpr<int8_t>(42), 5U);
+    EXPECT_EQ(WTF::getMSBSetConstexpr<int8_t>(3), 1U);
+    EXPECT_EQ(WTF::getMSBSetConstexpr<uint8_t>(static_cast<uint8_t>(-1)), 7U);
+
+    EXPECT_EQ(WTF::getMSBSetConstexpr<int64_t>(-1), 63U);
+    EXPECT_EQ(WTF::getMSBSetConstexpr<int64_t>(1), 0U);
+    EXPECT_EQ(WTF::getMSBSetConstexpr<int64_t>(3), 1U);
+    EXPECT_EQ(WTF::getMSBSetConstexpr<uint64_t>(42), 5U);
+}
+
 } // namespace TestWebKitAPI
diff --git a/Tools/TestWebKitAPI/Tests/WTF/Packed.cpp b/Tools/TestWebKitAPI/Tests/WTF/Packed.cpp
new file mode 100644 (file)
index 0000000..b4849bd
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "config.h"
+
+#include <wtf/Packed.h>
+
+namespace TestWebKitAPI {
+
+struct PackedPair {
+    PackedPtr<uint8_t> key { nullptr };
+    PackedPtr<uint8_t> value { nullptr };
+};
+
+TEST(WTF_Packed, StructSize)
+{
+    EXPECT_EQ(alignof(PackedPair), 1U);
+#if CPU(X86_64)
+    EXPECT_EQ(sizeof(PackedPair), 12U);
+#endif
+    {
+        Packed<double> value;
+        value = 4.2;
+        EXPECT_EQ(value.get(), 4.2);
+    }
+    {
+        uint64_t originalValue = 0xff00ff00dd00dd00UL;
+        Packed<uint64_t> value;
+        value = originalValue;
+        EXPECT_EQ(value.get(), originalValue);
+        EXPECT_EQ(alignof(Packed<uint64_t>), 1U);
+        EXPECT_EQ(sizeof(Packed<uint64_t>), sizeof(uint64_t));
+    }
+}
+
+TEST(WTF_Packed, AssignAndGet)
+{
+    {
+        PackedPtr<uint8_t> key { nullptr };
+        static_assert(WTF_CPU_EFFECTIVE_ADDRESS_WIDTH != 64, "");
+        uint8_t* max = bitwise_cast<uint8_t*>(static_cast<uintptr_t>(((1ULL) << WTF_CPU_EFFECTIVE_ADDRESS_WIDTH) - 1));
+        key = max;
+        EXPECT_EQ(key.get(), max);
+    }
+}
+
+TEST(WTF_Packed, PackedAlignedPtr)
+{
+    {
+        PackedAlignedPtr<uint8_t, 256> key { nullptr };
+        EXPECT_LE(sizeof(key), 5U);
+    }
+    {
+        PackedAlignedPtr<uint8_t, 16> key { nullptr };
+#if OS(DARWIN) && CPU(ARM64)
+        EXPECT_EQ(sizeof(key), 4U);
+#else
+        EXPECT_LE(sizeof(key), 6U);
+#endif
+    }
+}
+
+} // namespace TestWebKitAPI