blob: 3cbcc9e0c32cdfcfcf99c4cbed2f2b563b30d739 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ART_COMPILER_OPTIMIZING_DATA_TYPE_H_
#define ART_COMPILER_OPTIMIZING_DATA_TYPE_H_
#include <iosfwd>
#include <android-base/logging.h>
#include "base/bit_utils.h"
namespace art {
class DataType {
public:
enum class Type : uint8_t {
kReference = 0,
kBool,
kUint8,
kInt8,
kUint16,
kInt16,
kUint32,
kInt32,
kUint64,
kInt64,
kFloat32,
kFloat64,
kVoid,
kLast = kVoid
};
static constexpr Type FromShorty(char type);
static constexpr char TypeId(DataType::Type type);
static constexpr size_t SizeShift(Type type) {
switch (type) {
case Type::kVoid:
case Type::kBool:
case Type::kUint8:
case Type::kInt8:
return 0;
case Type::kUint16:
case Type::kInt16:
return 1;
case Type::kUint32:
case Type::kInt32:
case Type::kFloat32:
return 2;
case Type::kUint64:
case Type::kInt64:
case Type::kFloat64:
return 3;
case Type::kReference:
return WhichPowerOf2(kObjectReferenceSize);
default:
LOG(FATAL) << "Invalid type " << static_cast<int>(type);
return 0;
}
}
static constexpr size_t Size(Type type) {
switch (type) {
case Type::kVoid:
return 0;
case Type::kBool:
case Type::kUint8:
case Type::kInt8:
return 1;
case Type::kUint16:
case Type::kInt16:
return 2;
case Type::kUint32:
case Type::kInt32:
case Type::kFloat32:
return 4;
case Type::kUint64:
case Type::kInt64:
case Type::kFloat64:
return 8;
case Type::kReference:
return kObjectReferenceSize;
default:
LOG(FATAL) << "Invalid type " << static_cast<int>(type);
return 0;
}
}
static bool IsFloatingPointType(Type type) {
return type == Type::kFloat32 || type == Type::kFloat64;
}
static bool IsIntegralType(Type type) {
// The Java language does not allow treating boolean as an integral type but
// our bit representation makes it safe.
switch (type) {
case Type::kBool:
case Type::kUint8:
case Type::kInt8:
case Type::kUint16:
case Type::kInt16:
case Type::kUint32:
case Type::kInt32:
case Type::kUint64:
case Type::kInt64:
return true;
default:
return false;
}
}
static bool IsIntOrLongType(Type type) {
return type == Type::kInt32 || type == Type::kInt64;
}
static bool Is64BitType(Type type) {
return type == Type::kUint64 || type == Type::kInt64 || type == Type::kFloat64;
}
static bool IsUnsignedType(Type type) {
return type == Type::kBool || type == Type::kUint8 || type == Type::kUint16 ||
type == Type::kUint32 || type == Type::kUint64;
}
// Return the general kind of `type`, fusing integer-like types as Type::kInt.
static Type Kind(Type type) {
switch (type) {
case Type::kBool:
case Type::kUint8:
case Type::kInt8:
case Type::kUint16:
case Type::kInt16:
case Type::kUint32:
case Type::kInt32:
return Type::kInt32;
case Type::kUint64:
case Type::kInt64:
return Type::kInt64;
default:
return type;
}
}
static int64_t MinValueOfIntegralType(Type type) {
switch (type) {
case Type::kBool:
return std::numeric_limits<bool>::min();
case Type::kUint8:
return std::numeric_limits<uint8_t>::min();
case Type::kInt8:
return std::numeric_limits<int8_t>::min();
case Type::kUint16:
return std::numeric_limits<uint16_t>::min();
case Type::kInt16:
return std::numeric_limits<int16_t>::min();
case Type::kUint32:
return std::numeric_limits<uint32_t>::min();
case Type::kInt32:
return std::numeric_limits<int32_t>::min();
case Type::kUint64:
return std::numeric_limits<uint64_t>::min();
case Type::kInt64:
return std::numeric_limits<int64_t>::min();
default:
LOG(FATAL) << "non integral type";
}
return 0;
}
static int64_t MaxValueOfIntegralType(Type type) {
switch (type) {
case Type::kBool:
return std::numeric_limits<bool>::max();
case Type::kUint8:
return std::numeric_limits<uint8_t>::max();
case Type::kInt8:
return std::numeric_limits<int8_t>::max();
case Type::kUint16:
return std::numeric_limits<uint16_t>::max();
case Type::kInt16:
return std::numeric_limits<int16_t>::max();
case Type::kUint32:
return std::numeric_limits<uint32_t>::max();
case Type::kInt32:
return std::numeric_limits<int32_t>::max();
case Type::kUint64:
return std::numeric_limits<uint64_t>::max();
case Type::kInt64:
return std::numeric_limits<int64_t>::max();
default:
LOG(FATAL) << "non integral type";
}
return 0;
}
static bool IsTypeConversionImplicit(Type input_type, Type result_type);
static bool IsTypeConversionImplicit(int64_t value, Type result_type);
static bool IsZeroExtension(Type input_type, Type result_type) {
return IsIntOrLongType(result_type) &&
IsUnsignedType(input_type) &&
Size(result_type) > Size(input_type);
}
static Type ToSigned(Type type) {
switch (type) {
case Type::kUint8:
return Type::kInt8;
case Type::kUint16:
return Type::kInt16;
case Type::kUint32:
return Type::kInt32;
case Type::kUint64:
return Type::kInt64;
default:
return type;
}
}
static Type ToUnsigned(Type type) {
switch (type) {
case Type::kInt8:
return Type::kUint8;
case Type::kInt16:
return Type::kUint16;
case Type::kInt32:
return Type::kUint32;
case Type::kInt64:
return Type::kUint64;
default:
return type;
}
}
static const char* PrettyDescriptor(Type type);
private:
static constexpr size_t kObjectReferenceSize = 4u;
};
std::ostream& operator<<(std::ostream& os, DataType::Type data_type);
// Defined outside DataType to have the operator<< available for DCHECK_NE().
inline bool DataType::IsTypeConversionImplicit(Type input_type, Type result_type) {
DCHECK_NE(DataType::Type::kVoid, result_type);
DCHECK_NE(DataType::Type::kVoid, input_type);
// Invariant: We should never generate a conversion to a Boolean value.
DCHECK_NE(DataType::Type::kBool, result_type);
// Besides conversion to the same type, integral conversions to non-Int64 types
// are implicit if the result value range covers the input value range, i.e.
// widening conversions that do not need to trim the sign bits.
return result_type == input_type ||
(result_type != Type::kInt64 &&
IsIntegralType(input_type) &&
IsIntegralType(result_type) &&
MinValueOfIntegralType(input_type) >= MinValueOfIntegralType(result_type) &&
MaxValueOfIntegralType(input_type) <= MaxValueOfIntegralType(result_type));
}
inline bool DataType::IsTypeConversionImplicit(int64_t value, Type result_type) {
if (IsIntegralType(result_type) && result_type != Type::kInt64) {
// If the constant value falls in the range of the result_type, type
// conversion isn't needed.
return value >= MinValueOfIntegralType(result_type) &&
value <= MaxValueOfIntegralType(result_type);
}
// Conversion isn't implicit if it's into non-integer types, or 64-bit int
// which may have different number of registers.
return false;
}
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_DATA_TYPE_H_