Просмотр исходного кода

[1.7>master] [MERGE #3501 @obastemur] Perf Improvements

Merge pull request #3501 from obastemur:imp_acm_v2

AcmeAIR LTO gain ~3%

- jsrt-internals: use known length JsGetProperty
- numberToString: Use faster implementation
Previously, we had twice faster numberToString implementation is introduced. Use it on remaining places.
- jitFuncMaps: Use initial capacity and update size policy
In dictionary collision drops up to 10 times.
- Do not lookup for empty string record
Empty string propertyRecord shows up very often on benchmarks
- Update hashing algorithm to FNV-1a
Lower the amount of collision in dictionaries and make hash code data
type consistent.
Oguz Bastemur 8 лет назад
Родитель
Сommit
4efbffbc2d

+ 2 - 2
lib/Common/DataStructures/BaseDictionary.h

@@ -766,7 +766,7 @@ namespace JsUtil
             return SizePolicy::GetBucket(UNTAGHASH(hashCode), bucketCount, modFunctionIndex);
         }
 
-        uint GetBucket(uint hashCode) const
+        uint GetBucket(hash_t hashCode) const
         {
             return GetBucket(hashCode, this->bucketCount, modFunctionIndex);
         }
@@ -845,7 +845,7 @@ namespace JsUtil
             int * localBuckets = buckets;
             if (localBuckets != nullptr)
             {
-                uint hashCode = GetHashCodeWithKey<LookupType>(key);
+                hash_t hashCode = GetHashCodeWithKey<LookupType>(key);
                 *targetBucket = this->GetBucket(hashCode);
                 *last = -1;
                 EntryType * localEntries = entries;

+ 4 - 7
lib/Common/DataStructures/CharacterBuffer.h

@@ -33,7 +33,7 @@ namespace JsUtil
             return StaticGetHashCode(string, len);
         }
 
