Commit def0359e authored by Rui Zhang's avatar Rui Zhang Committed by Facebook Github Bot

Wrap TSX intrinsics with checks on compiler and architecture support for TSX.

Summary: The TSX intrinsics should only be compiled and used when compilers and platforms support them. This diff wraps the intrinsics and adds checks on TSX support to server this purpose.

Reviewed By: nbronson

Differential Revision: D16585187

fbshipit-source-id: 6490c0da7f6221caaa4529bfcebb261cfd99cc7d
parent 06a2d529
/*
* Copyright 2019-present Facebook, Inc.
*
* 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.
*/
#include <folly/synchronization/detail/Hardware.h>
#include <cassert>
#include <cstdlib>
#include <exception>
#include <utility>
#include <glog/logging.h>
#if defined(__GNUC__) || defined(__clang__)
#include <cpuid.h>
#elif defined(_MSC_VER)
#include <intrin.h>
#endif
#if FOLLY_X64 && defined(__RTM__)
#include <folly/lang/Assume.h>
#include <immintrin.h>
#define FOLLY_RTM_SUPPORT 1
#elif FOLLY_X64
#endif
namespace folly {
namespace hardware {
bool rtmEnabled() {
#if FOLLY_RTM_SUPPORT
#if defined(__GNUC__) || defined(__clang__)
if (__get_cpuid_max(0, nullptr) < 7) {
// very surprising, older than Core Duo!
return false;
}
unsigned ax, bx, cx, dx;
// CPUID EAX=7, ECX=0: Extended Features
// EBX bit 11 -> RTM support
__cpuid_count(7, 0, ax, bx, cx, dx);
return ((bx >> 11) & 1) != 0;
#elif defined(_MSC_VER)
// __cpuidex:
// https://docs.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex?view=vs-2019
int cpui[4];
__cpuid(cpui, 0);
if (unsigned(cpui[0]) < 7) {
return false;
}
__cpuidex(cpui, 7, 0);
return ((cpui[1] >> 11) & 1) != 0;
#else
return false;
#endif
#else // FOLLY_RTM_SUPPORT == 0
return false;
#endif
}
static unsigned rtmBeginFunc() {
#if FOLLY_RTM_SUPPORT
return _xbegin();
#else
return kRtmDisabled;
#endif
}
static void rtmEndFunc() {
#if FOLLY_RTM_SUPPORT
_xend();
#else
CHECK(false) << "rtmEnd called without txn available";
#endif
}
static bool rtmTestFunc() {
#if FOLLY_RTM_SUPPORT
return _xtest() != 0;
#else
return 0;
#endif
}
#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) {
case 0:
_xabort(0);
break;
case 1:
_xabort(1);
break;
case 2:
_xabort(2);
break;
case 3:
_xabort(3);
break;
case 4:
_xabort(4);
break;
}
rtmAbortFuncFailed();
#else
(void)status;
std::terminate();
#endif
}
unsigned (*const rtmBegin)() = rtmBeginFunc;
void (*const rtmEnd)() = rtmEndFunc;
bool (*const rtmTest)() = rtmTestFunc;
void (*const rtmAbort)(unsigned) = rtmAbortFunc;
} // namespace hardware
} // namespace folly
/*
* Copyright 2019-present Facebook, Inc.
*
* 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.
*/
#pragma once
#include <folly/Portability.h>
namespace folly {
namespace hardware {
// Valid status values returned from rtmBegin.
// kRtmDisabled is a new return value indicating that RTM support is unavailable
// on the platform or the compiler.
constexpr unsigned kRtmDisabled = static_cast<unsigned>(-2);
constexpr unsigned kRtmBeginStarted = static_cast<unsigned>(-1);
// Valid abort status bits (when the status value is not kRtmBeginStarted or
// kRtmDisabled), defined as per the Intel RTM specifications:
// https://en.wikipedia.org/wiki/Transactional_Synchronization_Extensions.
constexpr unsigned kRtmAbortExplicit = 1;
constexpr unsigned kRtmAbortRetry = 2;
constexpr unsigned kRtmAbortConflict = 4;
constexpr unsigned kRtmAbortCapacity = 8;
constexpr unsigned kRtmAbortDebug = 16;
constexpr unsigned kRtmAbortNested = 32;
// False if there is no need for a dynamic check to see if
// the current environment supports RTM
constexpr bool kRtmSupportEnabled = kIsArchAmd64;
// Check on cpu support for tsx-rtm
extern bool rtmEnabled();
// Use func ptrs to access the txn functions to avoid txn aborts
// due to plt mapping.
extern unsigned (*const rtmBegin)();
extern void (*const rtmEnd)();
extern bool (*const rtmTest)();
// The abort status code must be known at compile time, so
// 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) {
return status >> 24;
}
} // namespace hardware
} // namespace folly
/*
* Copyright 2019-present Facebook, Inc.
*
* 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.
*/
#include <folly/synchronization/detail/Hardware.h>
#include <folly/lang/Assume.h>
#include <folly/portability/GTest.h>
#include <glog/logging.h>
using namespace folly::hardware;
TEST(RTM, BasicUsage) {
// Test the checkers, though whichever values they returns,
// we are allowed to run the loop below and use the return value of rtmBegin
// to indicate whether RTM is supported.
if (kRtmSupportEnabled) {
LOG(INFO) << "rtmEnabled: " << (rtmEnabled() ? 1 : 0);
} else {
EXPECT_FALSE(rtmEnabled());
}
unsigned status;
static constexpr uint8_t kAbortCode = 1;
unsigned successCounts = 0;
unsigned abortCounts = 0;
// 30 attempts to start transactions
for (int i = 0; i < 30; ++i) {
EXPECT_FALSE(rtmTest());
status = rtmBegin();
if (status == kRtmBeginStarted) {
// we are inside a txn now
successCounts++;
auto t = rtmTest();
if (!t) {
EXPECT_TRUE(t);
}
if (i % 2 == 0) {
rtmEnd();
continue;
}
// abort explicitly
rtmAbort(kAbortCode);
EXPECT_TRUE(false);
} else if (status == kRtmDisabled) {
break;
} else if (
(status & kRtmAbortExplicit) != 0 &&
rtmStatusToAbortCode(status) == kAbortCode) {
abortCounts++;
} else if ((status & kRtmAbortRetry) != 0) {
}
}
// Expect that there is at least 1 successful transaction in an environment
// where RTM is enabled.
EXPECT_TRUE(
(successCounts > 0 && abortCounts > 0) ||
(status == kRtmDisabled && !rtmEnabled()));
}
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