Experiment with alternative implementation of memcpy/memset
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 9 Feb 2018 02:13:01 +0000 (02:13 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 9 Feb 2018 02:13:01 +0000 (02:13 +0000)
https://bugs.webkit.org/show_bug.cgi?id=182563

Reviewed by Michael Saboff and Mark Lam.

Source/bmalloc:

Add a faster x86_64-specific implementation of memcpy and memset. Ideally, this would just be
implemented in WTF, but we have to copy it into bmalloc since bmalloc sits below WTF on the
stack.

* bmalloc/Algorithm.h:
(bmalloc::fastCopy):
(bmalloc::fastZeroFill):
* bmalloc/Allocator.cpp:
(bmalloc::Allocator::reallocate):
* bmalloc/Bits.h:
(bmalloc::BitsWordOwner::operator=):
(bmalloc::BitsWordOwner::clearAll):
(bmalloc::BitsWordOwner::set):
* bmalloc/IsoPageInlines.h:
(bmalloc::IsoPage<Config>::IsoPage):
* bmalloc/Vector.h:
(bmalloc::Vector<T>::reallocateBuffer):

Source/JavaScriptCore:

This adopts new fastCopy/fastZeroFill calls for calls to memcpy/memset that do not take a
constant size argument.

* assembler/AssemblerBuffer.h:
(JSC::AssemblerBuffer::append):
* runtime/ArrayBuffer.cpp:
(JSC::ArrayBufferContents::tryAllocate):
(JSC::ArrayBufferContents::copyTo):
(JSC::ArrayBuffer::createInternal):
* runtime/ArrayBufferView.h:
(JSC::ArrayBufferView::zeroRangeImpl):
* runtime/ArrayConventions.cpp:
* runtime/ArrayConventions.h:
(JSC::clearArray):
* runtime/ArrayPrototype.cpp:
(JSC::arrayProtoPrivateFuncConcatMemcpy):
* runtime/ButterflyInlines.h:
(JSC::Butterfly::tryCreate):
(JSC::Butterfly::createOrGrowPropertyStorage):
(JSC::Butterfly::growArrayRight):
(JSC::Butterfly::resizeArray):
* runtime/GenericTypedArrayViewInlines.h:
(JSC::GenericTypedArrayView<Adaptor>::create):
* runtime/JSArray.cpp:
(JSC::JSArray::appendMemcpy):
(JSC::JSArray::fastSlice):
* runtime/JSArrayBufferView.cpp:
(JSC::JSArrayBufferView::ConstructionContext::ConstructionContext):
* runtime/JSGenericTypedArrayViewInlines.h:
(JSC::JSGenericTypedArrayView<Adaptor>::set):
* runtime/JSObject.cpp:
(JSC::JSObject::constructConvertedArrayStorageWithoutCopyingElements):
(JSC::JSObject::shiftButterflyAfterFlattening):
* runtime/PropertyTable.cpp:
(JSC::PropertyTable::PropertyTable):

Source/WTF:

Adds a faster x86_64-specific implementation of memcpy and memset. These versions go by
different names than memcpy/memset and have a different API:

WTF::fastCopy<T>(T* dst, T* src, size_t N): copies N values of type T from src to dst.
WTF::fastZeroFill(T* dst, size_T N): writes N * sizeof(T) zeroes to dst.

There are also *Bytes variants that take void* for dst and src and size_t numBytes. Those are
most appropriate in places where the code is already computing bytes.

These will just call memcpy/memset on platforms where the optimized versions are not supported.

These new functions are not known to the compiler to be memcpy/memset. This has the effect that
the compiler will not try to replace them with anything else. This could be good or bad:

- It's *good* if the size is *not known* at compile time. In that case, by my benchmarks, these
  versions are faster than either the memcpy/memset call or whatever else the compiler could
  emit. This is because of a combination of inlining and the algorithm itself (see below).

- It's *bad* if the size is *known* at compile time. In that case, the compiler could
  potentially emit a fully unrolled memcpy/memset. That might not happen if the size is large
  (even if it's known), but in this patch I avoid replacing any memcpy/memset calls when the
  size is a constant. In particular, this totally avoids the call overhead -- if the size is
  small, then the compiler will emit a nice inlined copy or set. If the size is large, then the
  most optimal thing to do is emit the shortest piece of code possible, and that's a call to
  memcpy/memset.

It's unfortunate that you have to choose between them on your own. One way to avoid that might
have been to override the memcpy/memset symbols, so that the compiler can still do its
reasoning. But that's not quite right, since then we would lose inlining in the unknonw-size
case. Also, it's possible that for some unknown-size cases, the compiler could choose to emit
something on its own because it might think that some property of aliasing or alignment could
help it. I think it's a bit better to use our own copy/set implementations even in those cases.
Another way that I tried avoiding this is to detect inside fastCopy/fastZeroFill if the size is
constant. But there is no good way to do that in C++. There is a builtin for doing that inside a
macro, but that feels janky, so I didn't want to do it in this patch.

The reason why these new fastCopy/fastZeroFill functions are faster is that:

- They can be inlined. There is no function call. Only a few registers get clobbered. So, the
  impact on the quality of the code surrounding the memcpy/memset is smaller.

- They use type information to select the implementation. For sizes that are multiples of 2, 4,
  or 8, the resulting code performs dramatically better on small arrays than memcpy because it
  uses fewer cycles. The difference is greatest for 2 and 4 byte types, since memcpy usually
  handles small arrays by tiering from a 8-byte word copy loop to a byte copy loop. So, for 2
  or 4 byte arrays, we use an algorithm that tiers from 8-byte word down to a 2-byte or 4-byte
  (depending on type) copy loop. So, for example, when copying a 16-bit string that has 1, 2, or
  3 characters, this means doing 1, 2, or 3 word copies rather than 2, 4, or 6 byte copies. For
  8-byte types, the resulting savings are mainly that there is no check to see if a tier-down to
  the byte-copy loop is needed -- so really that means reducing code size. 1-byte types don't
  get this inherent advantage over memcpy/memset, but they still benefit from all of the other
  advantages of these functions. Of course, this advantage isn't inherent to our approach. The
  compiler could also notice that the arguments to memcpy/memset have some alignment properties.
  It could do it even more generally than we do - for example a copy over bytes where the size
  is a multiple of 4 can use the 4-byte word algorithm. But based on my tests, the compiler does
  not do this (even though it does other things, like turn a memset call with a zero value
  argument into a bzero call).

- They use a very nicely written word copy/set loop for small arrays. I spent a lot of time
  getting the assembly just right. When we use memcpy/memset, sometimes we would optimize the
  call by having a fast path word copy loop for small sizes. That's not necessary with this
  implementation, since the assembly copy loop gets inlined.

- They use `rep movs` or `rep stos` for copies of 200 bytes or more. This decision benchmarks
  poorly on every synthetic memcpy/memset benchmark I have built, and so unsurprisingly, it's
  not what system memcpy/memset does. Most system memcpy/memset implementations end up doing
  some SSE for medium-sized copies,. However, I previously found that this decision is bad for
  one of the memset calls in GC (see clearArray() and friends in ArrayConventions.h|cpp) - I was
  able to make the overhead of that call virtually disappear by doing `rep stos` more
  aggressively. The theory behind this change is that it's not just the GC that prefers smaller
  `rep` threshold and no SSE. I am betting that `rep`ing more is better when the heap gets
  chaotic and the data being copied is used in interesting ways -- hence, synthetic
  memcpy/memset benchmarks think it's bad (they don't do enough chaotic memory accesses) while
  it's good for real-world uses. Also, when I previously worked on JVMs, I had found that the
  best memcpy/memset heuristics when dealing with GC'd objects in a crazy heap were different
  than any memcpy/memset in any system library.

This appears to be a 0.9% speed-up on PLT. I'm not sure if it's more because of the inlining or
the `rep`. I think it's both. I'll leave figuring out the exact tuning for future patches.

* wtf/BitVector.cpp:
(WTF::BitVector::setSlow):
(WTF::BitVector::clearAll):
(WTF::BitVector::resizeOutOfLine):
* wtf/BitVector.h:
(WTF::BitVector::wordCount):
(WTF::BitVector::OutOfLineBits::numWords const):
* wtf/ConcurrentBuffer.h:
(WTF::ConcurrentBuffer::growExact):
* wtf/FastBitVector.h:
(WTF::FastBitVectorWordOwner::operator=):
(WTF::FastBitVectorWordOwner::clearAll):
(WTF::FastBitVectorWordOwner::set):
* wtf/FastCopy.h: Added.
(WTF::fastCopy):
(WTF::fastCopyBytes):
* wtf/FastMalloc.cpp:
(WTF::fastZeroedMalloc):
(WTF::fastStrDup):
(WTF::tryFastZeroedMalloc):
* wtf/FastZeroFill.h: Added.
(WTF::fastZeroFill):
(WTF::fastZeroFillBytes):
* wtf/MD5.cpp:
* wtf/OSAllocator.h:
(WTF::OSAllocator::reallocateCommitted):
* wtf/StringPrintStream.cpp:
(WTF::StringPrintStream::increaseSize):
* wtf/Vector.h:
* wtf/persistence/PersistentDecoder.cpp:
(WTF::Persistence::Decoder::decodeFixedLengthData):
* wtf/persistence/PersistentEncoder.cpp:
(WTF::Persistence::Encoder::encodeFixedLengthData):
* wtf/text/CString.cpp:
(WTF::CString::init):
(WTF::CString::copyBufferIfNeeded):
* wtf/text/LineBreakIteratorPoolICU.h:
(WTF::LineBreakIteratorPool::makeLocaleWithBreakKeyword):
* wtf/text/StringBuilder.cpp:
(WTF::StringBuilder::allocateBuffer):
(WTF::StringBuilder::append):
* wtf/text/StringConcatenate.h:
* wtf/text/StringImpl.h:
(WTF::StringImpl::copyCharacters):
* wtf/text/icu/UTextProvider.cpp:
(WTF::uTextCloneImpl):
* wtf/text/icu/UTextProviderLatin1.cpp:
(WTF::uTextLatin1Clone):
(WTF::openLatin1UTextProvider):
* wtf/threads/Signals.cpp:

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

45 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/assembler/AssemblerBuffer.h
Source/JavaScriptCore/heap/LargeAllocation.cpp
Source/JavaScriptCore/heap/MarkedBlock.cpp
Source/JavaScriptCore/runtime/ArrayBuffer.cpp
Source/JavaScriptCore/runtime/ArrayBufferView.h
Source/JavaScriptCore/runtime/ArrayConventions.cpp
Source/JavaScriptCore/runtime/ArrayConventions.h
Source/JavaScriptCore/runtime/ArrayPrototype.cpp
Source/JavaScriptCore/runtime/ButterflyInlines.h
Source/JavaScriptCore/runtime/GenericTypedArrayViewInlines.h
Source/JavaScriptCore/runtime/JSArray.cpp
Source/JavaScriptCore/runtime/JSArrayBufferView.cpp
Source/JavaScriptCore/runtime/JSGenericTypedArrayViewInlines.h
Source/JavaScriptCore/runtime/JSObject.cpp
Source/JavaScriptCore/runtime/PropertyTable.cpp
Source/WTF/ChangeLog
Source/WTF/WTF.xcodeproj/project.pbxproj
Source/WTF/wtf/BitVector.cpp
Source/WTF/wtf/BitVector.h
Source/WTF/wtf/CMakeLists.txt
Source/WTF/wtf/ConcurrentBuffer.h
Source/WTF/wtf/FastBitVector.h
Source/WTF/wtf/FastCopy.h [new file with mode: 0644]
Source/WTF/wtf/FastMalloc.cpp
Source/WTF/wtf/FastZeroFill.h [new file with mode: 0644]
Source/WTF/wtf/OSAllocator.h
Source/WTF/wtf/StringPrintStream.cpp
Source/WTF/wtf/Vector.h
Source/WTF/wtf/persistence/PersistentDecoder.cpp
Source/WTF/wtf/persistence/PersistentEncoder.cpp
Source/WTF/wtf/text/CString.cpp
Source/WTF/wtf/text/LineBreakIteratorPoolICU.h
Source/WTF/wtf/text/StringBuilder.cpp
Source/WTF/wtf/text/StringConcatenate.h
Source/WTF/wtf/text/StringImpl.h
Source/WTF/wtf/text/icu/UTextProvider.cpp
Source/WTF/wtf/text/icu/UTextProviderLatin1.cpp
Source/WTF/wtf/threads/Signals.cpp
Source/bmalloc/ChangeLog
Source/bmalloc/bmalloc/Algorithm.h
Source/bmalloc/bmalloc/Allocator.cpp
Source/bmalloc/bmalloc/Bits.h
Source/bmalloc/bmalloc/IsoPageInlines.h
Source/bmalloc/bmalloc/Vector.h

index f3e7078..78e39af 100644 (file)
@@ -1,3 +1,46 @@
+2018-02-08  Filip Pizlo  <fpizlo@apple.com>
+
+        Experiment with alternative implementation of memcpy/memset
+        https://bugs.webkit.org/show_bug.cgi?id=182563
+
+        Reviewed by Michael Saboff and Mark Lam.
+        
+        This adopts new fastCopy/fastZeroFill calls for calls to memcpy/memset that do not take a
+        constant size argument.
+
+        * assembler/AssemblerBuffer.h:
+        (JSC::AssemblerBuffer::append):
+        * runtime/ArrayBuffer.cpp:
+        (JSC::ArrayBufferContents::tryAllocate):
+        (JSC::ArrayBufferContents::copyTo):
+        (JSC::ArrayBuffer::createInternal):
+        * runtime/ArrayBufferView.h:
+        (JSC::ArrayBufferView::zeroRangeImpl):
+        * runtime/ArrayConventions.cpp:
+        * runtime/ArrayConventions.h:
+        (JSC::clearArray):
+        * runtime/ArrayPrototype.cpp:
+        (JSC::arrayProtoPrivateFuncConcatMemcpy):
+        * runtime/ButterflyInlines.h:
+        (JSC::Butterfly::tryCreate):
+        (JSC::Butterfly::createOrGrowPropertyStorage):
+        (JSC::Butterfly::growArrayRight):
+        (JSC::Butterfly::resizeArray):
+        * runtime/GenericTypedArrayViewInlines.h:
+        (JSC::GenericTypedArrayView<Adaptor>::create):
+        * runtime/JSArray.cpp:
+        (JSC::JSArray::appendMemcpy):
+        (JSC::JSArray::fastSlice):
+        * runtime/JSArrayBufferView.cpp:
+        (JSC::JSArrayBufferView::ConstructionContext::ConstructionContext):
+        * runtime/JSGenericTypedArrayViewInlines.h:
+        (JSC::JSGenericTypedArrayView<Adaptor>::set):
+        * runtime/JSObject.cpp:
+        (JSC::JSObject::constructConvertedArrayStorageWithoutCopyingElements):
+        (JSC::JSObject::shiftButterflyAfterFlattening):
+        * runtime/PropertyTable.cpp:
+        (JSC::PropertyTable::PropertyTable):
+
 2018-02-08  Don Olmstead  <don.olmstead@sony.com>
 
         Remove JavaScriptCore/ForwardingHeaders directory
index 7340952..eac88f7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2012, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2008-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -276,7 +276,7 @@ namespace JSC {
             if (!isAvailable(size))
                 grow(size);
 
-            memcpy(m_storage.buffer() + m_index, data, size);
+            fastCopyBytes(m_storage.buffer() + m_index, data, size);
             m_index += size;
         }
 
index dfbf6ac..6dd021d 100644 (file)
@@ -45,7 +45,7 @@ LargeAllocation* LargeAllocation::tryCreate(Heap& heap, size_t size, Subspace* s
         return nullptr;
     
     // Make sure that the padding does not contain useful things.
-    memset(static_cast<char*>(space) + sizeBeforeDistancing, 0, distancing);
+    fastZeroFillBytes(static_cast<char*>(space) + sizeBeforeDistancing, distancing);
     
     if (scribbleFreeCells())
         scribble(space, size);
index f6a4cb7..6c10322 100644 (file)
@@ -493,7 +493,7 @@ void MarkedBlock::Handle::associateWithOrigin(SecurityOriginToken securityOrigin
     if (m_securityOriginToken == securityOriginToken)
         return;
     
-    memset(&block(), 0, endAtom * atomSize);
+    fastZeroFillBytes(&block(), endAtom * atomSize);
     m_securityOriginToken = securityOriginToken;
 }
 
index 4171f2e..b5867e4 100644 (file)
@@ -113,7 +113,7 @@ void ArrayBufferContents::tryAllocate(unsigned numElements, unsigned elementByte
     }
     
     if (policy == ZeroInitialize)
-        memset(m_data.get(), 0, size);
+        fastZeroFillBytes(m_data.get(), size);
 
     m_sizeInBytes = numElements * elementByteSize;
     m_destructor = [] (void* p) { Gigacage::free(Gigacage::Primitive, p); };
@@ -141,7 +141,7 @@ void ArrayBufferContents::copyTo(ArrayBufferContents& other)
     other.tryAllocate(m_sizeInBytes, sizeof(char), ArrayBufferContents::DontInitialize);
     if (!other.m_data)
         return;
-    memcpy(other.m_data.get(), m_data.get(), m_sizeInBytes);
+    fastCopyBytes(other.m_data.get(), m_data.get(), m_sizeInBytes);
     other.m_sizeInBytes = m_sizeInBytes;
 }
 
@@ -246,7 +246,7 @@ Ref<ArrayBuffer> ArrayBuffer::createInternal(ArrayBufferContents&& contents, con
 {
     ASSERT(!byteLength || source);
     auto buffer = adoptRef(*new ArrayBuffer(WTFMove(contents)));
-    memcpy(buffer->data(), source, byteLength);
+    fastCopyBytes(buffer->data(), source, byteLength);
     return buffer;
 }
 
index 703bc62..f4be7ba 100644 (file)
@@ -215,7 +215,7 @@ bool ArrayBufferView::zeroRangeImpl(unsigned byteOffset, size_t rangeByteLength)
     }
     
     uint8_t* base = static_cast<uint8_t*>(baseAddress());
-    memset(base + byteOffset, 0, rangeByteLength);
+    fastZeroFillBytes(base + byteOffset, rangeByteLength);
     return true;
 }
 
