Add ABSL_ATTRIBUTE_MUSTTAIL and use it in InlinedVector::EmplaceBack. Define `ABSL_ATTRIBUTE_MUSTTAIL` and `ABSL_HAVE_ATTRIBUTE_MUSTTAIL` in `absl/base/attributes.h` using `[[clang::musttail]]` on 64-bit x86 and AArch64 platforms (excluding MinGW, 32-bit targets, and PowerPC where backend tail-call support is incomplete), and apply it in `InlinedVector`'s `Storage::EmplaceBack` to guarantee a tail call to `EmplaceBackSlow`. PiperOrigin-RevId: 1009708782
diff --git a/absl/base/attributes.h b/absl/base/attributes.h index d829f17..6146184 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h
@@ -149,6 +149,43 @@ #define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 0 #endif +// ABSL_ATTRIBUTE_MUSTTAIL +// +// Tells the compiler that a `return` statement must be compiled as a tail +// call, reusing the caller's stack frame rather than creating a new one. +// Compilation fails if the compiler cannot generate a tail call. +// +// Requirements on supported platforms: +// * Must be placed immediately before `return`, and the returned expression +// must be a function call (not wrapped in another expression or conversion). +// * The caller and callee must have compatible function signatures, including +// matching return types, parameter types, calling conventions, and implicit +// `this` arguments (a non-static member function can only tail-call another +// non-static member function of the same class). +// * No variables, temporaries, or by-value parameters with non-trivial +// destructors may be live at the `return` statement. +// * Arguments to the tail call must not reference local variables or by-value +// parameters in the caller's stack frame, whose lifetime ends before the +// callee runs. +// +// Note: Because `ABSL_ATTRIBUTE_MUSTTAIL` expands to nothing when unsupported +// (`!ABSL_HAVE_ATTRIBUTE_MUSTTAIL`), callers must not rely on it for unbounded +// recursion without checking `ABSL_HAVE_ATTRIBUTE_MUSTTAIL`. +// +// See: https://clang.llvm.org/docs/AttributeReference.html#musttail +#ifdef ABSL_HAVE_ATTRIBUTE_MUSTTAIL +#error "ABSL_HAVE_ATTRIBUTE_MUSTTAIL cannot be directly set." +#elif defined(ABSL_ATTRIBUTE_MUSTTAIL) +#error "ABSL_ATTRIBUTE_MUSTTAIL cannot be directly set." +#elif ABSL_HAVE_CPP_ATTRIBUTE(clang::musttail) && !defined(__MINGW32__) && \ + (defined(__aarch64__) || defined(__x86_64__) || defined(_M_X64)) +#define ABSL_HAVE_ATTRIBUTE_MUSTTAIL 1 +#define ABSL_ATTRIBUTE_MUSTTAIL [[clang::musttail]] +#else +#define ABSL_HAVE_ATTRIBUTE_MUSTTAIL 0 +#define ABSL_ATTRIBUTE_MUSTTAIL +#endif + // ABSL_ATTRIBUTE_WEAK // // Tags a function as weak for the purposes of compilation and linking.
diff --git a/absl/base/attributes_test.cc b/absl/base/attributes_test.cc index e87b66e..e8f9faa 100644 --- a/absl/base/attributes_test.cc +++ b/absl/base/attributes_test.cc
@@ -14,7 +14,9 @@ #include "absl/base/attributes.h" +#include <cstdint> #include <type_traits> +#include <utility> #include "gtest/gtest.h" #include "absl/base/config.h" @@ -72,4 +74,27 @@ EXPECT_EQ(buffer[1], 1); } +ABSL_ATTRIBUTE_NOINLINE void MustTailVoidCallee(int* out, int x) { *out = x; } + +ABSL_ATTRIBUTE_NOINLINE void MustTailVoidCaller(int* out, int x) { + ABSL_ATTRIBUTE_MUSTTAIL return MustTailVoidCallee(out, x + 2); +} + +ABSL_ATTRIBUTE_NOINLINE int64_t MustTailCountdown(int64_t n, int64_t acc) { + if (n == 0) return acc; + ABSL_ATTRIBUTE_MUSTTAIL return MustTailCountdown(n - 1, acc + n); +} + +TEST(Attributes, MustTail) { + int out = 0; + MustTailVoidCaller(&out, 10); + EXPECT_EQ(out, 12); + +#if ABSL_HAVE_ATTRIBUTE_MUSTTAIL + // A 200,000-deep recursion would overflow the stack without tail-call. + constexpr int64_t kDepth = 200000; + EXPECT_EQ(MustTailCountdown(kDepth, 0), kDepth * (kDepth + 1) / 2); +#endif +} + } // namespace
diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h index e9eafdd..1e74234 100644 --- a/absl/container/internal/inlined_vector.h +++ b/absl/container/internal/inlined_vector.h
@@ -825,8 +825,7 @@ AddSize(1); return *last_ptr; } - // TODO(b/173712035): Annotate with musttail attribute to prevent regression. - return EmplaceBackSlow(std::forward<Args>(args)...); + ABSL_ATTRIBUTE_MUSTTAIL return EmplaceBackSlow(std::forward<Args>(args)...); } template <typename T, size_t N, typename A>