blob: 4b6bffdcc214616e4f315122930d01145111f4c4 [file] [log] [blame]
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_VM_COMPILER_BACKEND_COMPILE_TYPE_H_
#define RUNTIME_VM_COMPILER_BACKEND_COMPILE_TYPE_H_
#if defined(DART_PRECOMPILED_RUNTIME)
#error "AOT runtime should not use compiler sources (including header files)"
#endif // defined(DART_PRECOMPILED_RUNTIME)
#include "vm/allocation.h"
#include "vm/class_id.h"
#include "vm/compiler/runtime_api.h"
namespace dart {
class AbstractType;
class BaseTextBuffer;
class Definition;
template <typename T>
class GrowableArray;
// CompileType describes type of a value produced by a definition.
//
// It captures the following properties:
// - whether the value can potentially be null or if it is definitely not
// null;
// - whether the value can potentially be sentinel or if it is definitely
// not sentinel;
// - concrete class id of the value or kDynamicCid if unknown statically;
// - abstract super type of the value, where the concrete type of the value
// in runtime is guaranteed to be sub type of this type.
//
// Values of CompileType form a lattice with a None type as a bottom and a
// nullable Dynamic type as a top element. Method Union provides a join
// operation for the lattice.
class CompileType : public ZoneAllocated {
public:
static constexpr bool kCanBeNull = true;
static constexpr bool kCannotBeNull = false;
static constexpr bool kCanBeSentinel = true;
static constexpr bool kCannotBeSentinel = false;
CompileType(bool can_be_null,
bool can_be_sentinel,
intptr_t cid,
const AbstractType* type)
: can_be_null_(can_be_null),
can_be_sentinel_(can_be_sentinel),
cid_(cid),
type_(type) {}
CompileType(const CompileType& other)
: ZoneAllocated(),
can_be_null_(other.can_be_null_),
can_be_sentinel_(other.can_be_sentinel_),
cid_(other.cid_),
type_(other.type_) {}
CompileType& operator=(const CompileType& other) {
// This intentionally does not change the owner of this type.
can_be_null_ = other.can_be_null_;
can_be_sentinel_ = other.can_be_sentinel_;
cid_ = other.cid_;
type_ = other.type_;
return *this;
}
bool is_nullable() const { return can_be_null_; }
// Return true if value of this type can be Object::sentinel().
// Such values cannot be unboxed.
bool can_be_sentinel() const { return can_be_sentinel_; }
// Return type such that concrete value's type in runtime is guaranteed to
// be subtype of it.
const AbstractType* ToAbstractType();
// Return class id such that it is either kDynamicCid or in runtime
// value is guaranteed to have an equal class id.
intptr_t ToCid();
// Return class id such that it is either kDynamicCid or in runtime
// value is guaranteed to be either null or have an equal class id.
intptr_t ToNullableCid();
// Return true if the value is guaranteed to be not-null or is known to be
// always null.
bool HasDecidableNullability();
// Return true if the value is known to be always null.
bool IsNull();
// Return true if this type is a subtype of the given type.
bool IsSubtypeOf(const AbstractType& other);
// Return true if value of this type is assignable to a location of the
// given type.
bool IsAssignableTo(const AbstractType& other);
// Return true if value of this type always passes 'is' test
// against given type.
bool IsInstanceOf(const AbstractType& other);
// Return the non-nullable version of this type.
CompileType CopyNonNullable() {
if (IsNull()) {
// Represent a non-nullable null type (typically arising for
// unreachable values) as None.
return None();
}
return CompileType(kCannotBeNull, can_be_sentinel_, cid_, type_);
}
// Return the non-sentinel version of this type.
CompileType CopyNonSentinel() {
return CompileType(can_be_null_, kCannotBeSentinel, cid_, type_);
}
// Create a new CompileType representing given abstract type.
// By default nullability of values is determined by type.
// CompileType can be further constrained to non-nullable values by
// passing kCannotBeNull as |can_be_null| parameter.
static CompileType FromAbstractType(const AbstractType& type,
bool can_be_null,
bool can_be_sentinel);
// Create a new CompileType representing a value with the given class id.
// Resulting CompileType can be null only if cid is kDynamicCid or kNullCid.
// Resulting CompileType can be sentinel only if cid is kDynamicCid or
// kSentinelCid.
static CompileType FromCid(intptr_t cid);
// Create None CompileType. It is the bottom of the lattice and is used to
// represent type of the phi that was not yet inferred.
static CompileType None() {
return CompileType(kCanBeNull, kCanBeSentinel, kIllegalCid, nullptr);
}
// Create Dynamic CompileType. It is the top of the lattice and is used to
// represent unknown type.
static CompileType Dynamic();
static CompileType Null();
// Create non-nullable Bool type.
static CompileType Bool();
// Create non-nullable Int type.
static CompileType Int();
// Create non-nullable 32-bit Int type (arch dependent).
static CompileType Int32();
// Create nullable Int type.
static CompileType NullableInt();
// Create non-nullable Smi type.
static CompileType Smi();
// Create nullable Smi type.
static CompileType NullableSmi() {
return CompileType(kCanBeNull, kCannotBeSentinel, kSmiCid, nullptr);
}
// Create nullable Mint type.
static CompileType NullableMint() {
return CompileType(kCanBeNull, kCannotBeSentinel, kMintCid, nullptr);
}
// Create non-nullable Double type.
static CompileType Double();
// Create nullable Double type.
static CompileType NullableDouble();
// Create non-nullable String type.
static CompileType String();
// Perform a join operation over the type lattice.
void Union(CompileType* other);
// Refine old type with newly inferred type (it could be more or less
// specific, or even unrelated to an old type in case of unreachable code).
// May return 'old_type', 'new_type' or create a new CompileType instance.
static CompileType* ComputeRefinedType(CompileType* old_type,
CompileType* new_type);
// Return true if this and other types are the same.
bool IsEqualTo(CompileType* other) {
return (can_be_null_ == other->can_be_null_) &&
(can_be_sentinel_ == other->can_be_sentinel_) &&
(ToNullableCid() == other->ToNullableCid()) &&
(compiler::IsEqualType(*ToAbstractType(), *other->ToAbstractType()));
}
bool IsNone() const { return (cid_ == kIllegalCid) && (type_ == nullptr); }
// Return true if value of this type is a non-nullable int.
bool IsInt() { return !is_nullable() && IsNullableInt(); }
// Return true if value of this type is a non-nullable double.
bool IsDouble() { return !is_nullable() && IsNullableDouble(); }
// Return true if value of this type is a non-nullable double.
bool IsBool() { return !is_nullable() && IsNullableBool(); }
// Return true if value of this type is either int or null.
bool IsNullableInt() {
if (cid_ == kSmiCid || cid_ == kMintCid) {
return true;
}
if (cid_ == kIllegalCid || cid_ == kDynamicCid) {
return type_ != nullptr && compiler::IsSubtypeOfInt(*type_);
}
return false;
}
// Returns true if value of this type is either Smi or null.
bool IsNullableSmi() {
if (cid_ == kSmiCid) {
return true;
}
if (cid_ == kIllegalCid || cid_ == kDynamicCid) {
return type_ != nullptr && compiler::IsSmiType(*type_);
}
return false;
}
// Return true if value of this type is either double or null.
bool IsNullableDouble() {
if (cid_ == kDoubleCid) {
return true;
}
if ((cid_ == kIllegalCid) || (cid_ == kDynamicCid)) {
return type_ != nullptr && compiler::IsDoubleType(*type_);
}
return false;
}
// Return true if value of this type is either double or null.
bool IsNullableBool() {
if (cid_ == kBoolCid) {
return true;
}
if ((cid_ == kIllegalCid) || (cid_ == kDynamicCid)) {
return type_ != nullptr && compiler::IsBoolType(*type_);
}
return false;
}
// Returns true if a value of this CompileType can contain a Smi.
// Note that this is not the same as calling
// CompileType::Smi().IsAssignableTo(this) - because this compile type
// can be uninstantiated.
bool CanBeSmi();
bool Specialize(GrowableArray<intptr_t>* class_ids);
void PrintTo(BaseTextBuffer* f) const;
const char* ToCString() const;
// CompileType object might be unowned or owned by a definition.
// Owned CompileType objects can change during type propagation when
// [RecomputeType] is called on the owner. We keep track of which
// definition owns [CompileType] to prevent situations where
// owned [CompileType] is cached as a reaching type in a [Value] which
// is no longer connected to the original owning definition.
// See [Value::SetReachingType].
void set_owner(Definition* owner) { owner_ = owner; }
Definition* owner() const { return owner_; }
private:
bool can_be_null_;
bool can_be_sentinel_;
classid_t cid_;
const AbstractType* type_;
Definition* owner_ = nullptr;
};
} // namespace dart
#endif // RUNTIME_VM_COMPILER_BACKEND_COMPILE_TYPE_H_