Use intrinsics rather than inline assembly where possible
authorChristopher Dykes <cdykes@fb.com>
Thu, 9 Jun 2016 23:01:25 +0000 (16:01 -0700)
committerFacebook Github Bot 5 <facebook-github-bot-5-bot@fb.com>
Thu, 9 Jun 2016 23:08:23 +0000 (16:08 -0700)
Summary:
I would switch these to just use the intrinsic functions, but GCC 4.8 doesn't support them.
MSVC supports the intrinsics, which is the primary reason for the switch.

Reviewed By: philippv

Differential Revision: D3278901

fbshipit-source-id: 60103ac7cf7ddfb529f65f4aadc687dbdaf423a1

folly/Portability.h
folly/experimental/Instructions.h
folly/experimental/Select64.h

index 61c698469f3d305575d17ea6a56b40cfc689fb51..2d3ec417c3f32210679d692e3c7e7d35871b93a2 100644 (file)
@@ -92,6 +92,13 @@ constexpr bool kHasUnalignedAccess = false;
 # define FOLLY_ALWAYS_INLINE inline
 #endif
 
+// target
+#ifdef _MSC_VER
+# define FOLLY_TARGET_ATTRIBUTE(target)
+#else
+# define FOLLY_TARGET_ATTRIBUTE(target) __attribute__((__target__(target)))
+#endif
+
 // detection for 64 bit
 #if defined(__x86_64__) || defined(_M_X64)
 # define FOLLY_X64 1
index 97386ff16b5311cbddd7c9b48bd9cee95b45702c..97e6e78c1efa3d6662e7456c9530713e61270cd5 100644 (file)
 #pragma once
 
 #include <glog/logging.h>
+#include <immintrin.h>
+#ifdef __clang__
+// Clang defines the intrinsics in weird places.
+#include <popcntintrin.h>
+#endif
 
 #include <folly/CpuId.h>
+#include <folly/portability/Builtins.h>
 
 namespace folly { namespace compression { namespace instructions {
 
@@ -52,11 +58,18 @@ struct Nehalem : public Default {
   static bool supported(const folly::CpuId& cpuId = {}) {
     return cpuId.popcnt();
   }
+
+  FOLLY_TARGET_ATTRIBUTE("popcnt")
   static inline uint64_t popcount(uint64_t value) {
     // POPCNT is supported starting with Intel Nehalem, AMD K10.
+#if defined(__GNUC__) && !defined(__clang__) && !__GNUC_PREREQ(4, 9)
+    // GCC 4.8 doesn't support the intrinsics.
     uint64_t result;
     asm ("popcntq %1, %0" : "=r" (result) : "r" (value));
     return result;
+#else
+    return _mm_popcnt_u64(value);
+#endif
   }
 };
 
@@ -64,12 +77,19 @@ struct Haswell : public Nehalem {
   static bool supported(const folly::CpuId& cpuId = {}) {
     return Nehalem::supported(cpuId) && cpuId.bmi1();
   }
+
+  FOLLY_TARGET_ATTRIBUTE("bmi")
   static inline uint64_t blsr(uint64_t value) {
     // BMI1 is supported starting with Intel Haswell, AMD Piledriver.
     // BLSR combines two instuctions into one and reduces register pressure.
+#if defined(__GNUC__) && !defined(__clang__) && !__GNUC_PREREQ(4, 9)
+    // GCC 4.8 doesn't support the intrinsics.
     uint64_t result;
     asm ("blsrq %1, %0" : "=r" (result) : "r" (value));
     return result;
+#else
+    return _blsr_u64(value);
+#endif
   }
 };
 
index 9c2e03f90aebc788e9cb992f36825092d8574d63..039cfa85e197f11947f94b8ab21a94cc60329ca5 100644 (file)
@@ -63,8 +63,11 @@ inline uint64_t select64(uint64_t x, uint64_t k) {
 }
 
 template <>
+FOLLY_TARGET_ATTRIBUTE("bmi,bmi2")
 inline uint64_t select64<compression::instructions::Haswell>(uint64_t x,
                                                              uint64_t k) {
+#if defined(__GNUC__) && !defined(__clang__) && !__GNUC_PREREQ(4, 9)
+  // GCC 4.8 doesn't support the intrinsics.
   uint64_t result = uint64_t(1) << k;
 
   asm("pdep %1, %0, %0\n\t"
@@ -73,6 +76,9 @@ inline uint64_t select64<compression::instructions::Haswell>(uint64_t x,
       : "r"(x));
 
   return result;
+#else
+  return _tzcnt_u64(_pdep_u64(1ULL << k, x));
+#endif
 }
 
 } // namespace folly