index 69a4789..93a08ab 100644 (file)
 namespace JSC {
 
 #if USE(JSVALUE64)
-void clearArrayMemset(WriteBarrier<Unknown>* base, unsigned count)
-{
-#if CPU(X86_64) && COMPILER(GCC_OR_CLANG)
-    uint64_t zero = 0;
-    asm volatile (
-        "rep stosq\n\t"
-        : "+D"(base), "+c"(count)
-        : "a"(zero)
-        : "memory"
-        );
-#else // not CPU(X86_64)
-    memset(base, 0, count * sizeof(WriteBarrier<Unknown>));
-#endif // generic CPU
-}
-
 void clearArrayMemset(double* base, unsigned count)
 {
 #if CPU(X86_64) && COMPILER(GCC_OR_CLANG)
index aab19d0..101847d 100644 (file)
@@ -117,22 +117,17 @@ inline IndexingHeader baseIndexingHeaderForArrayStorage(unsigned length)
 }
 
 #if USE(JSVALUE64)
-JS_EXPORT_PRIVATE void clearArrayMemset(WriteBarrier<Unknown>* base, unsigned count);
 JS_EXPORT_PRIVATE void clearArrayMemset(double* base, unsigned count);
 #endif // USE(JSVALUE64)
 
 ALWAYS_INLINE void clearArray(WriteBarrier<Unknown>* base, unsigned count)
 {
 #if USE(JSVALUE64)
-    const unsigned minCountForMemset = 100;
-    if (count >= minCountForMemset) {
-        clearArrayMemset(base, count);
-        return;
-    }
-#endif
-    
+    fastZeroFill(base, count);
+#else
     for (unsigned i = count; i--;)
         base[i].clear();
+#endif
 }
 
 ALWAYS_INLINE void clearArray(double* base, unsigned count)
index efbaf32..a17b7c6 100644 (file)
@@ -1341,19 +1341,18 @@ EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncConcatMemcpy(ExecState* exec)
     
     if (type == ArrayWithDouble) {
         double* buffer = result->butterfly()->contiguousDouble().data();
-        memcpy(buffer, firstButterfly->contiguousDouble().data(), sizeof(JSValue) * firstArraySize);
-        memcpy(buffer + firstArraySize, secondButterfly->contiguousDouble().data(), sizeof(JSValue) * secondArraySize);
+        fastCopy(buffer, firstButterfly->contiguousDouble().data(), firstArraySize);
+        fastCopy(buffer + firstArraySize, secondButterfly->contiguousDouble().data(), secondArraySize);
     } else if (type != ArrayWithUndecided) {
         WriteBarrier<Unknown>* buffer = result->butterfly()->contiguous().data();
         
         auto copy = [&] (unsigned offset, void* source, unsigned size, IndexingType type) {
             if (type != ArrayWithUndecided) {
-                memcpy(buffer + offset, source, sizeof(JSValue) * size);
+                fastCopy(buffer + offset, static_cast<WriteBarrier<Unknown>*>(source), size);
                 return;
             }
             
-            for (unsigned i = size; i--;)
-                buffer[i + offset].clear();
+            clearArray(buffer + offset, size);
         };
         
         copy(0, firstButterfly->contiguous().data(), firstArraySize, firstType);
index 6265f58..403ac9c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -93,7 +93,7 @@ inline Butterfly* Butterfly::tryCreate(VM& vm, JSCell*, size_t preCapacity, size
     Butterfly* result = fromBase(base, preCapacity, propertyCapacity);
     if (hasIndexingHeader)
         *result->indexingHeader() = indexingHeader;
-    memset(result->propertyStorage() - propertyCapacity, 0, propertyCapacity * sizeof(EncodedJSValue));
+    fastZeroFill(result->propertyStorage() - propertyCapacity, propertyCapacity);
     return result;
 }
 
@@ -129,14 +129,13 @@ inline Butterfly* Butterfly::createOrGrowPropertyStorage(
     bool hasIndexingHeader = structure->hasIndexingHeader(intendedOwner);
     Butterfly* result = createUninitialized(
         vm, intendedOwner, preCapacity, newPropertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes);
-    memcpy(
+    fastCopyBytes(
         result->propertyStorage() - oldPropertyCapacity,
         oldButterfly->propertyStorage() - oldPropertyCapacity,
         totalSize(0, oldPropertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes));
-    memset(
+    fastZeroFill(
         result->propertyStorage() - newPropertyCapacity,
-        0,
-        (newPropertyCapacity - oldPropertyCapacity) * sizeof(EncodedJSValue));
+        newPropertyCapacity - oldPropertyCapacity);
     return result;
 }
 
@@ -168,8 +167,7 @@ inline Butterfly* Butterfly::growArrayRight(
     void* newBase = vm.jsValueGigacageAuxiliarySpace.allocateNonVirtual(vm, newSize, nullptr, AllocationFailureMode::ReturnNull);
     if (!newBase)
         return nullptr;
-    // FIXME: This probably shouldn't be a memcpy.
-    memcpy(newBase, theBase, oldSize);
+    fastCopyBytes(newBase, theBase, oldSize);
     return fromBase(newBase, 0, propertyCapacity);
 }
 
@@ -199,7 +197,7 @@ inline Butterfly* Butterfly::resizeArray(
     size_t size = std::min(
         totalSize(0, propertyCapacity, oldHasIndexingHeader, oldIndexingPayloadSizeInBytes),
         totalSize(0, propertyCapacity, newHasIndexingHeader, newIndexingPayloadSizeInBytes));
-    memcpy(to, from, size);
+    fastCopyBytes(to, from, size);
     return result;
 }
 
index 1b6da56..254f628 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013, 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -52,7 +52,7 @@ RefPtr<GenericTypedArrayView<Adaptor>> GenericTypedArrayView<Adaptor>::create(
     const typename Adaptor::Type* array, unsigned length)
 {
     RefPtr<GenericTypedArrayView> result = create(length);
-    memcpy(result->data(), array, length * sizeof(typename Adaptor::Type));
+    fastCopy(result->data(), array, length);
     return result;
 }
 
index fd6770d..1cf0b86 100644 (file)
@@ -553,9 +553,9 @@ bool JSArray::appendMemcpy(ExecState* exec, VM& vm, unsigned startIndex, JSC::JS
                 butterfly->contiguousInt32().at(this, i).setWithoutWriteBarrier(JSValue());
         }
     } else if (type == ArrayWithDouble)
-        memcpy(butterfly()->contiguousDouble().data() + startIndex, otherArray->butterfly()->contiguousDouble().data(), sizeof(JSValue) * otherLength);
+        fastCopy(butterfly()->contiguousDouble().data() + startIndex, otherArray->butterfly()->contiguousDouble().data(), otherLength);
     else
-        memcpy(butterfly()->contiguous().data() + startIndex, otherArray->butterfly()->contiguous().data(), sizeof(JSValue) * otherLength);
+        fastCopy(butterfly()->contiguous().data() + startIndex, otherArray->butterfly()->contiguous().data(), otherLength);
 
     return true;
 }
@@ -761,9 +761,9 @@ JSArray* JSArray::fastSlice(ExecState& exec, unsigned startIndex, unsigned count
 
         auto& resultButterfly = *resultArray->butterfly();
         if (arrayType == ArrayWithDouble)
-            memcpy(resultButterfly.contiguousDouble().data(), butterfly()->contiguousDouble().data() + startIndex, sizeof(JSValue) * count);
+            fastCopy(resultButterfly.contiguousDouble().data(), butterfly()->contiguousDouble().data() + startIndex, count);
         else
-            memcpy(resultButterfly.contiguous().data(), butterfly()->contiguous().data() + startIndex, sizeof(JSValue) * count);
+            fastCopy(resultButterfly.contiguous().data(), butterfly()->contiguous().data() + startIndex, count);
         resultButterfly.setPublicLength(count);
 
         return resultArray;
index dea3b13..ef7a187 100644 (file)
@@ -94,7 +94,7 @@ JSArrayBufferView::ConstructionContext::ConstructionContext(
     if (!m_vector)
         return;
     if (mode == ZeroFill)
-        memset(m_vector.get(), 0, size);
+        fastZeroFillBytes(m_vector.get(), size);
     
     vm.heap.reportExtraMemoryAllocated(static_cast<size_t>(length) * elementSize);
     
index 7857583..4a872a9 100644 (file)
@@ -246,7 +246,7 @@ bool JSGenericTypedArrayView<Adaptor>::set(
 
     const ClassInfo* ci = object->classInfo(vm);
     if (ci->typedArrayStorageType == Adaptor::typeValue) {
-        // The super fast case: we can just memcpy since we're the same type.
+        // The super fast case: we can just memmove since we're the same type.
         JSGenericTypedArrayView* other = jsCast<JSGenericTypedArrayView*>(object);
         length = std::min(length, other->length());
         
index 078f50f..e516e8f 100644 (file)
@@ -1178,10 +1178,10 @@ ArrayStorage* JSObject::constructConvertedArrayStorageWithoutCopyingElements(VM&
     Butterfly* newButterfly = Butterfly::createUninitialized(
         vm, this, 0, propertyCapacity, true, ArrayStorage::sizeFor(neededLength));
     
-    memcpy(
+    fastCopy(
         newButterfly->propertyStorage() - propertySize,
         m_butterfly->propertyStorage() - propertySize,
-        propertySize * sizeof(EncodedJSValue));
+        propertySize);
     
     ArrayStorage* newStorage = newButterfly->arrayStorage();
     newStorage->setVectorLength(neededLength);
@@ -3580,7 +3580,7 @@ void JSObject::shiftButterflyAfterFlattening(const GCSafeConcurrentJSLocker&, VM
     void* currentBase = oldButterfly->base(0, outOfLineCapacityAfter);
     void* newBase = newButterfly->base(0, outOfLineCapacityAfter);
 
-    memcpy(newBase, currentBase, Butterfly::totalSize(0, outOfLineCapacityAfter, hasIndexingHeader, indexingPayloadSizeInBytes));
+    fastCopyBytes(newBase, currentBase, Butterfly::totalSize(0, outOfLineCapacityAfter, hasIndexingHeader, indexingPayloadSizeInBytes));
     
     setButterfly(vm, newButterfly);
 }
index 59ec41c..28ac1b2 100644 (file)
@@ -74,7 +74,7 @@ PropertyTable::PropertyTable(VM& vm, const PropertyTable& other)
 {
     ASSERT(isPowerOf2(m_indexSize));
 
-    memcpy(m_index, other.m_index, dataSize());
+    fastCopyBytes(m_index, other.m_index, dataSize());
 
     iterator end = this->end();
     for (iterator iter = begin(); iter != end; ++iter)
index 5503cbb..a126b99 100644 (file)
@@ -1,3 +1,141 @@
+2018-02-08  Filip Pizlo  <fpizlo@apple.com>
+
+        Experiment with alternative implementation of memcpy/memset
+        https://bugs.webkit.org/show_bug.cgi?id=182563
+
+        Reviewed by Michael Saboff and Mark Lam.
+        
+        Adds a faster x86_64-specific implementation of memcpy and memset. These versions go by
+        different names than memcpy/memset and have a different API:
+        
+        WTF::fastCopy<T>(T* dst, T* src, size_t N): copies N values of type T from src to dst.
+        WTF::fastZeroFill(T* dst, size_T N): writes N * sizeof(T) zeroes to dst.
+        
+        There are also *Bytes variants that take void* for dst and src and size_t numBytes. Those are
+        most appropriate in places where the code is already computing bytes.
+        
+        These will just call memcpy/memset on platforms where the optimized versions are not supported.
+        
+        These new functions are not known to the compiler to be memcpy/memset. This has the effect that
+        the compiler will not try to replace them with anything else. This could be good or bad:
+        
+        - It's *good* if the size is *not known* at compile time. In that case, by my benchmarks, these
+          versions are faster than either the memcpy/memset call or whatever else the compiler could
+          emit. This is because of a combination of inlining and the algorithm itself (see below).
+        
+        - It's *bad* if the size is *known* at compile time. In that case, the compiler could
+          potentially emit a fully unrolled memcpy/memset. That might not happen if the size is large
+          (even if it's known), but in this patch I avoid replacing any memcpy/memset calls when the
+          size is a constant. In particular, this totally avoids the call overhead -- if the size is
+          small, then the compiler will emit a nice inlined copy or set. If the size is large, then the
+          most optimal thing to do is emit the shortest piece of code possible, and that's a call to
+          memcpy/memset.
+        
+        It's unfortunate that you have to choose between them on your own. One way to avoid that might
+        have been to override the memcpy/memset symbols, so that the compiler can still do its
+        reasoning. But that's not quite right, since then we would lose inlining in the unknonw-size
+        case. Also, it's possible that for some unknown-size cases, the compiler could choose to emit
+        something on its own because it might think that some property of aliasing or alignment could
+        help it. I think it's a bit better to use our own copy/set implementations even in those cases.
+        Another way that I tried avoiding this is to detect inside fastCopy/fastZeroFill if the size is
+        constant. But there is no good way to do that in C++. There is a builtin for doing that inside a
+        macro, but that feels janky, so I didn't want to do it in this patch.
+        
+        The reason why these new fastCopy/fastZeroFill functions are faster is that:
+        
+        - They can be inlined. There is no function call. Only a few registers get clobbered. So, the
+          impact on the quality of the code surrounding the memcpy/memset is smaller.
+        
+        - They use type information to select the implementation. For sizes that are multiples of 2, 4,
+          or 8, the resulting code performs dramatically better on small arrays than memcpy because it
+          uses fewer cycles. The difference is greatest for 2 and 4 byte types, since memcpy usually
+          handles small arrays by tiering from a 8-byte word copy loop to a byte copy loop. So, for 2
+          or 4 byte arrays, we use an algorithm that tiers from 8-byte word down to a 2-byte or 4-byte
+          (depending on type) copy loop. So, for example, when copying a 16-bit string that has 1, 2, or
+          3 characters, this means doing 1, 2, or 3 word copies rather than 2, 4, or 6 byte copies. For
+          8-byte types, the resulting savings are mainly that there is no check to see if a tier-down to
+          the byte-copy loop is needed -- so really that means reducing code size. 1-byte types don't
+          get this inherent advantage over memcpy/memset, but they still benefit from all of the other
+          advantages of these functions. Of course, this advantage isn't inherent to our approach. The
+          compiler could also notice that the arguments to memcpy/memset have some alignment properties.
+          It could do it even more generally than we do - for example a copy over bytes where the size
+          is a multiple of 4 can use the 4-byte word algorithm. But based on my tests, the compiler does
+          not do this (even though it does other things, like turn a memset call with a zero value
+          argument into a bzero call).
+        
+        - They use a very nicely written word copy/set loop for small arrays. I spent a lot of time
+          getting the assembly just right. When we use memcpy/memset, sometimes we would optimize the
+          call by having a fast path word copy loop for small sizes. That's not necessary with this
+          implementation, since the assembly copy loop gets inlined.
+        
+        - They use `rep movs` or `rep stos` for copies of 200 bytes or more. This decision benchmarks
+          poorly on every synthetic memcpy/memset benchmark I have built, and so unsurprisingly, it's
+          not what system memcpy/memset does. Most system memcpy/memset implementations end up doing
+          some SSE for medium-sized copies,. However, I previously found that this decision is bad for
+          one of the memset calls in GC (see clearArray() and friends in ArrayConventions.h|cpp) - I was
+          able to make the overhead of that call virtually disappear by doing `rep stos` more
+          aggressively. The theory behind this change is that it's not just the GC that prefers smaller
+          `rep` threshold and no SSE. I am betting that `rep`ing more is better when the heap gets
+          chaotic and the data being copied is used in interesting ways -- hence, synthetic
+          memcpy/memset benchmarks think it's bad (they don't do enough chaotic memory accesses) while
+          it's good for real-world uses. Also, when I previously worked on JVMs, I had found that the
+          best memcpy/memset heuristics when dealing with GC'd objects in a crazy heap were different
+          than any memcpy/memset in any system library.
+        
+        This appears to be a 0.9% speed-up on PLT. I'm not sure if it's more because of the inlining or
+        the `rep`. I think it's both. I'll leave figuring out the exact tuning for future patches.
+
+        * wtf/BitVector.cpp:
+        (WTF::BitVector::setSlow):
+        (WTF::BitVector::clearAll):
+        (WTF::BitVector::resizeOutOfLine):
+        * wtf/BitVector.h:
+        (WTF::BitVector::wordCount):
+        (WTF::BitVector::OutOfLineBits::numWords const):
+        * wtf/ConcurrentBuffer.h:
+        (WTF::ConcurrentBuffer::growExact):
+        * wtf/FastBitVector.h:
+        (WTF::FastBitVectorWordOwner::operator=):
+        (WTF::FastBitVectorWordOwner::clearAll):
+        (WTF::FastBitVectorWordOwner::set):
+        * wtf/FastCopy.h: Added.
+        (WTF::fastCopy):
+        (WTF::fastCopyBytes):
+        * wtf/FastMalloc.cpp:
+        (WTF::fastZeroedMalloc):
+        (WTF::fastStrDup):
+        (WTF::tryFastZeroedMalloc):
+        * wtf/FastZeroFill.h: Added.
+        (WTF::fastZeroFill):
+        (WTF::fastZeroFillBytes):
+        * wtf/MD5.cpp:
+        * wtf/OSAllocator.h:
+        (WTF::OSAllocator::reallocateCommitted):
+        * wtf/StringPrintStream.cpp:
+        (WTF::StringPrintStream::increaseSize):
+        * wtf/Vector.h:
+        * wtf/persistence/PersistentDecoder.cpp:
+        (WTF::Persistence::Decoder::decodeFixedLengthData):
+        * wtf/persistence/PersistentEncoder.cpp:
+        (WTF::Persistence::Encoder::encodeFixedLengthData):
+        * wtf/text/CString.cpp:
+        (WTF::CString::init):
+        (WTF::CString::copyBufferIfNeeded):
+        * wtf/text/LineBreakIteratorPoolICU.h:
+        (WTF::LineBreakIteratorPool::makeLocaleWithBreakKeyword):
+        * wtf/text/StringBuilder.cpp:
+        (WTF::StringBuilder::allocateBuffer):
+        (WTF::StringBuilder::append):
+        * wtf/text/StringConcatenate.h:
+        * wtf/text/StringImpl.h:
+        (WTF::StringImpl::copyCharacters):
+        * wtf/text/icu/UTextProvider.cpp:
+        (WTF::uTextCloneImpl):
+        * wtf/text/icu/UTextProviderLatin1.cpp:
+        (WTF::uTextLatin1Clone):
+        (WTF::openLatin1UTextProvider):
+        * wtf/threads/Signals.cpp:
+
 2018-02-06  Darin Adler  <darin@apple.com>
 
         Event improvements
index 60a25bf..a3d7a7e 100644 (file)
                0F5F3D681F3FEBA600B115A2 /* CagedUniquePtr.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CagedUniquePtr.h; sourceTree = "<group>"; };
                0F60F32D1DFCBD1B00416D6C /* LockedPrintStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LockedPrintStream.cpp; sourceTree = "<group>"; };
                0F60F32E1DFCBD1B00416D6C /* LockedPrintStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LockedPrintStream.h; sourceTree = "<group>"; };
+               0F62A8A6202CCC14007B8623 /* FastCopy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FastCopy.h; sourceTree = "<group>"; };
+               0F62A8A7202CCC15007B8623 /* FastZeroFill.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FastZeroFill.h; sourceTree = "<group>"; };
                0F66B2801DC97BAB004A1D3F /* ClockType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ClockType.cpp; sourceTree = "<group>"; };
                0F66B2811DC97BAB004A1D3F /* ClockType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ClockType.h; sourceTree = "<group>"; };
                0F66B2821DC97BAB004A1D3F /* MonotonicTime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MonotonicTime.cpp; sourceTree = "<group>"; };
                                A8A4729F151A825A004123FF /* ExportMacros.h */,
                                0F7C5FB51D885CF20044F5E2 /* FastBitVector.cpp */,
                                0FD81AC4154FB22E00983E72 /* FastBitVector.h */,
+                               0F62A8A6202CCC14007B8623 /* FastCopy.h */,
                                A8A472A1151A825A004123FF /* FastMalloc.cpp */,
                                A8A472A2151A825A004123FF /* FastMalloc.h */,
                                0F79C7C31E73511800EB34D1 /* FastTLS.h */,
+                               0F62A8A7202CCC15007B8623 /* FastZeroFill.h */,
                                B38FD7BC168953E80065C969 /* FeatureDefines.h */,
                                0F9D335B165DBA73005AD387 /* FilePrintStream.cpp */,
                                0F9D335C165DBA73005AD387 /* FilePrintStream.h */,
index 25f29b5..ea9b743 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -29,7 +29,9 @@
 #include <algorithm>
 #include <string.h>
 #include <wtf/Assertions.h>
+#include <wtf/FastCopy.h>
 #include <wtf/FastMalloc.h>
+#include <wtf/FastZeroFill.h>
 #include <wtf/StdLibExtras.h>
 
 namespace WTF {
@@ -41,7 +43,7 @@ void BitVector::setSlow(const BitVector& other)
         newBitsOrPointer = other.m_bitsOrPointer;
     else {
         OutOfLineBits* newOutOfLineBits = OutOfLineBits::create(other.size());
-        memcpy(newOutOfLineBits->bits(), other.bits(), byteCount(other.size()));
+        fastCopy(newOutOfLineBits->bits(), other.bits(), wordCount(other.size()));
         newBitsOrPointer = bitwise_cast<uintptr_t>(newOutOfLineBits) >> 1;
     }
     if (!isInline() && !isEmptyOrDeletedValue())
@@ -69,7 +71,7 @@ void BitVector::clearAll()
     if (isInline())
         m_bitsOrPointer = makeInlineBits(0);
     else
-        memset(outOfLineBits()->bits(), 0, byteCount(size()));
+        fastZeroFill(outOfLineBits()->bits(), wordCount(size()));
 }
 
 BitVector::OutOfLineBits* BitVector::OutOfLineBits::create(size_t numBits)
@@ -93,14 +95,14 @@ void BitVector::resizeOutOfLine(size_t numBits)
     if (isInline()) {
         // Make sure that all of the bits are zero in case we do a no-op resize.
         *newOutOfLineBits->bits() = m_bitsOrPointer & ~(static_cast<uintptr_t>(1) << maxInlineBits());
-        memset(newOutOfLineBits->bits() + 1, 0, (newNumWords - 1) * sizeof(void*));
+        fastZeroFill(newOutOfLineBits->bits() + 1, newNumWords - 1);
     } else {
         if (numBits > size()) {
             size_t oldNumWords = outOfLineBits()->numWords();
-            memcpy(newOutOfLineBits->bits(), outOfLineBits()->bits(), oldNumWords * sizeof(void*));
-            memset(newOutOfLineBits->bits() + oldNumWords, 0, (newNumWords - oldNumWords) * sizeof(void*));
+            fastCopy(newOutOfLineBits->bits(), outOfLineBits()->bits(), oldNumWords);
+            fastZeroFill(newOutOfLineBits->bits() + oldNumWords, newNumWords - oldNumWords);
         } else
-            memcpy(newOutOfLineBits->bits(), outOfLineBits()->bits(), newOutOfLineBits->numWords() * sizeof(void*));
+            fastCopy(newOutOfLineBits->bits(), outOfLineBits()->bits(), newOutOfLineBits->numWords());
         OutOfLineBits::destroy(outOfLineBits());
     }
     m_bitsOrPointer = bitwise_cast<uintptr_t>(newOutOfLineBits) >> 1;
index 8c8bdd7..b3bba7c 100644 (file)
@@ -354,6 +354,11 @@ private:
         return (bitCount + 7) >> 3;
     }
 
+    static size_t wordCount(uintptr_t bits)
+    {
+        return (bits + bitsInPointer() - 1) / bitsInPointer();
+    }
+    
     static uintptr_t makeInlineBits(uintptr_t bits)
     {
         ASSERT(!(bits & (static_cast<uintptr_t>(1) << maxInlineBits())));
@@ -418,7 +423,7 @@ private:
     class OutOfLineBits {
     public:
         size_t numBits() const { return m_numBits; }
-        size_t numWords() const { return (m_numBits + bitsInPointer() - 1) / bitsInPointer(); }
+        size_t numWords() const { return wordCount(m_numBits); }
         uintptr_t* bits() { return bitwise_cast<uintptr_t*>(this + 1); }
         const uintptr_t* bits() const { return bitwise_cast<const uintptr_t*>(this + 1); }
         
index 220bafd..877d814 100644 (file)
@@ -59,8 +59,10 @@ set(WTF_HEADERS
     Expected.h
     ExportMacros.h
     FastBitVector.h
+    FastCopy.h
     FastMalloc.h
     FastTLS.h
+    FastZeroFill.h
     FeatureDefines.h
     FilePrintStream.h
     FlipBytes.h
index 6da7e28..c6db4c9 100644 (file)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include <wtf/Atomics.h>
+#include <wtf/FastCopy.h>
 #include <wtf/FastMalloc.h>
 #include <wtf/HashFunctions.h>
 #include <wtf/Lock.h>
@@ -65,7 +66,7 @@ public:
         Array* newArray = createArray(newSize);
         // This allows us to do ConcurrentBuffer<std::unique_ptr<>>.
         if (array)
-            memcpy(newArray->data, array->data, sizeof(T) * array->size);
+            fastCopy(newArray->data, array->data, array->size);
         for (size_t i = array ? array->size : 0; i < newSize; ++i)
             new (newArray->data + i) T();
         WTF::storeStoreFence();
index 9119a1d..52d4c78 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013, 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -27,7 +27,9 @@
 
 #include <string.h>
 #include <wtf/Atomics.h>
+#include <wtf/FastCopy.h>
 #include <wtf/FastMalloc.h>
+#include <wtf/FastZeroFill.h>
 #include <wtf/PrintStream.h>
 #include <wtf/StdLibExtras.h>
 
@@ -95,7 +97,7 @@ public:
         if (arrayLength() != other.arrayLength())
             setEqualsSlow(other);
         else {
-            memcpy(m_words, other.m_words, arrayLength() * sizeof(uint32_t));
+            fastCopy(m_words, other.m_words, arrayLength());
             m_numBits = other.m_numBits;
         }
         return *this;
@@ -115,13 +117,13 @@ public:
     
     void clearAll()
     {
-        memset(m_words, 0, arrayLength() * sizeof(uint32_t));
+        fastZeroFill(m_words, arrayLength());
     }
     
     void set(const FastBitVectorWordOwner& other)
     {
         ASSERT_WITH_SECURITY_IMPLICATION(m_numBits == other.m_numBits);
-        memcpy(m_words, other.m_words, arrayLength() * sizeof(uint32_t));
+        fastCopy(m_words, other.m_words, arrayLength());
     }
     
     size_t numBits() const
diff --git a/Source/WTF/wtf/FastCopy.h b/Source/WTF/wtf/FastCopy.h
new file mode 100644 (file)
index 0000000..a48c251
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#pragma once
+
+#include <wtf/StdLibExtras.h>
+
+namespace WTF {
+
+template<typename T>
+void fastCopy(T* dst, const T* src, size_t length)
+{
+#if CPU(X86_64) && COMPILER(GCC_OR_CLANG)
+    uint64_t tmp = 0;
+    size_t count = length * sizeof(T);
+    if (!(sizeof(T) % sizeof(uint64_t))) {
+        asm volatile (
+            "cmpq $200, %%rcx\n\t"
+            "jb 1f\n\t"
+            "shrq $3, %%rcx\n\t"
+            "rep movsq\n\t"
+            "jmp 2f\n\t"
+            "3:\n\t"
+            "movq (%%rsi, %%rcx), %%rax\n\t"
+            "movq %%rax, (%%rdi, %%rcx)\n\t"
+            "1:\n\t"
+            "subq $8, %%rcx\n\t"
+            "jae 3b\n\t"
+            "2:\n\t"
+            : "+D"(dst), "+S"(src), "+c"(count), "+a"(tmp)
+            :
+            : "memory"
+            );
+        return;
+    }
+    if (!(sizeof(T) % sizeof(uint32_t))) {
+        asm volatile (
+            "cmpq $200, %%rcx\n\t"
+            "jb 1f\n\t"
+            "shrq $2, %%rcx\n\t"
+            "rep movsl\n\t"
+            "jmp 2f\n\t"
+            "3:\n\t"
+            "movq (%%rsi, %%rcx), %%rax\n\t"
+            "movq %%rax, (%%rdi, %%rcx)\n\t"
+            "1:\n\t"
+            "subq $8, %%rcx\n\t"
+            "jae 3b\n\t"
+            "cmpq $-8, %%rcx\n\t"
+            "je 2f\n\t"
+            "addq $4, %%rcx\n\t" // FIXME: This isn't really a loop. https://bugs.webkit.org/show_bug.cgi?id=182617
+            "4:\n\t"
+            "movl (%%rsi, %%rcx), %%eax\n\t"
+            "movl %%eax, (%%rdi, %%rcx)\n\t"
+            "subq $4, %%rcx\n\t"
+            "jae 4b\n\t"
+            "2:\n\t"
+            : "+D"(dst), "+S"(src), "+c"(count), "+a"(tmp)
+            :
+            : "memory"
+            );
+        return;
+    }
+    if (!(sizeof(T) % sizeof(uint16_t))) {
+        asm volatile (
+            "cmpq $200, %%rcx\n\t"
+            "jb 1f\n\t"
+            "shrq $1, %%rcx\n\t"
+            "rep movsw\n\t"
+            "jmp 2f\n\t"
+            "3:\n\t"
+            "movq (%%rsi, %%rcx), %%rax\n\t"
+            "movq %%rax, (%%rdi, %%rcx)\n\t"
+            "1:\n\t"
+            "subq $8, %%rcx\n\t"
+            "jae 3b\n\t"
+            "cmpq $-8, %%rcx\n\t"
+            "je 2f\n\t"
+            "addq $6, %%rcx\n\t"
+            "4:\n\t"
+            "movw (%%rsi, %%rcx), %%ax\n\t"
+            "movw %%ax, (%%rdi, %%rcx)\n\t"
+            "subq $2, %%rcx\n\t"
+            "jae 4b\n\t"
+            "2:\n\t"
+            : "+D"(dst), "+S"(src), "+c"(count), "+a"(tmp)
+            :
+            : "memory"
+            );
+        return;
+    }
+    asm volatile (
+        "cmpq $200, %%rcx\n\t"
+        "jb 1f\n\t"
+        "rep movsb\n\t"
+        "jmp 2f\n\t"
+        "3:\n\t"
+        "movq (%%rsi, %%rcx), %%rax\n\t"
+        "movq %%rax, (%%rdi, %%rcx)\n\t"
+        "1:\n\t"
+        "subq $8, %%rcx\n\t"
+        "jae 3b\n\t"
+        "cmpq $-8, %%rcx\n\t"
+        "je 2f\n\t"
+        "addq $7, %%rcx\n\t"
+        "4:\n\t"
+        "movb (%%rsi, %%rcx), %%al\n\t"
+        "movb %%al, (%%rdi, %%rcx)\n\t"
+        "subq $1, %%rcx\n\t"
+        "jae 4b\n\t"
+        "2:\n\t"
+        : "+D"(dst), "+S"(src), "+c"(count), "+a"(tmp)
+        :
+        : "memory"
+        );
+#else
+    memcpy(dst, src, length * sizeof(T));
+#endif
+}
+
+inline void fastCopyBytes(void* dst, const void* src, size_t bytes)
+{
+    fastCopy(static_cast<char*>(dst), static_cast<const char*>(src), bytes);
+}
+
+} // namespace WTF
+
+using WTF::fastCopy;
+using WTF::fastCopyBytes;
index 9a0f08b..f1b06ea 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2005, 2007, Google Inc. All rights reserved.
- * Copyright (C) 2005-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2005-2018 Apple Inc. All rights reserved.
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
@@ -31,6 +31,8 @@
 #include <limits>
 #include <string.h>
 #include <wtf/DataLog.h>
+#include <wtf/FastCopy.h>
+#include <wtf/FastZeroFill.h>
 
 #if OS(WINDOWS)
 #include <windows.h>
@@ -78,7 +80,7 @@ void fastSetMaxSingleAllocationSize(size_t size)
 void* fastZeroedMalloc(size_t n) 
 {
     void* result = fastMalloc(n);
-    memset(result, 0, n);
+    fastZeroFillBytes(result, n);
     return result;
 }
 
@@ -86,7 +88,7 @@ char* fastStrDup(const char* src)
 {
     size_t len = strlen(src) + 1;
     char* dup = static_cast<char*>(fastMalloc(len));
-    memcpy(dup, src, len);
+    fastCopy(dup, src, len);
     return dup;
 }
 
@@ -95,7 +97,7 @@ TryMallocReturnValue tryFastZeroedMalloc(size_t n)
     void* result;
     if (!tryFastMalloc(n).getValue(result))
         return 0;
-    memset(result, 0, n);
+    fastZeroFillBytes(result, n);
     return result;
 }
 
diff --git a/Source/WTF/wtf/FastZeroFill.h b/Source/WTF/wtf/FastZeroFill.h
new file mode 100644 (file)
index 0000000..56dad17
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#pragma once
+
+#include <wtf/StdLibExtras.h>
+
+namespace WTF {
+
+template<typename T>
+void fastZeroFill(T* dst, size_t length)
+{
+#if CPU(X86_64) && COMPILER(GCC_OR_CLANG)
+    uint64_t zero = 0;
+    size_t count = length * sizeof(T);
+    if (!(sizeof(T) % sizeof(uint64_t))) {
+        asm volatile (
+            "cmpq $200, %%rcx\n\t"
+            "jb 1f\n\t"
+            "shrq $3, %%rcx\n\t"
+            "rep stosq\n\t"
+            "jmp 2f\n\t"
+            "3:\n\t"
+            "movq %%rax, (%%rdi, %%rcx)\n\t"
+            "1:\n\t"
+            "subq $8, %%rcx\n\t"
+            "jae 3b\n\t"
+            "2:\n\t"
+            : "+D"(dst), "+c"(count)
+            : "a"(zero)
+            : "memory"
+            );
+        return;
+    }
+    if (!(sizeof(T) % sizeof(uint32_t))) {
+        asm volatile (
+            "cmpq $200, %%rcx\n\t"
+            "jb 1f\n\t"
+            "shrq $2, %%rcx\n\t"
+            "rep stosl\n\t"
+            "jmp 2f\n\t"
+            "3:\n\t"
+            "movq %%rax, (%%rdi, %%rcx)\n\t"
+            "1:\n\t"
+            "subq $8, %%rcx\n\t"
+            "jae 3b\n\t"
+            "cmpq $-8, %%rcx\n\t"
+            "je 2f\n\t"
+            "addq $4, %%rcx\n\t" // FIXME: This isn't really a loop. https://bugs.webkit.org/show_bug.cgi?id=182617
+            "4:\n\t"
+            "movl %%eax, (%%rdi, %%rcx)\n\t"
+            "subq $4, %%rcx\n\t"
+            "jae 4b\n\t"
+            "2:\n\t"
+            : "+D"(dst), "+c"(count)
+            : "a"(zero)
+            : "memory"
+            );
+        return;
+    }
+    if (!(sizeof(T) % sizeof(uint16_t))) {
+        asm volatile (
+            "cmpq $200, %%rcx\n\t"
+            "jb 1f\n\t"
+            "shrq $1, %%rcx\n\t"
+            "rep stosw\n\t"
+            "jmp 2f\n\t"
+            "3:\n\t"
+            "movq %%rax, (%%rdi, %%rcx)\n\t"
+            "1:\n\t"
+            "subq $8, %%rcx\n\t"
+            "jae 3b\n\t"
+            "cmpq $-8, %%rcx\n\t"
+            "je 2f\n\t"
+            "addq $6, %%rcx\n\t"
+            "4:\n\t"
+            "movw %%ax, (%%rdi, %%rcx)\n\t"
+            "subq $2, %%rcx\n\t"
+            "jae 4b\n\t"
+            "2:\n\t"
+            : "+D"(dst), "+c"(count)
+            : "a"(zero)
+            : "memory"
+            );
+        return;
+    }
+    asm volatile (
+        "cmpq $200, %%rcx\n\t"
+        "jb 1f\n\t"
+        "rep stosb\n\t"
+        "jmp 2f\n\t"
+        "3:\n\t"
+        "movq %%rax, (%%rdi, %%rcx)\n\t"
+        "1:\n\t"
+        "subq $8, %%rcx\n\t"
+        "jae 3b\n\t"
+        "cmpq $-8, %%rcx\n\t"
+        "je 2f\n\t"
+        "addq $7, %%rcx\n\t"
+        "4:\n\t"
+        "movb %%al, (%%rdi, %%rcx)\n\t"
+        "sub $1, %%rcx\n\t"
+        "jae 4b\n\t"
+        "2:\n\t"
+        : "+D"(dst), "+c"(count)
+        : "a"(zero)
+        : "memory"
+        );
+#else
+    memset(dst, 0, length * sizeof(T));
+#endif
+}
+
+inline void fastZeroFillBytes(void* dst, size_t bytes)
+{
+    fastZeroFill(static_cast<char*>(dst), bytes);
+}
+
+} // namespace WTF
+
+using WTF::fastZeroFill;
+using WTF::fastZeroFillBytes;
index 1acb265..ac97279 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2010-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -27,6 +27,7 @@
 #define OSAllocator_h
 
 #include <algorithm>
+#include <wtf/FastCopy.h>
 #include <wtf/VMTags.h>
 
 namespace WTF {
@@ -90,7 +91,7 @@ template<typename T>
 inline T* OSAllocator::reallocateCommitted(T* oldBase, size_t oldSize, size_t newSize, Usage usage, bool writable, bool executable)
 {
     void* newBase = reserveAndCommit(newSize, usage, writable, executable);
-    memcpy(newBase, oldBase, std::min(oldSize, newSize));
+    fastCopyBytes(newBase, oldBase, std::min(oldSize, newSize));
     decommitAndRelease(oldBase, oldSize);
     return static_cast<T*>(newBase);
 }
index 33fd5ab..cf6485d 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <stdarg.h>
 #include <stdio.h>
+#include <wtf/FastCopy.h>
 #include <wtf/FastMalloc.h>
 
 namespace WTF {
@@ -119,7 +120,7 @@ void StringPrintStream::increaseSize(size_t newSize)
     // fastRealloc will just do malloc+free anyway. Also, this simplifies the code since
     // we can't realloc the inline buffer.
     char* newBuffer = static_cast<char*>(fastMalloc(m_size));
-    memcpy(newBuffer, m_buffer, m_next + 1);
+    fastCopy(newBuffer, m_buffer, m_next + 1);
     if (m_buffer != m_inlineBuffer)
         fastFree(m_buffer);
     m_buffer = newBuffer;
index abcabd0..f458cc8 100644 (file)
@@ -27,7 +27,9 @@
 #include <type_traits>
 #include <utility>
 #include <wtf/CheckedArithmetic.h>
+#include <wtf/FastCopy.h>
 #include <wtf/FastMalloc.h>
+#include <wtf/FastZeroFill.h>
 #include <wtf/Forward.h>
 #include <wtf/MallocPtr.h>
 #include <wtf/MathExtras.h>
@@ -86,7 +88,7 @@ struct VectorInitializer<true, true, T>
 {
     static void initialize(T* begin, T* end) 
     {
-        memset(begin, 0, reinterpret_cast<char*>(end) - reinterpret_cast<char*>(begin));
+        fastZeroFill(begin, end - begin);
     }
 };
 
@@ -126,7 +128,7 @@ struct VectorMover<true, T>
 {
     static void move(const T* src, const T* srcEnd, T* dst) 
     {
-        memcpy(dst, src, reinterpret_cast<const char*>(srcEnd) - reinterpret_cast<const char*>(src));
+        fastCopy(dst, src, srcEnd - src);
     }
     static void moveOverlapping(const T* src, const T* srcEnd, T* dst) 
     {
index 92dd50e..ac05633 100644 (file)
@@ -52,7 +52,7 @@ bool Decoder::decodeFixedLengthData(uint8_t* data, size_t size)
     if (!bufferIsLargeEnoughToContain(size))
         return false;
 
-    memcpy(data, m_bufferPosition, size);
+    fastCopy(data, m_bufferPosition, size);
     m_bufferPosition += size;
 
     Encoder::updateChecksumForData(m_sha1, data, size);
index 6636eb6..4af4291 100644 (file)
@@ -58,7 +58,7 @@ void Encoder::encodeFixedLengthData(const uint8_t* data, size_t size)
     updateChecksumForData(m_sha1, data, size);
 
     uint8_t* buffer = grow(size);
-    memcpy(buffer, data, size);
+    fastCopy(buffer, data, size);
 }
 
 template<typename Type>
index 181b110..ab6901e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2003-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2003-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -28,6 +28,7 @@
 #include "CString.h"
 
 #include <string.h>
+#include <wtf/FastCopy.h>
 #include <wtf/text/StringHasher.h>
 #include <wtf/text/StringMalloc.h>
 
@@ -66,7 +67,7 @@ void CString::init(const char* str, size_t length)
     ASSERT(str);
 
     m_buffer = CStringBuffer::createUninitialized(length);
-    memcpy(m_buffer->mutableData(), str, length); 
+    fastCopy(m_buffer->mutableData(), str, length); 
     m_buffer->mutableData()[length] = '\0';
 }
 
@@ -96,7 +97,7 @@ void CString::copyBufferIfNeeded()
     RefPtr<CStringBuffer> buffer = WTFMove(m_buffer);
     size_t length = buffer->length();
     m_buffer = CStringBuffer::createUninitialized(length);
-    memcpy(m_buffer->mutableData(), buffer->data(), length + 1);
+    fastCopy(m_buffer->mutableData(), buffer->data(), length + 1);
 }
 
 bool CString::isSafeToSendToAnotherThread() const
index 8e89c31..17f1dd2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2011-2018 Apple Inc. All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -26,6 +26,8 @@
 #pragma once
 
 #include <unicode/uloc.h>
+#include <wtf/FastCopy.h>
+#include <wtf/FastZeroFill.h>
 #include <wtf/HashMap.h>
 #include <wtf/NeverDestroyed.h>
 #include <wtf/ThreadSpecific.h>
@@ -51,7 +53,7 @@ public:
         if (!utf8Locale.length())
             return locale;
         Vector<char> scratchBuffer(utf8Locale.length() + 11, 0);
-        memcpy(scratchBuffer.data(), utf8Locale.data(), utf8Locale.length());
+        fastCopy(scratchBuffer.data(), utf8Locale.data(), utf8Locale.length());
 
         const char* keywordValue = nullptr;
         switch (mode) {
@@ -75,7 +77,7 @@ public:
             return AtomicString::fromUTF8(scratchBuffer.data(), lengthNeeded);
         if (status == U_BUFFER_OVERFLOW_ERROR) {
             scratchBuffer.grow(lengthNeeded + 1);
-            memset(scratchBuffer.data() + utf8Locale.length(), 0, scratchBuffer.size() - utf8Locale.length());
+            fastZeroFill(scratchBuffer.data() + utf8Locale.length(), scratchBuffer.size() - utf8Locale.length());
             status = U_ZERO_ERROR;
             int32_t lengthNeeded2 = uloc_setKeywordValue("lb", keywordValue, scratchBuffer.data(), scratchBuffer.size(), &status);
             if (!U_SUCCESS(status) || lengthNeeded != lengthNeeded2)
index 85b0c21..4aa9007 100644 (file)
@@ -99,7 +99,7 @@ void StringBuilder::allocateBuffer(const LChar* currentCharacters, unsigned requ
     ASSERT(m_is8Bit);
     // Copy the existing data into a new buffer, set result to point to the end of the existing data.
     auto buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters8);
-    memcpy(m_bufferCharacters8, currentCharacters, static_cast<size_t>(m_length) * sizeof(LChar)); // This can't overflow.
+    fastCopy(m_bufferCharacters8, currentCharacters, m_length);
     
     // Update the builder state.
     m_buffer = WTFMove(buffer);
@@ -114,7 +114,7 @@ void StringBuilder::allocateBuffer(const UChar* currentCharacters, unsigned requ
     ASSERT(!m_is8Bit);
     // Copy the existing data into a new buffer, set result to point to the end of the existing data.
     auto buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters16);
-    memcpy(m_bufferCharacters16, currentCharacters, static_cast<size_t>(m_length) * sizeof(UChar)); // This can't overflow.
+    fastCopy(m_bufferCharacters16, currentCharacters, m_length);
     
     // Update the builder state.
     m_buffer = WTFMove(buffer);
@@ -276,10 +276,10 @@ void StringBuilder::append(const UChar* characters, unsigned length)
             allocateBufferUpConvert(m_string.isNull() ? 0 : m_string.characters8(), expandedCapacity(capacity(), requiredLength));
         }
 
-        memcpy(m_bufferCharacters16 + m_length, characters, static_cast<size_t>(length) * sizeof(UChar));
+        fastCopy(m_bufferCharacters16 + m_length, characters, length);
         m_length = requiredLength;
     } else
-        memcpy(appendUninitialized<UChar>(length), characters, static_cast<size_t>(length) * sizeof(UChar));
+        fastCopy(appendUninitialized<UChar>(length), characters, length);
     ASSERT(m_buffer->length() >= m_length);
 }
 
@@ -291,13 +291,7 @@ void StringBuilder::append(const LChar* characters, unsigned length)
 
     if (m_is8Bit) {
         LChar* dest = appendUninitialized<LChar>(length);
-        if (length > 8)
-            memcpy(dest, characters, static_cast<size_t>(length) * sizeof(LChar));
-        else {
-            const LChar* end = characters + length;
-            while (characters < end)
-                *(dest++) = *(characters++);
-        }
+        fastCopy(dest, characters, length);
     } else {
         UChar* dest = appendUninitialized<UChar>(length);
         const LChar* end = characters + length;
index 18ff3f8..472c08c 100644 (file)
@@ -27,6 +27,7 @@
 #define StringConcatenate_h
 
 #include <string.h>
+#include <wtf/FastCopy.h>
 
 #ifndef AtomicString_h
 #include <wtf/text/AtomicString.h>
@@ -157,7 +158,7 @@ public:
 
     void writeTo(UChar* destination) const
     {
-        memcpy(destination, m_characters, m_length * sizeof(UChar));
+        fastCopy(destination, m_characters, m_length);
     }
 
     String toString() const { return String(m_characters, m_length); }
index 7029f19..c3d2a5a 100644 (file)
@@ -1066,7 +1066,7 @@ template<typename CharacterType> inline void StringImpl::copyCharacters(Characte
         *destination = *source;
         return;
     }
-    memcpy(destination, source, numCharacters * sizeof(CharacterType));
+    fastCopy(destination, source, numCharacters);
 }
 
 ALWAYS_INLINE void StringImpl::copyCharacters(UChar* destination, const LChar* source, unsigned numCharacters)
index 7388fdb..dc1a2ce 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <algorithm>
 #include <string.h>
+#include <wtf/FastCopy.h>
 
 namespace WTF {
 
@@ -55,10 +56,10 @@ UText* uTextCloneImpl(UText* destination, const UText* source, UBool deep, UErro
     void* extraNew = destination->pExtra;
     int32_t flags = destination->flags;
     int sizeToCopy = std::min(source->sizeOfStruct, destination->sizeOfStruct);
-    memcpy(destination, source, sizeToCopy);
+    fastCopyBytes(destination, source, sizeToCopy);
     destination->pExtra = extraNew;
     destination->flags = flags;
-    memcpy(destination->pExtra, source->pExtra, extraSize);
+    fastCopyBytes(destination->pExtra, source->pExtra, extraSize);
     fixPointer(source, destination, destination->context);
     fixPointer(source, destination, destination->p);
     fixPointer(source, destination, destination->q);
index b8109d7..9921c45 100644 (file)
@@ -27,6 +27,7 @@
 #include "UTextProviderLatin1.h"
 
 #include "UTextProvider.h"
+#include <wtf/FastZeroFill.h>
 #include <wtf/text/StringImpl.h>
 
 namespace WTF {
@@ -82,7 +83,7 @@ static UText* uTextLatin1Clone(UText* destination, const UText* source, UBool de
     result->a = source->a;
     result->pFuncs = &uTextLatin1Funcs;
     result->chunkContents = (UChar*)result->pExtra;
-    memset(const_cast<UChar*>(result->chunkContents), 0, sizeof(UChar) * UTextWithBufferInlineCapacity);
+    fastZeroFill(const_cast<UChar*>(result->chunkContents), UTextWithBufferInlineCapacity);
 
     return result;
 }
@@ -228,7 +229,7 @@ UText* openLatin1UTextProvider(UTextWithBuffer* utWithBuffer, const LChar* strin
     text->a = length;
     text->pFuncs = &uTextLatin1Funcs;
     text->chunkContents = (UChar*)text->pExtra;
-    memset(const_cast<UChar*>(text->chunkContents), 0, sizeof(UChar) * UTextWithBufferInlineCapacity);
+    fastZeroFill(const_cast<UChar*>(text->chunkContents), UTextWithBufferInlineCapacity);
 
     return text;
 }
index fc7205b..b9b15ff 100644 (file)
@@ -172,7 +172,7 @@ kern_return_t catch_mach_exception_raise_state(
     Signal signal = fromMachException(exceptionType);
     RELEASE_ASSERT(signal != Signal::Unknown);
 
-    memcpy(outState, inState, inStateCount * sizeof(inState[0]));
+    fastCopy(outState, inState, inStateCount);
     *outStateCount = inStateCount;
 
 #if CPU(X86_64)
index 41cff62..eeb7bfd 100644 (file)
@@ -1,3 +1,28 @@
+2018-02-08  Filip Pizlo  <fpizlo@apple.com>
+
+        Experiment with alternative implementation of memcpy/memset
+        https://bugs.webkit.org/show_bug.cgi?id=182563
+
+        Reviewed by Michael Saboff and Mark Lam.
+        
+        Add a faster x86_64-specific implementation of memcpy and memset. Ideally, this would just be
+        implemented in WTF, but we have to copy it into bmalloc since bmalloc sits below WTF on the
+        stack.
+
+        * bmalloc/Algorithm.h:
+        (bmalloc::fastCopy):
+        (bmalloc::fastZeroFill):
+        * bmalloc/Allocator.cpp:
+        (bmalloc::Allocator::reallocate):
+        * bmalloc/Bits.h:
+        (bmalloc::BitsWordOwner::operator=):
+        (bmalloc::BitsWordOwner::clearAll):
+        (bmalloc::BitsWordOwner::set):
+        * bmalloc/IsoPageInlines.h:
+        (bmalloc::IsoPage<Config>::IsoPage):
+        * bmalloc/Vector.h:
+        (bmalloc::Vector<T>::reallocateBuffer):
+
 2018-02-05  JF Bastien  <jfbastien@apple.com>
 
         Gigacage: enable only for WebContent process and token executables
index d35fce8..f8d363e 100644 (file)
@@ -180,6 +180,221 @@ bool findBitInWord(T word, size_t& index, size_t endIndex, bool value)
     return false;
 }
 
+template<typename T>
+void fastCopy(T* dst, T* src, size_t length)
+{
+#if BCPU(X86_64)
+    uint64_t tmp = 0;
+    size_t count = length * sizeof(T);
+    if (!(sizeof(T) % sizeof(uint64_t))) {
+        asm volatile (
+            "cmpq $200, %%rcx\n\t"
+            "jb 1f\n\t"
+            "shrq $3, %%rcx\n\t"
+            "rep movsq\n\t"
+            "jmp 2f\n\t"
+            "3:\n\t"
+            "movq (%%rsi, %%rcx), %%rax\n\t"
+            "movq %%rax, (%%rdi, %%rcx)\n\t"
+            "1:\n\t"
+            "subq $8, %%rcx\n\t"
+            "jae 3b\n\t"
+            "2:\n\t"
+            : "+D"(dst), "+S"(src), "+c"(count), "+a"(tmp)
+            :
+            : "memory"
+            );
+        return;
+    }
+    if (!(sizeof(T) % sizeof(uint32_t))) {
+        asm volatile (
+            "cmpq $200, %%rcx\n\t"
+            "jb 1f\n\t"
+            "shrq $2, %%rcx\n\t"
+            "rep movsl\n\t"
+            "jmp 2f\n\t"
+            "3:\n\t"
+            "movq (%%rsi, %%rcx), %%rax\n\t"
+            "movq %%rax, (%%rdi, %%rcx)\n\t"
+            "1:\n\t"
+            "subq $8, %%rcx\n\t"
+            "jae 3b\n\t"
+            "cmpq $-8, %%rcx\n\t"
+            "je 2f\n\t"
+            "addq $4, %%rcx\n\t" // FIXME: This isn't really a loop. https://bugs.webkit.org/show_bug.cgi?id=182617
+            "4:\n\t"
+            "movl (%%rsi, %%rcx), %%eax\n\t"
+            "movl %%eax, (%%rdi, %%rcx)\n\t"
+            "subq $4, %%rcx\n\t"
+            "jae 4b\n\t"
+            "2:\n\t"
+            : "+D"(dst), "+S"(src), "+c"(count), "+a"(tmp)
+            :
+            : "memory"
+            );
+        return;
+    }
+    if (!(sizeof(T) % sizeof(uint16_t))) {
+        asm volatile (
+            "cmpq $200, %%rcx\n\t"
+            "jb 1f\n\t"
+            "shrq $1, %%rcx\n\t"
+            "rep movsw\n\t"
+            "jmp 2f\n\t"
+            "3:\n\t"
+            "movq (%%rsi, %%rcx), %%rax\n\t"
+            "movq %%rax, (%%rdi, %%rcx)\n\t"
+            "1:\n\t"
+            "subq $8, %%rcx\n\t"
+            "jae 3b\n\t"
+            "cmpq $-8, %%rcx\n\t"
+            "je 2f\n\t"
+            "addq $6, %%rcx\n\t"
+            "4:\n\t"
+            "movw (%%rsi, %%rcx), %%ax\n\t"
+            "movw %%ax, (%%rdi, %%rcx)\n\t"
+            "subq $2, %%rcx\n\t"
+            "jae 4b\n\t"
+            "2:\n\t"
+            : "+D"(dst), "+S"(src), "+c"(count), "+a"(tmp)
+            :
+            : "memory"
+            );
+        return;
+    }
+    asm volatile (
+        "cmpq $200, %%rcx\n\t"
+        "jb 1f\n\t"
+        "rep movsb\n\t"
+        "jmp 2f\n\t"
+        "3:\n\t"
+        "movq (%%rsi, %%rcx), %%rax\n\t"
+        "movq %%rax, (%%rdi, %%rcx)\n\t"
+        "1:\n\t"
+        "subq $8, %%rcx\n\t"
+        "jae 3b\n\t"
+        "cmpq $-8, %%rcx\n\t"
+        "je 2f\n\t"
+        "addq $7, %%rcx\n\t"
+        "4:\n\t"
+        "movb (%%rsi, %%rcx), %%al\n\t"
+        "movb %%al, (%%rdi, %%rcx)\n\t"
+        "subq $1, %%rcx\n\t"
+        "jae 4b\n\t"
+        "2:\n\t"
+        : "+D"(dst), "+S"(src), "+c"(count), "+a"(tmp)
+        :
+        : "memory"
+        );
+#else
+    memcpy(dst, src, length * sizeof(T));
+#endif
+}
+
+template<typename T>
+void fastZeroFill(T* dst, size_t length)
+{
+#if BCPU(X86_64)
+    uint64_t zero = 0;
+    size_t count = length * sizeof(T);
+    if (!(sizeof(T) % sizeof(uint64_t))) {
+        asm volatile (
+            "cmpq $200, %%rcx\n\t"
+            "jb 1f\n\t"
+            "shrq $3, %%rcx\n\t"
+            "rep stosq\n\t"
+            "jmp 2f\n\t"
+            "3:\n\t"
+            "movq %%rax, (%%rdi, %%rcx)\n\t"
+            "1:\n\t"
+            "subq $8, %%rcx\n\t"
+            "jae 3b\n\t"
+            "2:\n\t"
+            : "+D"(dst), "+c"(count)
+            : "a"(zero)
+            : "memory"
+            );
+        return;
+    }
+    if (!(sizeof(T) % sizeof(uint32_t))) {
+        asm volatile (
+            "cmpq $200, %%rcx\n\t"
+            "jb 1f\n\t"
+            "shrq $2, %%rcx\n\t"
+            "rep stosl\n\t"
+            "jmp 2f\n\t"
+            "3:\n\t"
+            "movq %%rax, (%%rdi, %%rcx)\n\t"
+            "1:\n\t"
+            "subq $8, %%rcx\n\t"
+            "jae 3b\n\t"
+            "cmpq $-8, %%rcx\n\t"
+            "je 2f\n\t"
+            "addq $4, %%rcx\n\t" // FIXME: This isn't really a loop. https://bugs.webkit.org/show_bug.cgi?id=182617
+            "4:\n\t"
+            "movl %%eax, (%%rdi, %%rcx)\n\t"
+            "subq $4, %%rcx\n\t"
+            "jae 4b\n\t"
+            "2:\n\t"
+            : "+D"(dst), "+c"(count)
+            : "a"(zero)
+            : "memory"
+            );
+        return;
+    }
+    if (!(sizeof(T) % sizeof(uint16_t))) {
+        asm volatile (
+            "cmpq $200, %%rcx\n\t"
+            "jb 1f\n\t"
+            "shrq $1, %%rcx\n\t"
+            "rep stosw\n\t"
+            "jmp 2f\n\t"
+            "3:\n\t"
+            "movq %%rax, (%%rdi, %%rcx)\n\t"
+            "1:\n\t"
+            "subq $8, %%rcx\n\t"
+            "jae 3b\n\t"
+            "cmpq $-8, %%rcx\n\t"
+            "je 2f\n\t"
+            "addq $6, %%rcx\n\t"
+            "4:\n\t"
+            "movw %%ax, (%%rdi, %%rcx)\n\t"
+            "subq $2, %%rcx\n\t"
+            "jae 4b\n\t"
+            "2:\n\t"
+            : "+D"(dst), "+c"(count)
+            : "a"(zero)
+            : "memory"
+            );
+        return;
+    }
+    asm volatile (
+        "cmpq $200, %%rcx\n\t"
+        "jb 1f\n\t"
+        "rep stosb\n\t"
+        "jmp 2f\n\t"
+        "3:\n\t"
+        "movq %%rax, (%%rdi, %%rcx)\n\t"
+        "1:\n\t"
+        "subq $8, %%rcx\n\t"
+        "jae 3b\n\t"
+        "cmpq $-8, %%rcx\n\t"
+        "je 2f\n\t"
+        "addq $7, %%rcx\n\t"
+        "4:\n\t"
+        "movb %%al, (%%rdi, %%rcx)\n\t"
+        "sub $1, %%rcx\n\t"
+        "jae 4b\n\t"
+        "2:\n\t"
+        : "+D"(dst), "+c"(count)
+        : "a"(zero)
+        : "memory"
+        );
+#else
+    memset(dst, 0, length * sizeof(T));
+#endif
+}
+
 } // namespace bmalloc
 
 #endif // Algorithm_h
index b442def..d844977 100644 (file)
@@ -125,7 +125,7 @@ void* Allocator::reallocate(void* object, size_t newSize)
 
     void* result = allocate(newSize);
     size_t copySize = std::min(oldSize, newSize);
-    memcpy(result, object, copySize);
+    fastCopy(static_cast<char*>(result), static_cast<char*>(object), copySize);
     m_deallocator.deallocate(object);
     return result;
 }
index 8bdd344..a808ddf 100644 (file)
@@ -80,7 +80,7 @@ public:
     
     BitsWordOwner& operator=(const BitsWordOwner& other)
     {
-        memcpy(m_words, other.m_words, arrayLength() * sizeof(uint32_t));
+        fastCopy(m_words, other.m_words, arrayLength());
         return *this;
     }
     
@@ -91,12 +91,12 @@ public:
     
     void clearAll()
     {
-        memset(m_words, 0, arrayLength() * sizeof(uint32_t));
+        fastZeroFill(m_words, arrayLength());
     }
     
     void set(const BitsWordOwner& other)
     {
-        memcpy(m_words, other.m_words, arrayLength() * sizeof(uint32_t));
+        fastCopy(m_words, other.m_words, arrayLength());
     }
     
     size_t numBits() const
index 0c47864..1b76ba5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2017-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -47,7 +47,7 @@ IsoPage<Config>::IsoPage(IsoDirectoryBase<Config>& directory, unsigned index)
     : m_directory(directory)
     , m_index(index)
 {
-    memset(m_allocBits, 0, sizeof(m_allocBits));
+    fastZeroFill(m_allocBits, bitsArrayLength(numObjects));
 }
 
 template<typename Config>
index 9d87655..04df5e2 100644 (file)
@@ -203,7 +203,7 @@ void Vector<T>::reallocateBuffer(size_t newCapacity)
     size_t vmSize = bmalloc::vmSize(newCapacity * sizeof(T));
     T* newBuffer = vmSize ? static_cast<T*>(vmAllocate(vmSize)) : nullptr;
     if (m_buffer) {
-        std::memcpy(newBuffer, m_buffer, m_size * sizeof(T));
+        fastCopy(newBuffer, m_buffer, m_size);
         vmDeallocate(m_buffer, bmalloc::vmSize(m_capacity * sizeof(T)));
     }