Resource Management 1: Math ops

Support 64-bit log2 and power-of-two alignment.

BUG=dawn:27

Change-Id: I2d254e5dda9626a6e26017b0d8e33f5db4c9298d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/9224
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Bryan Bernhart <bryan.bernhart@intel.com>
diff --git a/src/common/Math.cpp b/src/common/Math.cpp
index 4077bdb..e0de2ce 100644
--- a/src/common/Math.cpp
+++ b/src/common/Math.cpp
@@ -47,6 +47,26 @@
 #endif
 }
 
+uint32_t Log2(uint64_t value) {
+    ASSERT(value != 0);
+#if defined(DAWN_COMPILER_MSVC)
+    unsigned long firstBitIndex = 0ul;
+    unsigned char ret = _BitScanReverse64(&firstBitIndex, value);
+    ASSERT(ret != 0);
+    return firstBitIndex;
+#else
+    return 63 - static_cast<uint32_t>(__builtin_clzll(value));
+#endif
+}
+
+uint64_t NextPowerOfTwo(uint64_t x) {
+#if defined(DAWN_COMPILER_MSVC)
+    return x == 1 ? 1 : 1ull << (64 - __lzcnt64(x - 1));
+#else
+    return x == 1 ? 1 : 1ull << (64 - __builtin_clzll(x - 1));
+#endif
+}
+
 bool IsPowerOfTwo(size_t n) {
     ASSERT(n != 0);
     return (n & (n - 1)) == 0;
diff --git a/src/common/Math.h b/src/common/Math.h
index d52252e..777edba 100644
--- a/src/common/Math.h
+++ b/src/common/Math.h
@@ -25,8 +25,10 @@
 // The following are not valid for 0
 uint32_t ScanForward(uint32_t bits);
 uint32_t Log2(uint32_t value);
+uint32_t Log2(uint64_t value);
 bool IsPowerOfTwo(size_t n);
 
+uint64_t NextPowerOfTwo(uint64_t x);
 bool IsPtrAligned(const void* ptr, size_t alignment);
 void* AlignVoidPtr(void* ptr, size_t alignment);
 bool IsAligned(uint32_t value, size_t alignment);
diff --git a/src/tests/unittests/MathTests.cpp b/src/tests/unittests/MathTests.cpp
index 70f300f..7bcaf60 100644
--- a/src/tests/unittests/MathTests.cpp
+++ b/src/tests/unittests/MathTests.cpp
@@ -33,15 +33,18 @@
 // Tests for Log2
 TEST(Math, Log2) {
     // Test extrema
-    ASSERT_EQ(Log2(1), 0u);
-    ASSERT_EQ(Log2(0xFFFFFFFF), 31u);
+    ASSERT_EQ(Log2(1u), 0u);
+    ASSERT_EQ(Log2(0xFFFFFFFFu), 31u);
+    ASSERT_EQ(Log2(static_cast<uint64_t>(0xFFFFFFFFFFFFFFFF)), 63u);
 
     // Test boundary between two logs
-    ASSERT_EQ(Log2(0x80000000), 31u);
-    ASSERT_EQ(Log2(0x7FFFFFFF), 30u);
+    ASSERT_EQ(Log2(0x80000000u), 31u);
+    ASSERT_EQ(Log2(0x7FFFFFFFu), 30u);
+    ASSERT_EQ(Log2(static_cast<uint64_t>(0x8000000000000000)), 63u);
+    ASSERT_EQ(Log2(static_cast<uint64_t>(0x7FFFFFFFFFFFFFFF)), 62u);
 
-    ASSERT_EQ(Log2(16), 4u);
-    ASSERT_EQ(Log2(15), 3u);
+    ASSERT_EQ(Log2(16u), 4u);
+    ASSERT_EQ(Log2(15u), 3u);
 }
 
 // Tests for IsPowerOfTwo
@@ -54,6 +57,19 @@
     ASSERT_FALSE(IsPowerOfTwo(0x8000400));
 }
 
+// Tests for NextPowerOfTwo
+TEST(Math, NextPowerOfTwo) {
+    // Test extrema
+    ASSERT_EQ(NextPowerOfTwo(0), 1ull);
+    ASSERT_EQ(NextPowerOfTwo(0x7FFFFFFFFFFFFFFF), 0x8000000000000000);
+
+    // Test boundary between powers-of-two.
+    ASSERT_EQ(NextPowerOfTwo(31), 32ull);
+    ASSERT_EQ(NextPowerOfTwo(33), 64ull);
+
+    ASSERT_EQ(NextPowerOfTwo(32), 32ull);
+}
+
 // Tests for AlignPtr
 TEST(Math, AlignPtr) {
     constexpr size_t kTestAlignment = 8;