Commit 488fe98c authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot

RTM nits

Summary:
[Folly] RTM nits.

* No nested namespace.
* All status values supported.
* Tests included in cmake builds.
* API functions avoid `SIGILL` at runtime.

Reviewed By: nbronson

Differential Revision: D16636130

fbshipit-source-id: 9f9dc99b0826abbda295b069eb4823b3416af5d1
parent c20009d3
...@@ -752,6 +752,9 @@ if (BUILD_TESTS) ...@@ -752,6 +752,9 @@ if (BUILD_TESTS)
TEST rw_spin_lock_test SOURCES RWSpinLockTest.cpp TEST rw_spin_lock_test SOURCES RWSpinLockTest.cpp
TEST semaphore_test SOURCES SemaphoreTest.cpp TEST semaphore_test SOURCES SemaphoreTest.cpp
DIRECTORY synchronization/detail/test/
TEST hardware_test SOURCES HardwareTest.cpp
DIRECTORY system/test/ DIRECTORY system/test/
TEST memory_mapping_test SOURCES MemoryMappingTest.cpp TEST memory_mapping_test SOURCES MemoryMappingTest.cpp
TEST shell_test SOURCES ShellTest.cpp TEST shell_test SOURCES ShellTest.cpp
......
...@@ -16,18 +16,21 @@ ...@@ -16,18 +16,21 @@
#include <folly/synchronization/detail/Hardware.h> #include <folly/synchronization/detail/Hardware.h>
#include <cassert> #include <atomic>
#include <cstdlib> #include <cstdlib>
#include <exception> #include <stdexcept>
#include <utility> #include <utility>
#include <glog/logging.h> #include <boost/preprocessor/repetition/repeat.hpp>
#if FOLLY_X64 && defined(__RTM__)
#include <folly/lang/Assume.h> #include <folly/lang/Assume.h>
#include <folly/lang/Exception.h>
#if FOLLY_X64 && defined(__RTM__)
#include <immintrin.h> #include <immintrin.h>
#define FOLLY_RTM_SUPPORT 1 #define FOLLY_RTM_SUPPORT 1
#elif FOLLY_X64 #else
#define FOLLY_RTM_SUPPORT 0
#endif #endif
#if FOLLY_RTM_SUPPORT #if FOLLY_RTM_SUPPORT
...@@ -39,12 +42,13 @@ ...@@ -39,12 +42,13 @@
#endif #endif
namespace folly { namespace folly {
namespace hardware {
bool rtmEnabled() { static bool rtmEnabledImpl() {
#if FOLLY_RTM_SUPPORT #if !FOLLY_RTM_SUPPORT
#if defined(__GNUC__) || defined(__clang__) return false;
#elif defined(__GNUC__) || defined(__clang__)
if (__get_cpuid_max(0, nullptr) < 7) { if (__get_cpuid_max(0, nullptr) < 7) {
// very surprising, older than Core Duo! // very surprising, older than Core Duo!
...@@ -55,7 +59,9 @@ bool rtmEnabled() { ...@@ -55,7 +59,9 @@ bool rtmEnabled() {
// EBX bit 11 -> RTM support // EBX bit 11 -> RTM support
__cpuid_count(7, 0, ax, bx, cx, dx); __cpuid_count(7, 0, ax, bx, cx, dx);
return ((bx >> 11) & 1) != 0; return ((bx >> 11) & 1) != 0;
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
// __cpuidex: // __cpuidex:
// https://docs.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex?view=vs-2019 // https://docs.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex?view=vs-2019
int cpui[4]; int cpui[4];
...@@ -65,87 +71,127 @@ bool rtmEnabled() { ...@@ -65,87 +71,127 @@ bool rtmEnabled() {
} }
__cpuidex(cpui, 7, 0); __cpuidex(cpui, 7, 0);
return ((cpui[1] >> 11) & 1) != 0; return ((cpui[1] >> 11) & 1) != 0;
#else #else
return false;
#endif
#else // FOLLY_RTM_SUPPORT == 0
return false; return false;
#endif #endif
} }
static unsigned rtmBeginFunc() { bool rtmEnabled() {
static std::atomic<int> result{-1};
auto value = result.load(std::memory_order_relaxed);
if (value < 0) {
value = int(rtmEnabledImpl());
result.store(value, std::memory_order_relaxed);
}
return value;
}
#if FOLLY_RTM_SUPPORT
#define FOLLY_RTM_DISABLED_NORETURN
#else
#define FOLLY_RTM_DISABLED_NORETURN [[noreturn]]
#endif
FOLLY_RTM_DISABLED_NORETURN static unsigned rtmBeginFunc() {
#if FOLLY_RTM_SUPPORT #if FOLLY_RTM_SUPPORT
return _xbegin(); return _xbegin();
#else #else
return kRtmDisabled; assume_unreachable();
#endif #endif
} }
static void rtmEndFunc() { FOLLY_RTM_DISABLED_NORETURN static void rtmEndFunc() {
#if FOLLY_RTM_SUPPORT #if FOLLY_RTM_SUPPORT
_xend(); _xend();
#else #else
CHECK(false) << "rtmEnd called without txn available"; assume_unreachable();
#endif #endif
} }
static bool rtmTestFunc() { FOLLY_RTM_DISABLED_NORETURN static bool rtmTestFunc() {
#if FOLLY_RTM_SUPPORT #if FOLLY_RTM_SUPPORT
return _xtest() != 0; return _xtest() != 0;
#else #else
return 0; assume_unreachable();
#endif #endif
} }
template <size_t... I>
FOLLY_DISABLE_SANITIZERS FOLLY_ALWAYS_INLINE static void
FOLLY_RTM_DISABLED_NORETURN
rtmAbortFunc_(std::index_sequence<I...>, uint8_t status) {
#if FOLLY_RTM_SUPPORT #if FOLLY_RTM_SUPPORT
[[noreturn]] FOLLY_NOINLINE void rtmAbortFuncFailed() {
// If we get here either status is too large or we weren't in a txn.
// If we are actually in a transaction then assert, std::abort, or
// _xabort will all end up aborting the txn. If we're not in a txn
// then we have done something very wrong.
CHECK(false)
<< "rtmAbort called with an invalid status or without an active txn";
folly::assume_unreachable();
}
#endif
[[noreturn]] static void rtmAbortFunc(unsigned status) {
#if FOLLY_RTM_SUPPORT
// Manual case statement instead of using make_index_sequence so
// that we can avoid any object lifetime ASAN interactions even
// in non-optimized builds. _xabort needs a compile-time constant
// argument :(
switch (status) { switch (status) {
case 0: #define FOLLY_RTM_ABORT_ONE(z, n, text) \
_xabort(0); case uint8_t(n): \
break; _xabort(uint8_t(n)); \
case 1: FOLLY_FALLTHROUGH;
_xabort(1); BOOST_PP_REPEAT(256, FOLLY_RTM_ABORT_ONE, unused)
break; #undef FOLLY_RTM_ABORT_ONE
case 2: default:
_xabort(2); terminate_with<std::runtime_error>("rtm not in transaction");
break;
case 3:
_xabort(3);
break;
case 4:
_xabort(4);
break;
} }
rtmAbortFuncFailed();
#else #else
(void)status; assume_unreachable();
std::terminate();
#endif #endif
} }
unsigned (*const rtmBegin)() = rtmBeginFunc; FOLLY_DISABLE_SANITIZERS static void rtmAbortFunc(uint8_t status) {
rtmAbortFunc_(std::make_index_sequence<256u>{}, status);
}
namespace detail {
unsigned rtmBeginDisabled() {
return kRtmDisabled;
}
void rtmEndDisabled() {}
bool rtmTestDisabled() {
return false;
}
[[noreturn]] void rtmAbortDisabled(uint8_t) {
terminate_with<std::runtime_error>("rtm not enabled");
}
static void rewrite() {
if (rtmEnabled()) {
rtmBeginV.store(rtmBeginFunc, std::memory_order_relaxed);
rtmEndV.store(rtmEndFunc, std::memory_order_relaxed);
rtmTestV.store(rtmTestFunc, std::memory_order_relaxed);
rtmAbortV.store(rtmAbortFunc, std::memory_order_relaxed);
} else {
rtmBeginV.store(rtmBeginDisabled, std::memory_order_relaxed);
rtmEndV.store(rtmEndDisabled, std::memory_order_relaxed);
rtmTestV.store(rtmTestDisabled, std::memory_order_relaxed);
rtmAbortV.store(rtmAbortDisabled, std::memory_order_relaxed);
}
}
unsigned rtmBeginVE() {
rewrite();
return rtmBeginV.load(std::memory_order_relaxed)();
}
void rtmEndVE() {
rewrite();
rtmEndV.load(std::memory_order_relaxed)();
}
bool rtmTestVE() {
rewrite();
return rtmTestV.load(std::memory_order_relaxed)();
}
void rtmAbortVE(uint8_t status) {
rewrite();
rtmAbortV.load(std::memory_order_relaxed)(status);
}
void (*const rtmEnd)() = rtmEndFunc; std::atomic<unsigned (*)()> rtmBeginV{rtmBeginVE};
std::atomic<void (*)()> rtmEndV{rtmEndVE};
std::atomic<bool (*)()> rtmTestV{rtmTestVE};
std::atomic<void (*)(uint8_t)> rtmAbortV{rtmAbortVE};
bool (*const rtmTest)() = rtmTestFunc; } // namespace detail
void (*const rtmAbort)(unsigned) = rtmAbortFunc;
} // namespace hardware
} // namespace folly } // namespace folly
...@@ -16,10 +16,11 @@ ...@@ -16,10 +16,11 @@
#pragma once #pragma once
#include <atomic>
#include <folly/Portability.h> #include <folly/Portability.h>
namespace folly { namespace folly {
namespace hardware {
// Valid status values returned from rtmBegin. // Valid status values returned from rtmBegin.
// kRtmDisabled is a new return value indicating that RTM support is unavailable // kRtmDisabled is a new return value indicating that RTM support is unavailable
...@@ -44,20 +45,31 @@ constexpr bool kRtmSupportEnabled = kIsArchAmd64; ...@@ -44,20 +45,31 @@ constexpr bool kRtmSupportEnabled = kIsArchAmd64;
// Check on cpu support for tsx-rtm // Check on cpu support for tsx-rtm
extern bool rtmEnabled(); extern bool rtmEnabled();
namespace detail {
// Use func ptrs to access the txn functions to avoid txn aborts // Use func ptrs to access the txn functions to avoid txn aborts
// due to plt mapping. // due to plt mapping.
extern unsigned (*const rtmBegin)(); extern std::atomic<unsigned (*)()> rtmBeginV;
extern void (*const rtmEnd)(); extern std::atomic<void (*)()> rtmEndV;
extern bool (*const rtmTest)(); extern std::atomic<bool (*)()> rtmTestV;
extern std::atomic<void (*)(uint8_t)> rtmAbortV;
// The abort status code must be known at compile time, so } // namespace detail
// the abstraction layer only supports a subset of the full
// range. rtmAbort(s) fails if s > 4 in the current implementation.
extern void (*const rtmAbort)(unsigned status);
inline unsigned rtmStatusToAbortCode(unsigned status) { inline unsigned rtmBegin() {
return detail::rtmBeginV.load(std::memory_order_relaxed)();
}
inline void rtmEnd() {
return detail::rtmEndV.load(std::memory_order_relaxed)();
}
inline bool rtmTest() {
return detail::rtmTestV.load(std::memory_order_relaxed)();
}
inline void rtmAbort(uint8_t status) {
return detail::rtmAbortV.load(std::memory_order_relaxed)(status);
}
inline uint8_t rtmStatusToAbortCode(unsigned status) {
return status >> 24; return status >> 24;
} }
} // namespace hardware
} // namespace folly } // namespace folly
...@@ -15,13 +15,16 @@ ...@@ -15,13 +15,16 @@
*/ */
#include <folly/synchronization/detail/Hardware.h> #include <folly/synchronization/detail/Hardware.h>
#include <folly/lang/Assume.h>
#include <folly/portability/GTest.h> #include <folly/portability/GTest.h>
#include <glog/logging.h> #include <glog/logging.h>
using namespace folly::hardware; using namespace folly;
class HardwareTest : public testing::Test {};
TEST(RTM, BasicUsage) { TEST_F(HardwareTest, RtmBasicUsage) {
// Test the checkers, though whichever values they returns, // Test the checkers, though whichever values they returns,
// we are allowed to run the loop below and use the return value of rtmBegin // we are allowed to run the loop below and use the return value of rtmBegin
// to indicate whether RTM is supported. // to indicate whether RTM is supported.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment