diff --git a/src/hotspot/share/gc/z/zForwardingEntry.hpp b/src/hotspot/share/gc/z/zForwardingEntry.hpp index a6db411c314..2aa538f0ec2 100644 --- a/src/hotspot/share/gc/z/zForwardingEntry.hpp +++ b/src/hotspot/share/gc/z/zForwardingEntry.hpp @@ -27,6 +27,7 @@ #include "gc/z/zBitField.hpp" #include "memory/allocation.hpp" #include "metaprogramming/primitiveConversions.hpp" +#include // // Forwarding entry layout @@ -46,7 +47,7 @@ // class ZForwardingEntry { - friend class PrimitiveConversions; + friend struct PrimitiveConversions::Translate; private: typedef ZBitField field_populated; @@ -79,7 +80,7 @@ public: // Needed to allow atomic operations on ZForwardingEntry template <> -struct PrimitiveConversions::Translate : public TrueType { +struct PrimitiveConversions::Translate : public std::true_type { typedef ZForwardingEntry Value; typedef uint64_t Decayed; diff --git a/src/hotspot/share/metaprogramming/primitiveConversions.hpp b/src/hotspot/share/metaprogramming/primitiveConversions.hpp index 2d9fa6e3dbb..08806442bf4 100644 --- a/src/hotspot/share/metaprogramming/primitiveConversions.hpp +++ b/src/hotspot/share/metaprogramming/primitiveConversions.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,147 +25,102 @@ #ifndef SHARE_METAPROGRAMMING_PRIMITIVECONVERSIONS_HPP #define SHARE_METAPROGRAMMING_PRIMITIVECONVERSIONS_HPP -#include "memory/allocation.hpp" +#include "memory/allStatic.hpp" #include "metaprogramming/enableIf.hpp" -#include "metaprogramming/integralConstant.hpp" -#include "metaprogramming/isFloatingPoint.hpp" -#include "metaprogramming/isIntegral.hpp" -#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" #include class PrimitiveConversions : public AllStatic { + + // True if types are the same size and either is integral. + template + static constexpr bool check_cast() { + return (sizeof(To) == sizeof(From)) && + (std::is_integral::value || std::is_integral::value); + } + public: - // Return a value of type T with the same representation as x. + // template To cast(From x) // - // T and U must be of the same size. + // Return a value of type To with the same value representation as x. // - // At least one of T or U must be an integral type. The other must - // be an integral, floating point, or pointer type. - template static T cast(U x); + // To and From must be of the same size. + // + // At least one of To or From must be an integral type. The other must + // be an integral, enum, floating point, or pointer type. + + // integer -> integer + // Use static_cast for conversion. See C++14 4.7 Integral + // conversions. If To is signed and From unsigned, the result is + // implementation-defined. All supported platforms provide two's + // complement behavior, and that behavior is required by C++20. + // Using an lvalue to reference cast (see C++03 3.10/15) involves a + // reinterpret_cast, which prevents constexpr support. + template::value), + ENABLE_IF(std::is_integral::value)> + static constexpr To cast(From x) { + return static_cast(x); + } + + // integer -> enum, enum -> integer + // Use the enum's underlying type for integer -> integer cast. + template()), + ENABLE_IF(std::is_enum::value)> + static constexpr To cast(From x) { + return static_cast(cast>(x)); + } + + template()), + ENABLE_IF(std::is_enum::value)> + static constexpr To cast(From x) { + return cast(static_cast>(x)); + } + + // integer -> pointer, pointer -> integer + // Use reinterpret_cast, so no constexpr support. + template()), + ENABLE_IF(std::is_pointer::value || std::is_pointer::value)> + static To cast(From x) { + return reinterpret_cast(x); + } + + // integer -> floating point, floating point -> integer + // Use the union trick. The union trick is technically UB, but is + // widely and well supported, producing good code. In some cases, + // such as gcc, that support is explicitly documented. Using memcpy + // is the correct method, but some compilers produce wretched code + // for that method, even at maximal optimization levels. Neither + // the union trick nor memcpy provides constexpr support. + template()), + ENABLE_IF(std::is_floating_point::value || + std::is_floating_point::value)> + static To cast(From x) { + union { From from; To to; } converter = { x }; + return converter.to; + } // Support thin wrappers over primitive types. - // If derived from TrueType, provides representational conversion + // If derived from std::true_type, provides representational conversion // from T to some other type. When true, must provide // - Value: typedef for T. // - Decayed: typedef for decayed type. // - static Decayed decay(T x): return value of type Decayed with - // the same representation as x. + // the same value representation as x. // - static T recover(Decayed x): return a value of type T with the - // same representation as x. - template struct Translate : public FalseType {}; - -private: - - template - struct Cast; - - template static T cast_using_union(U x); + // same value representation as x. + template struct Translate : public std::false_type {}; }; -// Return an object of type T with the same value representation as x. -// -// T and U must be of the same size. It is expected that one of T and -// U is an integral type, and the other is an integral type, an enum type, -// or a floating point type. -// -// This implementation uses the "union trick", which seems to be the -// best of a bad set of options. Though technically undefined -// behavior, it is widely and well supported, producing good code. In -// some cases, such as gcc, that support is explicitly documented. -// -// Using memcpy is the correct method, but some compilers produce -// wretched code for that method, even at maximal optimization levels. -// -// Using static_cast is only possible for integral and enum types, not -// for floating point types. And for integral and enum conversions, -// static_cast has unspecified or implementation-defined behavior for -// some cases. C++11 can be used to avoid most or all -// of those unspecified or implementation-defined issues, though that -// may require multi-step conversions. -// -// Using reinterpret_cast of references has undefined behavior for -// many cases, and there is much less empirical basis for its use, as -// compared to the union trick. -template -inline T PrimitiveConversions::cast_using_union(U x) { - STATIC_ASSERT(sizeof(T) == sizeof(U)); - union { T t; U u; }; - u = x; - return t; -} - -////////////////////////////////////////////////////////////////////////////// -// cast(x) -// -// Cast - -// Give an informative error if the sizes differ. -template -struct PrimitiveConversions::Cast { - STATIC_ASSERT(sizeof(T) == sizeof(U)); -}; - -// Conversion between integral types. -template -struct PrimitiveConversions::Cast< - T, U, true, - typename EnableIf::value && IsIntegral::value>::type> -{ - T operator()(U x) const { return cast_using_union(x); } -}; - -// Convert an enum or floating point value to an integer value. -template -struct PrimitiveConversions::Cast< - T, U, true, - typename EnableIf::value && - (std::is_enum::value || - IsFloatingPoint::value)>::type> -{ - T operator()(U x) const { return cast_using_union(x); } -}; - -// Convert an integer to an enum or floating point value. -template -struct PrimitiveConversions::Cast< - T, U, true, - typename EnableIf::value && - (std::is_enum::value || - IsFloatingPoint::value)>::type> -{ - T operator()(U x) const { return cast_using_union(x); } -}; - -// Convert a pointer to an integral value. -template -struct PrimitiveConversions::Cast< - T, U*, true, - typename EnableIf::value>::type> -{ - T operator()(U* x) const { return reinterpret_cast(x); } -}; - -// Convert an integral value to a pointer. -template -struct PrimitiveConversions::Cast< - T*, U, true, - typename EnableIf::value>::type> -{ - T* operator()(U x) const { return reinterpret_cast(x); } -}; - -template -inline T PrimitiveConversions::cast(U x) { - return Cast()(x); -} - // jfloat and jdouble translation to integral types template<> -struct PrimitiveConversions::Translate : public TrueType { +struct PrimitiveConversions::Translate : public std::true_type { typedef double Value; typedef int64_t Decayed; @@ -174,7 +129,7 @@ struct PrimitiveConversions::Translate : public TrueType { }; template<> -struct PrimitiveConversions::Translate : public TrueType { +struct PrimitiveConversions::Translate : public std::true_type { typedef float Value; typedef int32_t Decayed; diff --git a/test/hotspot/gtest/metaprogramming/test_primitiveConversions.cpp b/test/hotspot/gtest/metaprogramming/test_primitiveConversions.cpp index 448295122bd..f143e28feee 100644 --- a/test/hotspot/gtest/metaprogramming/test_primitiveConversions.cpp +++ b/test/hotspot/gtest/metaprogramming/test_primitiveConversions.cpp @@ -95,6 +95,51 @@ TEST(PrimitiveConversionsTest, round_trip_int) { EXPECT_EQ(ufive, PrimitiveConversions::cast(PrimitiveConversions::cast(ufive))); } +TEST(PrimitiveConversionsTest, round_trip_int_constexpr) { + constexpr int sfive = 5; + constexpr int mfive = -5; + constexpr uint ufive = 5u; + + typedef PrimitiveConversionsTestSupport::Signed::type SI; + typedef PrimitiveConversionsTestSupport::Unsigned::type UI; + + { + constexpr SI i = PrimitiveConversions::cast(sfive); + constexpr int r = PrimitiveConversions::cast(i); + EXPECT_EQ(sfive, r); + } + + { + constexpr UI i = PrimitiveConversions::cast(sfive); + constexpr int r = PrimitiveConversions::cast(i); + EXPECT_EQ(sfive, r); + } + + { + constexpr SI i = PrimitiveConversions::cast(mfive); + constexpr int r = PrimitiveConversions::cast(i); + EXPECT_EQ(mfive, r); + } + + { + constexpr UI i = PrimitiveConversions::cast(mfive); + constexpr int r = PrimitiveConversions::cast(i); + EXPECT_EQ(mfive, r); + } + + { + constexpr SI i = PrimitiveConversions::cast(ufive); + constexpr uint r = PrimitiveConversions::cast(i); + EXPECT_EQ(ufive, r); + } + + { + constexpr UI i = PrimitiveConversions::cast(ufive); + constexpr uint r = PrimitiveConversions::cast(i); + EXPECT_EQ(ufive, r); + } +} + TEST(PrimitiveConversionsTest, round_trip_float) { float ffive = 5.0f; double dfive = 5.0; diff --git a/test/hotspot/gtest/utilities/test_globalDefinitions.cpp b/test/hotspot/gtest/utilities/test_globalDefinitions.cpp index a920351ee78..e82eea051ae 100644 --- a/test/hotspot/gtest/utilities/test_globalDefinitions.cpp +++ b/test/hotspot/gtest/utilities/test_globalDefinitions.cpp @@ -23,9 +23,10 @@ #include "precompiled.hpp" #include "runtime/os.hpp" -#include "unittest.hpp" #include "utilities/align.hpp" #include "utilities/globalDefinitions.hpp" +#include +#include "unittest.hpp" static ::testing::AssertionResult testPageAddress( const char* expected_addr_expr, @@ -192,7 +193,7 @@ TEST(globalDefinitions, byte_size_in_exact_unit) { #define EXPECT_EQ_LOG2(fn, type) \ { \ int limit = sizeof (type) * BitsPerByte; \ - if (IsSigned::value) { \ + if (std::is_signed::value) { \ EXPECT_EQ(limit - 1, fn(std::numeric_limits::min())); \ EXPECT_EQ(limit - 1, fn((type)-1)); \ limit--; \