Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files
Automatic merge of jdk:master into master
  • Loading branch information
duke committed Sep 15, 2020
2 parents 6651837 + 7eb4d4a commit 69998b371e9d3f9241b026771b7bcafebecffdf7
@@ -27,6 +27,7 @@
#include "gc/z/zBitField.hpp"
#include "memory/allocation.hpp"
#include "metaprogramming/primitiveConversions.hpp"
#include <type_traits>

//
// Forwarding entry layout
@@ -46,7 +47,7 @@
//

class ZForwardingEntry {
friend class PrimitiveConversions;
friend struct PrimitiveConversions::Translate<ZForwardingEntry>;

private:
typedef ZBitField<uint64_t, bool, 0, 1> field_populated;
@@ -79,7 +80,7 @@ class ZForwardingEntry {

// Needed to allow atomic operations on ZForwardingEntry
template <>
struct PrimitiveConversions::Translate<ZForwardingEntry> : public TrueType {
struct PrimitiveConversions::Translate<ZForwardingEntry> : public std::true_type {
typedef ZForwardingEntry Value;
typedef uint64_t Decayed;

@@ -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 <type_traits>

class PrimitiveConversions : public AllStatic {

// True if types are the same size and either is integral.
template<typename To, typename From>
static constexpr bool check_cast() {
return (sizeof(To) == sizeof(From)) &&
(std::is_integral<To>::value || std::is_integral<From>::value);
}

public:
// Return a value of type T with the same representation as x.
// template<typename To, typename From> To cast(From x)
//
// Return a value of type To with the same value representation as x.
//
// T and U must be of the same size.
// To and From must be of the same size.
//
// At least one of T or U must be an integral type. The other must
// be an integral, floating point, or pointer type.
template<typename T, typename U> static T cast(U x);
// 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<typename To, typename From,
ENABLE_IF(sizeof(To) == sizeof(From)),
ENABLE_IF(std::is_integral<To>::value),
ENABLE_IF(std::is_integral<From>::value)>
static constexpr To cast(From x) {
return static_cast<To>(x);
}

// integer -> enum, enum -> integer
// Use the enum's underlying type for integer -> integer cast.
template<typename To, typename From,
ENABLE_IF(check_cast<To, From>()),
ENABLE_IF(std::is_enum<To>::value)>
static constexpr To cast(From x) {
return static_cast<To>(cast<std::underlying_type_t<To>>(x));
}

template<typename To, typename From,
ENABLE_IF(check_cast<To, From>()),
ENABLE_IF(std::is_enum<From>::value)>
static constexpr To cast(From x) {
return cast<To>(static_cast<std::underlying_type_t<From>>(x));
}

// integer -> pointer, pointer -> integer
// Use reinterpret_cast, so no constexpr support.
template<typename To, typename From,
ENABLE_IF(check_cast<To, From>()),
ENABLE_IF(std::is_pointer<To>::value || std::is_pointer<From>::value)>
static To cast(From x) {
return reinterpret_cast<To>(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<typename To, typename From,
ENABLE_IF(check_cast<To, From>()),
ENABLE_IF(std::is_floating_point<To>::value ||
std::is_floating_point<From>::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<typename T> struct Translate : public FalseType {};

private:

template<typename T,
typename U,
bool same_size = sizeof(T) == sizeof(U),
typename Enable = void>
struct Cast;

template<typename T, typename U> static T cast_using_union(U x);
};

// 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 <type_traits> 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<typename T, typename U>
inline T PrimitiveConversions::cast_using_union(U x) {
STATIC_ASSERT(sizeof(T) == sizeof(U));
union { T t; U u; };
u = x;
return t;
}

//////////////////////////////////////////////////////////////////////////////
// cast<T>(x)
//
// Cast<T, U, same_size, Enable>

// Give an informative error if the sizes differ.
template<typename T, typename U>
struct PrimitiveConversions::Cast<T, U, false> {
STATIC_ASSERT(sizeof(T) == sizeof(U));
};

// Conversion between integral types.
template<typename T, typename U>
struct PrimitiveConversions::Cast<
T, U, true,
typename EnableIf<IsIntegral<T>::value && IsIntegral<U>::value>::type>
{
T operator()(U x) const { return cast_using_union<T>(x); }
// same value representation as x.
template<typename T> struct Translate : public std::false_type {};
};

// Convert an enum or floating point value to an integer value.
template<typename T, typename U>
struct PrimitiveConversions::Cast<
T, U, true,
typename EnableIf<IsIntegral<T>::value &&
(std::is_enum<U>::value ||
IsFloatingPoint<U>::value)>::type>
{
T operator()(U x) const { return cast_using_union<T>(x); }
};

// Convert an integer to an enum or floating point value.
template<typename T, typename U>
struct PrimitiveConversions::Cast<
T, U, true,
typename EnableIf<IsIntegral<U>::value &&
(std::is_enum<T>::value ||
IsFloatingPoint<T>::value)>::type>
{
T operator()(U x) const { return cast_using_union<T>(x); }
};

// Convert a pointer to an integral value.
template<typename T, typename U>
struct PrimitiveConversions::Cast<
T, U*, true,
typename EnableIf<IsIntegral<T>::value>::type>
{
T operator()(U* x) const { return reinterpret_cast<T>(x); }
};

// Convert an integral value to a pointer.
template<typename T, typename U>
struct PrimitiveConversions::Cast<
T*, U, true,
typename EnableIf<IsIntegral<U>::value>::type>
{
T* operator()(U x) const { return reinterpret_cast<T*>(x); }
};

template<typename T, typename U>
inline T PrimitiveConversions::cast(U x) {
return Cast<T, U>()(x);
}

// jfloat and jdouble translation to integral types

template<>
struct PrimitiveConversions::Translate<jdouble> : public TrueType {
struct PrimitiveConversions::Translate<jdouble> : public std::true_type {
typedef double Value;
typedef int64_t Decayed;

@@ -174,7 +129,7 @@ struct PrimitiveConversions::Translate<jdouble> : public TrueType {
};

template<>
struct PrimitiveConversions::Translate<jfloat> : public TrueType {
struct PrimitiveConversions::Translate<jfloat> : public std::true_type {
typedef float Value;
typedef int32_t Decayed;

@@ -95,6 +95,51 @@ TEST(PrimitiveConversionsTest, round_trip_int) {
EXPECT_EQ(ufive, PrimitiveConversions::cast<uint>(PrimitiveConversions::cast<UI>(ufive)));
}

TEST(PrimitiveConversionsTest, round_trip_int_constexpr) {
constexpr int sfive = 5;
constexpr int mfive = -5;
constexpr uint ufive = 5u;

typedef PrimitiveConversionsTestSupport::Signed<int>::type SI;
typedef PrimitiveConversionsTestSupport::Unsigned<int>::type UI;

{
constexpr SI i = PrimitiveConversions::cast<SI>(sfive);
constexpr int r = PrimitiveConversions::cast<int>(i);
EXPECT_EQ(sfive, r);
}

{
constexpr UI i = PrimitiveConversions::cast<UI>(sfive);
constexpr int r = PrimitiveConversions::cast<int>(i);
EXPECT_EQ(sfive, r);
}

{
constexpr SI i = PrimitiveConversions::cast<SI>(mfive);
constexpr int r = PrimitiveConversions::cast<int>(i);
EXPECT_EQ(mfive, r);
}

{
constexpr UI i = PrimitiveConversions::cast<UI>(mfive);
constexpr int r = PrimitiveConversions::cast<int>(i);
EXPECT_EQ(mfive, r);
}

{
constexpr SI i = PrimitiveConversions::cast<SI>(ufive);
constexpr uint r = PrimitiveConversions::cast<uint>(i);
EXPECT_EQ(ufive, r);
}

{
constexpr UI i = PrimitiveConversions::cast<UI>(ufive);
constexpr uint r = PrimitiveConversions::cast<uint>(i);
EXPECT_EQ(ufive, r);
}
}

TEST(PrimitiveConversionsTest, round_trip_float) {
float ffive = 5.0f;
double dfive = 5.0;
@@ -23,9 +23,10 @@

#include "precompiled.hpp"
#include "runtime/os.hpp"
#include "unittest.hpp"
#include "utilities/align.hpp"
#include "utilities/globalDefinitions.hpp"
#include <type_traits>
#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<type>::value) { \
if (std::is_signed<type>::value) { \
EXPECT_EQ(limit - 1, fn(std::numeric_limits<type>::min())); \
EXPECT_EQ(limit - 1, fn((type)-1)); \
limit--; \

0 comments on commit 69998b3

Please sign in to comment.