ART: Improve RegType::IsAssignableFrom
Change the chained if-else-if to a switch by introducing an
AssignmentType enum and corresponding getter in RegType.
Reduces instruction count from 39.4B to 39.3B and branch count
from 9.6B to 9.5B when compiling a well-known large app with
the verify compiler filter.
Bug: 10921004
Test: m test-art-host
Change-Id: I2d72df78f281715015829ff555a3c4c2446c03be
diff --git a/runtime/verifier/reg_type-inl.h b/runtime/verifier/reg_type-inl.h
index aa4a259..9245828 100644
--- a/runtime/verifier/reg_type-inl.h
+++ b/runtime/verifier/reg_type-inl.h
@@ -71,59 +71,62 @@
if (lhs.Equals(rhs)) {
return true;
} else {
- if (lhs.IsBoolean()) {
- return rhs.IsBooleanTypes();
- } else if (lhs.IsByte()) {
- return rhs.IsByteTypes();
- } else if (lhs.IsShort()) {
- return rhs.IsShortTypes();
- } else if (lhs.IsChar()) {
- return rhs.IsCharTypes();
- } else if (lhs.IsInteger()) {
- return rhs.IsIntegralTypes();
- } else if (lhs.IsFloat()) {
- return rhs.IsFloatTypes();
- } else if (lhs.IsLongLo()) {
- return rhs.IsLongTypes();
- } else if (lhs.IsDoubleLo()) {
- return rhs.IsDoubleTypes();
- } else if (lhs.IsConflict()) {
- LOG(WARNING) << "RegType::AssignableFrom lhs is Conflict!";
- return false;
- } else {
- CHECK(lhs.IsReferenceTypes())
- << "Unexpected register type in IsAssignableFrom: '"
- << lhs << "' := '" << rhs << "'";
- if (rhs.IsZero()) {
- return true; // All reference types can be assigned null.
- } else if (!rhs.IsReferenceTypes()) {
- return false; // Expect rhs to be a reference type.
- } else if (lhs.IsUninitializedTypes() || rhs.IsUninitializedTypes()) {
- // Uninitialized types are only allowed to be assigned to themselves.
- // TODO: Once we have a proper "reference" super type, this needs to be extended.
+ switch (lhs.GetAssignmentType()) {
+ case AssignmentType::kBoolean:
+ return rhs.IsBooleanTypes();
+ case AssignmentType::kByte:
+ return rhs.IsByteTypes();
+ case AssignmentType::kShort:
+ return rhs.IsShortTypes();
+ case AssignmentType::kChar:
+ return rhs.IsCharTypes();
+ case AssignmentType::kInteger:
+ return rhs.IsIntegralTypes();
+ case AssignmentType::kFloat:
+ return rhs.IsFloatTypes();
+ case AssignmentType::kLongLo:
+ return rhs.IsLongTypes();
+ case AssignmentType::kDoubleLo:
+ return rhs.IsDoubleTypes();
+ case AssignmentType::kConflict:
+ LOG(WARNING) << "RegType::AssignableFrom lhs is Conflict!";
return false;
- } else if (lhs.IsJavaLangObject()) {
- return true; // All reference types can be assigned to Object.
- } else if (!strict && !lhs.IsUnresolvedTypes() && lhs.GetClass()->IsInterface()) {
- // If we're not strict allow assignment to any interface, see comment in ClassJoin.
- return true;
- } else if (lhs.IsJavaLangObjectArray()) {
- return rhs.IsObjectArrayTypes(); // All reference arrays may be assigned to Object[]
- } else if (lhs.HasClass() && rhs.HasClass()) {
- // Test assignability from the Class point-of-view.
- bool result = lhs.GetClass()->IsAssignableFrom(rhs.GetClass());
- // Record assignability dependency. The `verifier` is null during unit tests and
- // VerifiedMethod::GenerateSafeCastSet.
- if (verifier != nullptr) {
- VerifierDeps::MaybeRecordAssignability(
- verifier->GetDexFile(), lhs.GetClass(), rhs.GetClass(), strict, result);
+ case AssignmentType::kReference:
+ if (rhs.IsZero()) {
+ return true; // All reference types can be assigned null.
+ } else if (!rhs.IsReferenceTypes()) {
+ return false; // Expect rhs to be a reference type.
+ } else if (lhs.IsUninitializedTypes() || rhs.IsUninitializedTypes()) {
+ // Uninitialized types are only allowed to be assigned to themselves.
+ // TODO: Once we have a proper "reference" super type, this needs to be extended.
+ return false;
+ } else if (lhs.IsJavaLangObject()) {
+ return true; // All reference types can be assigned to Object.
+ } else if (!strict && !lhs.IsUnresolvedTypes() && lhs.GetClass()->IsInterface()) {
+ // If we're not strict allow assignment to any interface, see comment in ClassJoin.
+ return true;
+ } else if (lhs.IsJavaLangObjectArray()) {
+ return rhs.IsObjectArrayTypes(); // All reference arrays may be assigned to Object[]
+ } else if (lhs.HasClass() && rhs.HasClass()) {
+ // Test assignability from the Class point-of-view.
+ bool result = lhs.GetClass()->IsAssignableFrom(rhs.GetClass());
+ // Record assignability dependency. The `verifier` is null during unit tests and
+ // VerifiedMethod::GenerateSafeCastSet.
+ if (verifier != nullptr) {
+ VerifierDeps::MaybeRecordAssignability(
+ verifier->GetDexFile(), lhs.GetClass(), rhs.GetClass(), strict, result);
+ }
+ return result;
+ } else {
+ // Unresolved types are only assignable for null and equality.
+ return false;
}
- return result;
- } else {
- // Unresolved types are only assignable for null and equality.
- return false;
- }
+ case AssignmentType::kNotAssignable:
+ break;
}
+ LOG(FATAL) << "Unexpected register type in IsAssignableFrom: '"
+ << lhs << "' := '" << rhs << "'";
+ UNREACHABLE();
}
}
diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h
index dedf77f..25baac5 100644
--- a/runtime/verifier/reg_type.h
+++ b/runtime/verifier/reg_type.h
@@ -268,6 +268,52 @@
static void* operator new(size_t size, ArenaAllocator* arena) = delete;
static void* operator new(size_t size, ScopedArenaAllocator* arena);
+ enum class AssignmentType {
+ kBoolean,
+ kByte,
+ kShort,
+ kChar,
+ kInteger,
+ kFloat,
+ kLongLo,
+ kDoubleLo,
+ kConflict,
+ kReference,
+ kNotAssignable,
+ };
+
+ ALWAYS_INLINE
+ inline AssignmentType GetAssignmentType() const {
+ AssignmentType t = GetAssignmentTypeImpl();
+ if (kIsDebugBuild) {
+ if (IsBoolean()) {
+ CHECK(AssignmentType::kBoolean == t);
+ } else if (IsByte()) {
+ CHECK(AssignmentType::kByte == t);
+ } else if (IsShort()) {
+ CHECK(AssignmentType::kShort == t);
+ } else if (IsChar()) {
+ CHECK(AssignmentType::kChar == t);
+ } else if (IsInteger()) {
+ CHECK(AssignmentType::kInteger == t);
+ } else if (IsFloat()) {
+ CHECK(AssignmentType::kFloat == t);
+ } else if (IsLongLo()) {
+ CHECK(AssignmentType::kLongLo == t);
+ } else if (IsDoubleLo()) {
+ CHECK(AssignmentType::kDoubleLo == t);
+ } else if (IsConflict()) {
+ CHECK(AssignmentType::kConflict == t);
+ } else if (IsReferenceTypes()) {
+ CHECK(AssignmentType::kReference == t);
+ } else {
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+ }
+ }
+ return t;
+ }
+
protected:
RegType(mirror::Class* klass,
const StringPiece& descriptor,
@@ -285,6 +331,8 @@
}
}
+ virtual AssignmentType GetAssignmentTypeImpl() const = 0;
+
const StringPiece descriptor_;
mutable GcRoot<mirror::Class> klass_; // Non-const only due to moving classes.
const uint16_t cache_id_;
@@ -341,6 +389,10 @@
// Destroy the singleton instance.
static void Destroy();
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kConflict;
+ }
+
private:
ConflictType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
@@ -372,6 +424,10 @@
// Destroy the singleton instance.
static void Destroy();
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kNotAssignable;
+ }
+
private:
UndefinedType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
@@ -407,6 +463,10 @@
static const IntegerType* GetInstance() PURE;
static void Destroy();
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kInteger;
+ }
+
private:
IntegerType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
@@ -427,6 +487,10 @@
static const BooleanType* GetInstance() PURE;
static void Destroy();
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kBoolean;
+ }
+
private:
BooleanType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
@@ -448,6 +512,10 @@
static const ByteType* GetInstance() PURE;
static void Destroy();
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kByte;
+ }
+
private:
ByteType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
@@ -468,6 +536,10 @@
static const ShortType* GetInstance() PURE;
static void Destroy();
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kShort;
+ }
+
private:
ShortType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
@@ -488,6 +560,10 @@
static const CharType* GetInstance() PURE;
static void Destroy();
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kChar;
+ }
+
private:
CharType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
@@ -508,6 +584,10 @@
static const FloatType* GetInstance() PURE;
static void Destroy();
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kFloat;
+ }
+
private:
FloatType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
@@ -535,6 +615,10 @@
static const LongLoType* GetInstance() PURE;
static void Destroy();
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kLongLo;
+ }
+
private:
LongLoType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
@@ -555,6 +639,10 @@
static const LongHiType* GetInstance() PURE;
static void Destroy();
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kNotAssignable;
+ }
+
private:
LongHiType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
@@ -576,6 +664,10 @@
static const DoubleLoType* GetInstance() PURE;
static void Destroy();
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kDoubleLo;
+ }
+
private:
DoubleLoType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
@@ -596,6 +688,10 @@
static const DoubleHiType* GetInstance() PURE;
static void Destroy();
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kNotAssignable;
+ }
+
private:
DoubleHiType(mirror::Class* klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
@@ -658,6 +754,10 @@
}
virtual bool IsConstantTypes() const OVERRIDE { return true; }
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kNotAssignable;
+ }
+
private:
const uint32_t constant_;
};
@@ -673,6 +773,10 @@
bool IsPreciseConstant() const OVERRIDE { return true; }
std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kNotAssignable;
+ }
};
class PreciseConstLoType FINAL : public ConstantType {
@@ -684,6 +788,10 @@
}
bool IsPreciseConstantLo() const OVERRIDE { return true; }
std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kNotAssignable;
+ }
};
class PreciseConstHiType FINAL : public ConstantType {
@@ -695,6 +803,10 @@
}
bool IsPreciseConstantHi() const OVERRIDE { return true; }
std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kNotAssignable;
+ }
};
class ImpreciseConstType FINAL : public ConstantType {
@@ -706,6 +818,10 @@
}
bool IsImpreciseConstant() const OVERRIDE { return true; }
std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kNotAssignable;
+ }
};
class ImpreciseConstLoType FINAL : public ConstantType {
@@ -717,6 +833,10 @@
}
bool IsImpreciseConstantLo() const OVERRIDE { return true; }
std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kNotAssignable;
+ }
};
class ImpreciseConstHiType FINAL : public ConstantType {
@@ -728,6 +848,10 @@
}
bool IsImpreciseConstantHi() const OVERRIDE { return true; }
std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kNotAssignable;
+ }
};
// Common parent of all uninitialized types. Uninitialized types are created by
@@ -747,6 +871,10 @@
return allocation_pc_;
}
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kReference;
+ }
+
private:
const uint32_t allocation_pc_;
};
@@ -848,6 +976,10 @@
bool HasClassVirtual() const OVERRIDE { return true; }
std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kReference;
+ }
};
// A type of register holding a reference to an Object of type GetClass and only
@@ -866,6 +998,10 @@
bool HasClassVirtual() const OVERRIDE { return true; }
std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kReference;
+ }
};
// Common parent of unresolved types.
@@ -876,6 +1012,10 @@
: RegType(nullptr, descriptor, cache_id) {}
bool IsNonZeroReferenceTypes() const OVERRIDE;
+
+ AssignmentType GetAssignmentTypeImpl() const OVERRIDE {
+ return AssignmentType::kReference;
+ }
};
// Similar to ReferenceType except the Class couldn't be loaded. Assignability