-        int FastHash() const
+        hash_t FastHash() const
         {
             Assert(string != nullptr);
             return InternalGetHashCode<true>(string, len);
@@ -49,18 +49,15 @@ namespace JsUtil
 
         static bool StaticEquals(__in_z T const * s1, __in_z T const* s2, __in charcount_t length);
 
-        static int StaticGetHashCode(__in_z T const * s, __in charcount_t length)
+        static hash_t StaticGetHashCode(__in_z T const * s, __in charcount_t length)
         {
             return InternalGetHashCode<false>(s, length);
         }
 
-        // This must be identical to Trident's getHash function in fastDOMCompiler.pl
         template <bool fastHash>
-        static int InternalGetHashCode(__in_z T const * s, __in charcount_t length)
+        static hash_t InternalGetHashCode(__in_z T const * s, __in charcount_t length)
         {
-            // TODO: This hash performs poorly on small strings, consider finding a better hash function
-            // now that some type handlers hash by string instead of PropertyId.
-            int hash = 0;
+            hash_t hash = CC_HASH_OFFSET_VALUE;
             charcount_t hashLength = length;
             if (fastHash)
             {

+ 13 - 3
lib/Common/DataStructures/Comparer.h

@@ -114,8 +114,18 @@ struct RecyclerPointerComparer
     }
 };
 
+// FNV-1a hash -> https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
+// #define CC_HASH_OFFSET_VALUE 2166136261
+// #define CC_HASH_LOGIC(hash, byte) \
+//    hash ^= byte;                  \
+//    hash *= 16777619
+
+// previous hash function.
+// TODO: hash function below is bad for key distribution.
+//       FNV-1a above results better but expensive for lookups in small data sets.
+#define CC_HASH_OFFSET_VALUE 0
 #define CC_HASH_LOGIC(hash, byte) \
-    hash  = _rotl(hash, 7);        \
+    hash ^= _rotl(hash, 7);       \
     hash ^= byte;
 
 template <>
@@ -129,7 +139,7 @@ struct DefaultComparer<GUID>
      inline static hash_t GetHashCode(GUID const& guid)
      {
         char* p = (char*)&guid;
-        int hash = 0;
+        hash_t hash = CC_HASH_OFFSET_VALUE;
         for (int i = 0; i < sizeof(GUID); i++)
         {
             CC_HASH_LOGIC(hash, (uint32)(p[i]));
@@ -148,7 +158,7 @@ struct StringComparer
 
     inline static hash_t GetHashCode(T str)
     {
-        int hash = 0;
+        hash_t hash = CC_HASH_OFFSET_VALUE;
         while (*str)
         {
             CC_HASH_LOGIC(hash, *str);

+ 2 - 2
lib/Common/DataStructures/InternalStringNoCaseComparer.cpp

@@ -13,11 +13,11 @@ namespace JsUtil
         return (s1.GetLength() == s2.GetLength()) && (NoCaseComparer<JsUtil::CharacterBuffer<WCHAR>>::Compare(s1, s2)==0);
     }
 
-    uint NoCaseComparer<JsUtil::CharacterBuffer<WCHAR>>::GetHashCode(JsUtil::CharacterBuffer<WCHAR> const& s1)
+    hash_t NoCaseComparer<JsUtil::CharacterBuffer<WCHAR>>::GetHashCode(JsUtil::CharacterBuffer<WCHAR> const& s1)
     {
         const char16* s = s1.GetBuffer();
         size_t length = s1.GetLength();
-        uint hash = 0;
+        hash_t hash = CC_HASH_OFFSET_VALUE;
         for (size_t i = 0; i < length; i++)
         {
             CC_HASH_LOGIC(hash, tolower(s[i]));

+ 1 - 1
lib/Common/Memory/RecyclerWeakReference.h

@@ -284,7 +284,7 @@ private:
         return nullptr;
     }
 
-    uint HashKeyToBucket(char* strongReference, int size)
+    hash_t HashKeyToBucket(char* strongReference, int size)
     {
         hash_t hashCode = DefaultComparer<char*>::GetHashCode(strongReference);
         return SizePolicy::GetBucket(hashCode, size, modFunctionIndex);

+ 7 - 4
lib/Jsrt/Jsrt.cpp

@@ -2817,15 +2817,13 @@ CHAKRA_API JsIsRuntimeExecutionDisabled(_In_ JsRuntimeHandle runtimeHandle, _Out
     return JsNoError;
 }
 
-CHAKRA_API JsGetPropertyIdFromName(_In_z_ const WCHAR *name, _Out_ JsPropertyIdRef *propertyId)
+inline JsErrorCode JsGetPropertyIdFromNameInternal(_In_z_ const WCHAR *name, size_t cPropertyNameLength, _Out_ JsPropertyIdRef *propertyId)
 {
     return ContextAPINoScriptWrapper_NoRecord([&](Js::ScriptContext * scriptContext) -> JsErrorCode {
         PARAM_NOT_NULL(name);
         PARAM_NOT_NULL(propertyId);
         *propertyId = nullptr;
 
-        size_t cPropertyNameLength = wcslen(name);
-
         if (cPropertyNameLength <= INT_MAX)
         {
             scriptContext->GetOrAddPropertyRecord(name, static_cast<int>(cPropertyNameLength), (Js::PropertyRecord const **)propertyId);
@@ -2839,6 +2837,11 @@ CHAKRA_API JsGetPropertyIdFromName(_In_z_ const WCHAR *name, _Out_ JsPropertyIdR
     });
 }
 
+CHAKRA_API JsGetPropertyIdFromName(_In_z_ const WCHAR *name, _Out_ JsPropertyIdRef *propertyId)
+{
+    return JsGetPropertyIdFromNameInternal(name, wcslen(name), propertyId);
+}
+
 CHAKRA_API JsGetPropertyIdFromSymbol(_In_ JsValueRef symbol, _Out_ JsPropertyIdRef *propertyId)
 {
     return ContextAPINoScriptWrapper([&](Js::ScriptContext * scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode {
@@ -4401,7 +4404,7 @@ CHAKRA_API JsCreatePropertyId(
         return JsErrorOutOfMemory;
     }
 
-    return JsGetPropertyIdFromName(wname, propertyId);
+    return JsGetPropertyIdFromNameInternal(wname, wname.Length(), propertyId);
 }
 
 CHAKRA_API JsCopyPropertyId(

+ 1 - 1
lib/Jsrt/JsrtSourceHolder.h

@@ -108,7 +108,7 @@ namespace Js
             return RecyclerNewFinalized(scriptContext->GetRecycler(), JsrtSourceHolder, this->scriptLoadCallback, this->scriptUnloadCallback, this->sourceContext);
         }
 
-        virtual int GetHashCode() override
+        virtual hash_t GetHashCode() override
         {
             LPCUTF8 source = GetSource(_u("Hash Code Calculation"));
             size_t byteLength = GetByteLength(_u("Hash Code Calculation"));

+ 1 - 1
lib/Runtime/Base/PropertyRecord.cpp

@@ -28,7 +28,7 @@ namespace Js
 
         WCHAR* target = (WCHAR*)((PropertyRecord*)this + 1);
         isNumeric = (isSymbol || length > 10 || length <= 0) ? false : true;
-        hash = 0;
+        hash = CC_HASH_OFFSET_VALUE;
 
         for (int i = 0; i < length; i++)
         {

+ 1 - 1
lib/Runtime/Base/PropertyRecord.h

@@ -34,7 +34,7 @@ namespace Js
         Field(PropertyId) pid;
         //Made this mutable so that we can set it for Built-In js property records when we are adding it.
         //If we try to set it when initializing; we get extra code added for each built in; and thus increasing the size of chakracore
-        mutable Field(uint) hash;
+        mutable Field(hash_t) hash;
         Field(bool) isNumeric;
         Field(bool) isBound;
         Field(bool) isSymbol;

+ 4 - 2
lib/Runtime/Base/ScriptContext.cpp

@@ -201,6 +201,8 @@ namespace Js
         , bailOutOffsetBytes(0)
 #endif
         , emptyStringPropertyId(Js::PropertyIds::_none)
+        , debugContext(nullptr)
+        , jitFuncRangeCache(nullptr)
     {
 #ifdef ENABLE_SCRIPT_DEBUGGING
        // This may allocate memory and cause exception, but it is ok, as we all we have done so far
@@ -6254,7 +6256,7 @@ void ScriptContext::RegisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertie
         {
             if (jitPageAddrToFuncRangeMap == nullptr)
             {
-                jitPageAddrToFuncRangeMap = HeapNew(JITPageAddrToFuncRangeMap, &HeapAllocator::Instance);
+                jitPageAddrToFuncRangeMap = HeapNew(JITPageAddrToFuncRangeMap, &HeapAllocator::Instance, 1027);
             }
 
             void * pageAddr = GetPageAddr(address);
@@ -6274,7 +6276,7 @@ void ScriptContext::RegisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertie
         {
             if (largeJitFuncToSizeMap == nullptr)
             {
-                largeJitFuncToSizeMap = HeapNew(LargeJITFuncAddrToSizeMap, &HeapAllocator::Instance);
+                largeJitFuncToSizeMap = HeapNew(LargeJITFuncAddrToSizeMap, &HeapAllocator::Instance, 1027);
             }
 
             uint byteCount = 0;

+ 3 - 9
lib/Runtime/Base/ScriptContext.h

@@ -392,8 +392,8 @@ namespace Js
     {
     public:
         typedef JsUtil::BaseDictionary<void *, uint, HeapAllocator> RangeMap;
-        typedef JsUtil::BaseDictionary<void *, RangeMap*, HeapAllocator> JITPageAddrToFuncRangeMap;
-        typedef JsUtil::BaseDictionary<void *, uint, HeapAllocator> LargeJITFuncAddrToSizeMap;
+        typedef JsUtil::BaseDictionary<void *, RangeMap*, HeapAllocator, PrimeSizePolicy> JITPageAddrToFuncRangeMap;
+        typedef JsUtil::BaseDictionary<void *, uint, HeapAllocator, PrimeSizePolicy> LargeJITFuncAddrToSizeMap;
 
     private:
         JITPageAddrToFuncRangeMap * jitPageAddrToFuncRangeMap;
@@ -1429,13 +1429,7 @@ private:
     public:
         Js::PropertyId GetEmptyStringPropertyId()
         {
-            if (emptyStringPropertyId == Js::PropertyIds::_none)
-            {
-                Js::PropertyRecord const * propertyRecord;
-                this->GetOrAddPropertyRecord(_u(""), 0, &propertyRecord);
-                emptyStringPropertyId = propertyRecord->GetPropertyId();
-            }
-            return emptyStringPropertyId;
+            return threadContext->GetEmptyStringPropertyId();
         }
 
         void FreeFunctionEntryPoint(Js::JavascriptMethod codeAddress, Js::JavascriptMethod thunkAddress);

+ 2 - 2
lib/Runtime/Base/SourceHolder.h

@@ -22,7 +22,7 @@ namespace Js
         virtual size_t GetByteLength(const char16* reasonString) = 0;
         virtual ISourceHolder* Clone(ScriptContext* scriptContext) = 0;
         virtual bool Equals(ISourceHolder* other) = 0;
-        virtual int GetHashCode() = 0;
+        virtual hash_t GetHashCode() = 0;
         virtual bool IsEmpty() = 0;
         virtual bool IsDeferrable() = 0;
     };
@@ -72,7 +72,7 @@ namespace Js
             return this->isEmpty;
         }
 
-        virtual int GetHashCode() override
+        virtual hash_t GetHashCode() override
         {
             Assert(byteLength < MAXUINT32);
             return JsUtil::CharacterBuffer<utf8char_t>::StaticGetHashCode(source, (charcount_t)byteLength);

+ 17 - 12
lib/Runtime/Base/ThreadContext.cpp

@@ -218,6 +218,7 @@ ThreadContext::ThreadContext(AllocationPolicyManager * allocationPolicyManager,
 #if ENABLE_JS_REENTRANCY_CHECK
     , noJsReentrancy(false)
 #endif
+    , emptyStringPropertyRecord(nullptr)
 {
     pendingProjectionContextCloseList = JsUtil::List<IProjectionContext*, ArenaAllocator>::New(GetThreadAlloc());
     hostScriptContextStack = Anew(GetThreadAlloc(), JsUtil::Stack<HostScriptContext*>, GetThreadAlloc());
@@ -919,19 +920,23 @@ ThreadContext::IsNumericProperty(Js::PropertyId propertyId)
 const Js::PropertyRecord *
 ThreadContext::FindPropertyRecord(const char16 * propertyName, int propertyNameLength)
 {
-    Js::PropertyRecord const * propertyRecord = nullptr;
-
-    if (IsDirectPropertyName(propertyName, propertyNameLength))
+    // IsDirectPropertyName == 1 char properties && GetEmptyStringPropertyRecord == 0 length
+    if (propertyNameLength < 2)
     {
-        propertyRecord = propertyNamesDirect[propertyName[0]];
-        Assert(propertyRecord == propertyMap->LookupWithKey(Js::HashedCharacterBuffer<char16>(propertyName, propertyNameLength)));
-    }
-    else
-    {
-        propertyRecord = propertyMap->LookupWithKey(Js::HashedCharacterBuffer<char16>(propertyName, propertyNameLength));
+        if (propertyNameLength == 0)
+        {
+            return this->GetEmptyStringPropertyRecord();
+        }
+
+        if (IsDirectPropertyName(propertyName, propertyNameLength))
+        {
+            Js::PropertyRecord const * propertyRecord = propertyNamesDirect[propertyName[0]];
+            Assert(propertyRecord == propertyMap->LookupWithKey(Js::HashedCharacterBuffer<char16>(propertyName, propertyNameLength)));
+            return propertyRecord;
+        }
     }
 
-    return propertyRecord;
+    return propertyMap->LookupWithKey(Js::HashedCharacterBuffer<char16>(propertyName, propertyNameLength));
 }
 
 Js::PropertyRecord const *
@@ -1092,7 +1097,7 @@ ThreadContext::AddPropertyRecordInternal(const Js::PropertyRecord * propertyReco
 #if DBG
     // Only Assert we can't find the property if we are not adding a symbol.
     // For a symbol, the propertyName is not used and may collide with something in the map already.
-    if (!propertyRecord->IsSymbol())
+    if (propertyNameLength > 0 && !propertyRecord->IsSymbol())
     {
         Assert(FindPropertyRecord(propertyName, propertyNameLength) == nullptr);
     }
@@ -1141,7 +1146,7 @@ ThreadContext::AddPropertyRecordInternal(const Js::PropertyRecord * propertyReco
 #if DBG
     // Only Assert we can find the property if we are not adding a symbol.
     // For a symbol, the propertyName is not used and we won't be able to look the pid up via name.
-    if (!propertyRecord->IsSymbol())
+    if (propertyNameLength && !propertyRecord->IsSymbol())
     {
         Assert(FindPropertyRecord(propertyName, propertyNameLength) == propertyRecord);
     }

+ 23 - 2
lib/Runtime/Base/ThreadContext.h

@@ -425,7 +425,28 @@ public:
 #endif
 #endif
 
+public:
+    Js::PropertyRecord const * GetEmptyStringPropertyRecord()
+    {
+        if (!emptyStringPropertyRecord)
+        {
+            emptyStringPropertyRecord = propertyMap->LookupWithKey(Js::HashedCharacterBuffer<char16>(_u(""), 0));
+            if (emptyStringPropertyRecord == nullptr)
+            {
+                emptyStringPropertyRecord = this->UncheckedAddPropertyId(_u(""), 0, true);
+            }
+        }
+        return emptyStringPropertyRecord;
+    }
+
+    Js::PropertyId GetEmptyStringPropertyId()
+    {
+        return GetEmptyStringPropertyRecord()->GetPropertyId();
+    }
+
 private:
+    const Js::PropertyRecord * emptyStringPropertyRecord;
+
     Js::JavascriptExceptionObject * pendingFinallyException;
     bool noScriptScope;
 
@@ -811,7 +832,7 @@ private:
     bool isScriptActive;
 
     // When ETW rundown in background thread which needs to walk scriptContext/functionBody/entryPoint lists,
-    // or when JIT thread is getting auxPtrs from function body, we should not be modifying the list of 
+    // or when JIT thread is getting auxPtrs from function body, we should not be modifying the list of
     // functionBody/entrypoints, or expanding the auxPtrs
     CriticalSection csFunctionBody;
 
@@ -1795,7 +1816,7 @@ private:
 extern void(*InitializeAdditionalProperties)(ThreadContext *threadContext);
 
 // This is for protecting a region of code, where we can't recover and be consistent upon failures (mainly due to OOM and SO).
-// FailFast on that. 
+// FailFast on that.
 class AutoDisableInterrupt
 {
 public:

+ 1 - 1
lib/Runtime/Base/Utf8SourceInfo.h

@@ -272,7 +272,7 @@ namespace Js
 
         bool IsHostManagedSource() const;
 
-        static int StaticGetHashCode(__in const Utf8SourceInfo* const si)
+        static hash_t StaticGetHashCode(__in const Utf8SourceInfo* const si)
         {
             return si->GetSourceHolder()->GetHashCode();
         }

+ 2 - 2
lib/Runtime/Language/TaggedInt.cpp

@@ -434,7 +434,7 @@ LblDone:
     }
 
     // fills the buffer from the end and returns the start index
-    static int UnsignedToString(unsigned long value, char16 *buffer, int bufferSize)
+    int TaggedInt::UnsignedToString(unsigned __int64 value, char16 *buffer, int bufferSize)
     {
         static_assert(sizeof(unsigned long) <= 8, "This method may not support the target architecture");
         AssertMsg(bufferSize >= 22, "Error: bufferSize is too small. value may not be represented properly");
@@ -469,7 +469,7 @@ LblDone:
         return pos + 1;
     }
 
-    static int SignedToString(long value, char16 *buffer, int bufferSize)
+    int TaggedInt::SignedToString(__int64 value, char16 *buffer, int bufferSize)
     {
         bool neg = value < 0;
         unsigned long val = (unsigned long) (neg ? -1 * value : value);

+ 3 - 0
lib/Runtime/Language/TaggedInt.h

@@ -59,6 +59,9 @@ namespace Js {
         static JavascriptString* ToString(int value,ScriptContext* scriptContext);
         static JavascriptString* ToString(uint value,ScriptContext* scriptContext);
 
+        static int SignedToString(__int64 value, char16 *buffer, int bufferSize);
+        static int UnsignedToString(unsigned __int64 value, char16 *buffer, int bufferSize);
+
         static Var MinVal() { return ToVarUnchecked(k_nMinValue); }
         static Var MaxVal() { return ToVarUnchecked(k_nMaxValue); }
 

+ 3 - 4
lib/Runtime/Library/JavascriptProxy.cpp

@@ -1956,11 +1956,10 @@ namespace Js
 
     void JavascriptProxy::PropertyIdFromInt(uint32 index, PropertyRecord const** propertyRecord)
     {
-        char16 buffer[20];
+        char16 buffer[22];
+        int pos = TaggedInt::ToBuffer(index, buffer, _countof(buffer));
 
-        ::_i64tow_s(index, buffer, sizeof(buffer) / sizeof(char16), 10);
-
-        GetScriptContext()->GetOrAddPropertyRecord((LPCWSTR)buffer, static_cast<int>(wcslen(buffer)), propertyRecord);
+        GetScriptContext()->GetOrAddPropertyRecord((LPCWSTR)buffer + pos, (_countof(buffer) - 1) - pos, propertyRecord);
     }
 
     Var JavascriptProxy::GetName(ScriptContext* requestContext, PropertyId propertyId)

+ 2 - 2
lib/Runtime/Library/JavascriptString.h

@@ -366,7 +366,7 @@ namespace Js
                 JsUtil::CharacterBuffer<WCHAR>::StaticEquals(str1->GetString(), str2->GetBuffer(), str1->GetLength()));
         }
 
-        inline static uint GetHashCode(JavascriptString * str)
+        inline static hash_t GetHashCode(JavascriptString * str)
         {
             return JsUtil::CharacterBuffer<WCHAR>::StaticGetHashCode(str->GetString(), str->GetLength());
         }
@@ -394,7 +394,7 @@ struct DefaultComparer<Js::JavascriptString*>
         return Js::JavascriptString::Equals(x, y);
     }
 
-    inline static uint GetHashCode(Js::JavascriptString * pStr)
+    inline static hash_t GetHashCode(Js::JavascriptString * pStr)
     {
         return JsUtil::CharacterBuffer<char16>::StaticGetHashCode(pStr->GetString(), pStr->GetLength());
     }

+ 6 - 8
lib/Runtime/Library/JavascriptTypedNumber.cpp

@@ -33,21 +33,19 @@ namespace Js
     template <>
     JavascriptString* JavascriptTypedNumber<__int64>::ToString(Var value, ScriptContext* scriptContext)
     {
-        char16 szBuffer[30];
+        char16 szBuffer[22];
         __int64 val = JavascriptTypedNumber<__int64>::FromVar(value)->GetValue();
-        errno_t err = _i64tow_s(val, szBuffer, 30, 10);
-        AssertMsg(err == 0, "convert int64 to string failed");
-        return JavascriptString::NewCopySz(szBuffer, scriptContext);
+        int pos = TaggedInt::SignedToString(val, szBuffer, 22);
+        return JavascriptString::NewCopyBuffer(szBuffer + pos, (_countof(szBuffer) - 1) - pos, scriptContext);
     }
 
     template <>
     JavascriptString* JavascriptTypedNumber<unsigned __int64>::ToString(Var value, ScriptContext* scriptContext)
     {
-        char16 szBuffer[30];
+        char16 szBuffer[22];
         unsigned __int64 val = JavascriptUInt64Number::FromVar(value)->GetValue();
-        errno_t err = _ui64tow_s(val, szBuffer, 30, 10);
-        AssertMsg(err == 0, "convert int64 to string failed");
-        return JavascriptString::NewCopySz(szBuffer, scriptContext);
+        int pos = TaggedInt::UnsignedToString(val, szBuffer, 22);
+        return JavascriptString::NewCopyBuffer(szBuffer + pos, (_countof(szBuffer) - 1) - pos, scriptContext);
     }
 
     template <typename T>

+ 304 - 0
lib/wabt/built/config.h

@@ -0,0 +1,304 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WABT_CONFIG_H_
+#define WABT_CONFIG_H_
+
+/* TODO(binji): nice way to define these with WABT_ prefix? */
+
+/* Whether <alloca.h> is available */
+#define HAVE_ALLOCA_H 1
+
+/* Whether <unistd.h> is available */
+#define HAVE_UNISTD_H 1
+
+/* Whether snprintf is defined by stdio.h */
+#define HAVE_SNPRINTF 1
+
+/* Whether sysconf is defined by unistd.h */
+#define HAVE_SYSCONF 1
+
+/* Whether ssize_t is defined by stddef.h */
+#define HAVE_SSIZE_T 1
+
+/* Whether strcasecmp is defined by strings.h */
+#define HAVE_STRCASECMP 1
+
+/* Whether ENABLE_VIRTUAL_TERMINAL_PROCESSING is defined by windows.h */
+#define HAVE_WIN32_VT100 0
+
+#define COMPILER_IS_CLANG 1
+#define COMPILER_IS_GNU 0
+#define COMPILER_IS_MSVC 0
+
+#define WITH_EXCEPTIONS 0
+
+#define SIZEOF_SIZE_T 8
+#define SIZEOF_INT 4
+#define SIZEOF_LONG 8
+#define SIZEOF_LONG_LONG 8
+
+#if HAVE_ALLOCA_H
+#include <alloca.h>
+#elif COMPILER_IS_MSVC
+#include <malloc.h>
+#define alloca _alloca
+#elif defined(__MINGW32__)
+#include <malloc.h>
+#elif defined(__FreeBSD__)
+#include <stdlib.h>
+#else
+#error no alloca
+#endif
+
+#if COMPILER_IS_CLANG || COMPILER_IS_GNU
+
+#define WABT_UNUSED __attribute__ ((unused))
+#define WABT_WARN_UNUSED __attribute__ ((warn_unused_result))
+#define WABT_INLINE inline
+#define WABT_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#define WABT_LIKELY(x) __builtin_expect(!!(x), 1)
+
+#if __MINGW32__
+// mingw defaults to printf format specifier being ms_printf (which doesn't
+// understand 'llu', etc.) We always want gnu_printf, and force mingw to always
+// use mingw_printf, mingw_vprintf, etc.
+#define WABT_PRINTF_FORMAT(format_arg, first_arg) \
+  __attribute__((format(gnu_printf, (format_arg), (first_arg))))
+#else
+#define WABT_PRINTF_FORMAT(format_arg, first_arg) \
+  __attribute__((format(printf, (format_arg), (first_arg))))
+#endif
+
+#ifdef __cplusplus
+#if __cplusplus >= 201103L
+#define WABT_STATIC_ASSERT(x) static_assert((x), #x)
+#else
+#define WABT_STATIC_ASSERT__(x, c) \
+  static int static_assert_##c[(x ? 0 : -1)] WABT_UNUSED
+#define WABT_STATIC_ASSERT_(x, c) WABT_STATIC_ASSERT__(x, c)
+#define WABT_STATIC_ASSERT(x) WABT_STATIC_ASSERT_(x, __COUNTER__)
+#endif
+#else
+#define WABT_STATIC_ASSERT(x) _Static_assert((x), #x)
+#endif
+
+#if SIZEOF_INT == 4
+#define wabt_clz_u32(x) __builtin_clz(x)
+#define wabt_ctz_u32(x) __builtin_ctz(x)
+#define wabt_popcount_u32(x) __builtin_popcount(x)
+#elif SIZEOF_LONG == 4
+#define wabt_clz_u32(x) __builtin_clzl(x)
+#define wabt_ctz_u32(x) __builtin_ctzl(x)
+#define wabt_popcount_u32(x) __builtin_popcountl(x)
+#else
+#error "don't know how to define 32-bit builtins"
+#endif
+
+#if SIZEOF_LONG == 8
+#define wabt_clz_u64(x) __builtin_clzl(x)
+#define wabt_ctz_u64(x) __builtin_ctzl(x)
+#define wabt_popcount_u64(x) __builtin_popcountl(x)
+#elif SIZEOF_LONG_LONG == 8
+#define wabt_clz_u64(x) __builtin_clzll(x)
+#define wabt_ctz_u64(x) __builtin_ctzll(x)
+#define wabt_popcount_u64(x) __builtin_popcountll(x)
+#else
+#error "don't know how to define 64-bit builtins"
+#endif
+
+#define WABT_UNREACHABLE __builtin_unreachable()
+
+#elif COMPILER_IS_MSVC
+
+#include <cstring>
+#include <intrin.h>
+
+#define WABT_UNUSED
+#define WABT_WARN_UNUSED _Check_return_
+#define WABT_INLINE __inline
+#define WABT_STATIC_ASSERT(x) _STATIC_ASSERT(x)
+#define WABT_UNLIKELY(x) (x)
+#define WABT_LIKELY(x) (x)
+#define WABT_PRINTF_FORMAT(format_arg, first_arg)
+
+#define WABT_UNREACHABLE __assume(0)
+
+__inline unsigned long wabt_clz_u32(unsigned long mask) {
+  unsigned long index;
+  _BitScanReverse(&index, mask);
+  return sizeof(unsigned long) * 8 - (index + 1);
+}
+
+__inline unsigned long wabt_clz_u64(unsigned __int64 mask) {
+#if _M_X64
+  unsigned long index;
+  _BitScanReverse64(&index, mask);
+  return sizeof(unsigned __int64) * 8 - (index + 1);
+#elif _M_IX86
+  unsigned long index;
+  unsigned long high_mask;
+  memcpy(&high_mask, (unsigned char*)&mask + sizeof(unsigned long),
+         sizeof(unsigned long));
+  if (_BitScanReverse(&index, high_mask)) {
+    return sizeof(unsigned long) * 8 - (index + 1);
+  }
+
+  unsigned long low_mask;
+  memcpy(&low_mask, &mask, sizeof(unsigned long));
+  _BitScanReverse(&index, low_mask);
+  return sizeof(unsigned __int64) * 8 - (index + 1);
+#else
+#error unexpected architecture
+#endif
+}
+
+__inline unsigned long wabt_ctz_u32(unsigned long mask) {
+    unsigned long index;
+    _BitScanForward(&index, mask);
+    return index;
+}
+
+__inline unsigned long wabt_ctz_u64(unsigned __int64 mask) {
+#if _M_X64
+    unsigned long index;
+    _BitScanForward64(&index, mask);
+    return index;
+#elif _M_IX86
+    unsigned long low_mask = (unsigned long)mask;
+    if (low_mask) {
+        return wabt_ctz_u32(low_mask);
+    }
+    unsigned long high_mask;
+    memcpy(&high_mask, (unsigned char*)&mask + sizeof(unsigned long),
+           sizeof(unsigned long));
+    return sizeof(unsigned long) * 8 + wabt_ctz_u32(high_mask);
+#else
+#error unexpected architecture
+#endif
+}
+
+
+#define wabt_popcount_u32 __popcnt
+#if _M_X64
+#elif _M_IX86
+__inline unsigned __int64 __popcnt64(unsigned __int64 value) {
+    unsigned long high_value;
+    unsigned long low_value;
+    memcpy(&high_value, (unsigned char*)&value + sizeof(unsigned long),
+           sizeof(unsigned long));
+    memcpy(&low_value, &value, sizeof(unsigned long));
+    return wabt_popcount_u32(high_value) + wabt_popcount_u32(low_value);
+}
+#else
+#error unexpected architecture
+#endif
+#define wabt_popcount_u64 __popcnt64
+
+#else
+
+#error unknown compiler
+
+#endif
+
+
+#if COMPILER_IS_MSVC
+
+/* print format specifier for size_t */
+#if SIZEOF_SIZE_T == 4
+#define PRIzd "d"
+#define PRIzx "x"
+#elif SIZEOF_SIZE_T == 8
+#define PRIzd "I64d"
+#define PRIzx "I64x"
+#else
+#error "weird sizeof size_t"
+#endif
+
+#elif COMPILER_IS_CLANG || COMPILER_IS_GNU
+
+/* print format specifier for size_t */
+#define PRIzd "zd"
+#define PRIzx "zx"
+
+#else
+
+#error unknown compiler
+
+#endif
+
+
+#if HAVE_SNPRINTF
+#define wabt_snprintf snprintf
+#elif COMPILER_IS_MSVC
+/* can't just use _snprintf because it doesn't always null terminate */
+#include <cstdarg>
+int wabt_snprintf(char* str, size_t size, const char* format, ...);
+#else
+#error no snprintf
+#endif
+
+#if COMPILER_IS_MSVC
+/* can't just use vsnprintf because it doesn't always null terminate */
+int wabt_vsnprintf(char* str, size_t size, const char* format, va_list ap);
+#else
+#define wabt_vsnprintf vsnprintf
+#endif
+
+#if !HAVE_SSIZE_T
+typedef int ssize_t;
+#endif
+
+#if !HAVE_STRCASECMP
+#if COMPILER_IS_MSVC
+#define strcasecmp _stricmp
+#else
+#error no strcasecmp
+#endif
+#endif
+
+#if COMPILER_IS_MSVC && defined(_M_X64)
+// MSVC on x64 generates uint64 -> float conversions but doesn't do
+// round-to-nearest-ties-to-even, which is required by WebAssembly.
+#include <emmintrin.h>
+__inline double wabt_convert_uint64_to_double(unsigned __int64 x) {
+  __m128d result = _mm_setzero_pd();
+  if (x & 0x8000000000000000ULL) {
+    result = _mm_cvtsi64_sd(result, (x >> 1) | (x & 1));
+    result = _mm_add_sd(result, result);
+  } else {
+    result = _mm_cvtsi64_sd(result, x);
+  }
+  return _mm_cvtsd_f64(result);
+}
+
+__inline float wabt_convert_uint64_to_float(unsigned __int64 x) {
+  __m128 result = _mm_setzero_ps();
+  if (x & 0x8000000000000000ULL) {
+    result = _mm_cvtsi64_ss(result, (x >> 1) | (x & 1));
+    result = _mm_add_ss(result, result);
+  } else {
+    result = _mm_cvtsi64_ss(result, x);
+  }
+  return _mm_cvtss_f32(result);
+}
+
+#else
+#define wabt_convert_uint64_to_double(x) static_cast<double>(x)
+#define wabt_convert_uint64_to_float(x) static_cast<float>(x)
+#endif
+
+#endif /* WABT_CONFIG_H_ */