[PATCH] PR libstdc++/91788 improve codegen for std::variant<T...>::index()

Jonathan Wakely jwakely@redhat.com
Mon Sep 23 15:54:00 GMT 2019


If __index_type is a smaller type than size_t, then the result of
size_t(__index_type(-1)) is not equal to size_t(-1), but to an incorrect
value such as size_t(255) or size_t(65535). The old implementation of
variant<T...>::index() uses (size_t(__index_type(_M_index + 1)) - 1)
which is always correct, but generates suboptimal code for many common
cases.

When the __index_type is size_t or valueless variants are not possible
we can just return the value directly.

When the number of alternatives is sufficiently small the result of
converting the _M_index value to the corresponding signed type will be
either non-negative or -1. In those cases converting to the signed type
and then to size_t will either produce the correct positive value or
will sign extend -1 to (size_t)-1 as desired.

For the remaining case we keep the existing arithmetic operations to
ensure the correct result.

	PR libstdc++/91788 (partial)
	* include/std/variant (variant::index()): Improve codegen for cases
	where conversion to size_t already works correctly.

Tested x86_64-linux, committed to trunk.


-------------- next part --------------
commit 6ef8d9750c46e3d781c4d8cf169f66a13e0d0827
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Mon Sep 23 16:07:54 2019 +0100

    PR libstdc++/91788 improve codegen for std::variant<T...>::index()
    
    If __index_type is a smaller type than size_t, then the result of
    size_t(__index_type(-1)) is not equal to size_t(-1), but to an incorrect
    value such as size_t(255) or size_t(65535). The old implementation of
    variant<T...>::index() uses (size_t(__index_type(_M_index + 1)) - 1)
    which is always correct, but generates suboptimal code for many common
    cases.
    
    When the __index_type is size_t or valueless variants are not possible
    we can just return the value directly.
    
    When the number of alternatives is sufficiently small the result of
    converting the _M_index value to the corresponding signed type will be
    either non-negative or -1. In those cases converting to the signed type
    and then to size_t will either produce the correct positive value or
    will sign extend -1 to (size_t)-1 as desired.
    
    For the remaining case we keep the existing arithmetic operations to
    ensure the correct result.
    
            PR libstdc++/91788 (partial)
            * include/std/variant (variant::index()): Improve codegen for cases
            where conversion to size_t already works correctly.

diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant
index d93ea86ea29..c0043243ec2 100644
--- a/libstdc++-v3/include/std/variant
+++ b/libstdc++-v3/include/std/variant
@@ -1518,7 +1518,17 @@ namespace __variant
       { return !this->_M_valid(); }
 
       constexpr size_t index() const noexcept
-      { return size_t(typename _Base::__index_type(this->_M_index + 1)) - 1; }
+      {
+	using __index_type = typename _Base::__index_type;
+	if constexpr (is_same_v<__index_type, size_t>)
+	  return this->_M_index;
+	else if constexpr (__detail::__variant::__never_valueless<_Types...>())
+	  return this->_M_index;
+	else if constexpr (sizeof...(_Types) <= __index_type(-1) / 2)
+	  return make_signed_t<__index_type>(this->_M_index);
+	else
+	  return size_t(__index_type(this->_M_index + 1)) - 1;
+      }
 
       void
       swap(variant& __rhs)


More information about the Libstdc++ mailing list