summaryrefslogtreecommitdiff
path: root/absl/base
diff options
context:
space:
mode:
authorGravatar Evan Brown <ezb@google.com>2022-07-27 13:45:19 -0700
committerGravatar Copybara-Service <copybara-worker@google.com>2022-07-27 13:46:14 -0700
commitc7e60ccfcd708a73008ed2df040162c66697bc18 (patch)
tree904eb83861d0174d48ee11b5637c8919be623d40 /absl/base
parent51f6d868c8463ac13492d504cbc034e91b43a461 (diff)
Add ABSL_IS_TRIVIALLY_RELOCATABLE and ABSL_ATTRIBUTE_TRIVIAL_ABI macros for use with clang's __is_trivially_relocatable and [[clang::trivial_abi]].
PiperOrigin-RevId: 463668740 Change-Id: I2d2d2f53d8184a7e4f7c848c2a5f5140c2481d72
Diffstat (limited to 'absl/base')
-rw-r--r--absl/base/attributes.h37
-rw-r--r--absl/base/optimization.h24
-rw-r--r--absl/base/optimization_test.cc19
3 files changed, 80 insertions, 0 deletions
diff --git a/absl/base/attributes.h b/absl/base/attributes.h
index e4e7a3d8..e11a064a 100644
--- a/absl/base/attributes.h
+++ b/absl/base/attributes.h
@@ -759,4 +759,41 @@
#define ABSL_ATTRIBUTE_LIFETIME_BOUND
#endif
+// ABSL_ATTRIBUTE_TRIVIAL_ABI
+// Indicates that a type is "trivially relocatable" -- meaning it can be
+// relocated without invoking the constructor/destructor, using a form of move
+// elision.
+//
+// From a memory safety point of view, putting aside destructor ordering, it's
+// safe to apply ABSL_ATTRIBUTE_TRIVIAL_ABI if an object's location
+// can change over the course of its lifetime: if a constructor can be run one
+// place, and then the object magically teleports to another place where some
+// methods are run, and then the object teleports to yet another place where it
+// is destroyed. This is notably not true for self-referential types, where the
+// move-constructor must keep the self-reference up to date. If the type changed
+// location without invoking the move constructor, it would have a dangling
+// self-reference.
+//
+// The use of this teleporting machinery means that the number of paired
+// move/destroy operations can change, and so it is a bad idea to apply this to
+// a type meant to count the number of moves.
+//
+// Warning: applying this can, rarely, break callers. Objects passed by value
+// will be destroyed at the end of the call, instead of the end of the
+// full-expression containing the call. In addition, it changes the ABI
+// of functions accepting this type by value (e.g. to pass in registers).
+//
+// See also the upstream documentation:
+// https://clang.llvm.org/docs/AttributeReference.html#trivial-abi
+//
+#if ABSL_HAVE_CPP_ATTRIBUTE(clang::trivial_abi)
+#define ABSL_ATTRIBUTE_TRIVIAL_ABI [[clang::trivial_abi]]
+#define ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI 1
+#elif ABSL_HAVE_ATTRIBUTE(trivial_abi)
+#define ABSL_ATTRIBUTE_TRIVIAL_ABI __attribute__((trivial_abi))
+#define ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI 1
+#else
+#define ABSL_ATTRIBUTE_TRIVIAL_ABI
+#endif
+
#endif // ABSL_BASE_ATTRIBUTES_H_
diff --git a/absl/base/optimization.h b/absl/base/optimization.h
index db5cc097..57999a18 100644
--- a/absl/base/optimization.h
+++ b/absl/base/optimization.h
@@ -249,4 +249,28 @@
#define ABSL_INTERNAL_UNIQUE_SMALL_NAME()
#endif
+// ABSL_IS_TRIVIALLY_RELOCATABLE(type)
+// Detects whether a type is "trivially relocatable" -- meaning it can be
+// relocated without invoking the constructor/destructor, using a form of move
+// elision.
+//
+// Example:
+//
+// if constexpr (ABSL_IS_TRIVIALLY_RELOCATABLE(T)) {
+// memcpy(new_location, old_location, sizeof(T));
+// } else {
+// new(new_location) T(std::move(*old_location));
+// old_location->~T();
+// }
+//
+// Upstream documentation:
+//
+// https://clang.llvm.org/docs/LanguageExtensions.html#:~:text=__is_trivially_relocatable
+//
+#if ABSL_HAVE_BUILTIN(__is_trivially_relocatable)
+#define ABSL_IS_TRIVIALLY_RELOCATABLE(type) __is_trivially_relocatable(type)
+#else
+#define ABSL_IS_TRIVIALLY_RELOCATABLE(type) false
+#endif
+
#endif // ABSL_BASE_OPTIMIZATION_H_
diff --git a/absl/base/optimization_test.cc b/absl/base/optimization_test.cc
index e83369f3..5f7a8f4c 100644
--- a/absl/base/optimization_test.cc
+++ b/absl/base/optimization_test.cc
@@ -15,6 +15,7 @@
#include "absl/base/optimization.h"
#include "gtest/gtest.h"
+#include "absl/base/attributes.h"
#include "absl/types/optional.h"
namespace {
@@ -126,4 +127,22 @@ TEST(PredictTest, ExplicitBoolConversion) {
if (ABSL_PREDICT_FALSE(is_false)) ADD_FAILURE();
}
+TEST(TrivallyRelocatable, Sanity) {
+#if !defined(ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI) || \
+ !ABSL_HAVE_BUILTIN(__is_trivially_relocatable)
+ GTEST_SKIP() << "No trivial ABI support.";
+#endif
+
+ struct Trivial {};
+ struct NonTrivial {
+ NonTrivial(const NonTrivial&) {}
+ };
+ struct ABSL_ATTRIBUTE_TRIVIAL_ABI TrivialAbi {
+ TrivialAbi(const TrivialAbi&) {}
+ };
+ EXPECT_TRUE(ABSL_IS_TRIVIALLY_RELOCATABLE(Trivial));
+ EXPECT_FALSE(ABSL_IS_TRIVIALLY_RELOCATABLE(NonTrivial));
+ EXPECT_TRUE(ABSL_IS_TRIVIALLY_RELOCATABLE(TrivialAbi));
+}
+
} // namespace