ee529e3451
Change-Id: I67906e25ddaf2fb6650f61dad7c12c96b6367222 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/96821 Commit-Queue: Samir Jindel <sjindel@google.com> Auto-Submit: Alexander Markov <alexmarkov@google.com> Reviewed-by: Samir Jindel <sjindel@google.com>
388 lines
13 KiB
C++
388 lines
13 KiB
C++
// Copyright (c) 2014, 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.
|
|
|
|
#include "vm/compiler/method_recognizer.h"
|
|
|
|
#include "vm/object.h"
|
|
#include "vm/reusable_handles.h"
|
|
#include "vm/symbols.h"
|
|
|
|
namespace dart {
|
|
|
|
MethodRecognizer::Kind MethodRecognizer::RecognizeKind(
|
|
const Function& function) {
|
|
return function.recognized_kind();
|
|
}
|
|
|
|
bool MethodRecognizer::AlwaysInline(const Function& function) {
|
|
return function.always_inline();
|
|
}
|
|
|
|
bool MethodRecognizer::PolymorphicTarget(const Function& function) {
|
|
return function.is_polymorphic_target();
|
|
}
|
|
|
|
intptr_t MethodRecognizer::NumArgsCheckedForStaticCall(
|
|
const Function& function) {
|
|
switch (RecognizeKind(function)) {
|
|
case MethodRecognizer::kDoubleFromInteger:
|
|
case MethodRecognizer::kMathMin:
|
|
case MethodRecognizer::kMathMax:
|
|
return 2;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
intptr_t MethodRecognizer::ResultCidFromPragma(
|
|
const Object& function_or_field) {
|
|
// TODO(vm-team): The caller should only call us if the
|
|
// function_or_field.has_pragma(). If this method turns out to be a
|
|
// performance problem nonetheless, we could consider adding a cache.
|
|
auto T = Thread::Current();
|
|
auto Z = T->zone();
|
|
auto& klass = Class::Handle(Z);
|
|
if (function_or_field.IsFunction()) {
|
|
auto& function = Function::Cast(function_or_field);
|
|
ASSERT(function.has_pragma());
|
|
klass = function.Owner();
|
|
} else {
|
|
auto& field = Field::Cast(function_or_field);
|
|
ASSERT(field.has_pragma());
|
|
klass = field.Owner();
|
|
}
|
|
auto& library = Library::Handle(Z, klass.library());
|
|
const bool can_use_pragma = library.IsAnyCoreLibrary();
|
|
if (can_use_pragma) {
|
|
auto& option = Object::Handle(Z);
|
|
if (library.FindPragma(T, function_or_field,
|
|
Symbols::vm_exact_result_type(), &option)) {
|
|
if (option.IsType()) {
|
|
return Type::Cast(option).type_class_id();
|
|
} else if (option.IsString()) {
|
|
auto& str = String::Cast(option);
|
|
// 'str' should match the pattern '([^#]+)#([^#\?]+)' where group 1
|
|
// is the library URI and group 2 is the class name.
|
|
bool parse_failure = false;
|
|
intptr_t library_end = -1;
|
|
for (intptr_t i = 0; i < str.Length(); ++i) {
|
|
if (str.CharAt(i) == '#') {
|
|
if (library_end != -1) {
|
|
parse_failure = true;
|
|
break;
|
|
} else {
|
|
library_end = i;
|
|
}
|
|
}
|
|
}
|
|
if (!parse_failure && library_end > 0) {
|
|
auto& tmp = String::Handle(Z);
|
|
tmp = String::SubString(str, 0, library_end, Heap::kOld);
|
|
library = Library::LookupLibrary(Thread::Current(), tmp);
|
|
if (!library.IsNull()) {
|
|
tmp = String::SubString(str, library_end + 1,
|
|
str.Length() - library_end - 1, Heap::kOld);
|
|
klass = library.LookupClassAllowPrivate(tmp);
|
|
if (!klass.IsNull()) {
|
|
return klass.id();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return kDynamicCid;
|
|
}
|
|
|
|
bool MethodRecognizer::HasNonNullableResultTypeFromPragma(
|
|
const Object& function_or_field) {
|
|
auto T = Thread::Current();
|
|
auto Z = T->zone();
|
|
auto& klass = Class::Handle(Z);
|
|
if (function_or_field.IsFunction()) {
|
|
auto& function = Function::Cast(function_or_field);
|
|
ASSERT(function.has_pragma());
|
|
klass = function.Owner();
|
|
} else {
|
|
auto& field = Field::Cast(function_or_field);
|
|
ASSERT(field.has_pragma());
|
|
klass = field.Owner();
|
|
}
|
|
auto& library = Library::Handle(Z, klass.library());
|
|
const bool can_use_pragma = library.IsAnyCoreLibrary();
|
|
if (can_use_pragma) {
|
|
auto& option = Object::Handle(Z);
|
|
if (library.FindPragma(T, function_or_field,
|
|
Symbols::vm_non_nullable_result_type(), &option)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// If nothing said otherwise, the return type is nullable.
|
|
return false;
|
|
}
|
|
|
|
intptr_t MethodRecognizer::MethodKindToReceiverCid(Kind kind) {
|
|
switch (kind) {
|
|
case kImmutableArrayGetIndexed:
|
|
return kImmutableArrayCid;
|
|
|
|
case kObjectArrayGetIndexed:
|
|
case kObjectArraySetIndexed:
|
|
case kObjectArraySetIndexedUnchecked:
|
|
return kArrayCid;
|
|
|
|
case kGrowableArrayGetIndexed:
|
|
case kGrowableArraySetIndexed:
|
|
case kGrowableArraySetIndexedUnchecked:
|
|
return kGrowableObjectArrayCid;
|
|
|
|
case kFloat32ArrayGetIndexed:
|
|
case kFloat32ArraySetIndexed:
|
|
return kTypedDataFloat32ArrayCid;
|
|
|
|
case kFloat64ArrayGetIndexed:
|
|
case kFloat64ArraySetIndexed:
|
|
return kTypedDataFloat64ArrayCid;
|
|
|
|
case kInt8ArrayGetIndexed:
|
|
case kInt8ArraySetIndexed:
|
|
return kTypedDataInt8ArrayCid;
|
|
|
|
case kUint8ArrayGetIndexed:
|
|
case kUint8ArraySetIndexed:
|
|
return kTypedDataUint8ArrayCid;
|
|
|
|
case kUint8ClampedArrayGetIndexed:
|
|
case kUint8ClampedArraySetIndexed:
|
|
return kTypedDataUint8ClampedArrayCid;
|
|
|
|
case kExternalUint8ArrayGetIndexed:
|
|
case kExternalUint8ArraySetIndexed:
|
|
return kExternalTypedDataUint8ArrayCid;
|
|
|
|
case kExternalUint8ClampedArrayGetIndexed:
|
|
case kExternalUint8ClampedArraySetIndexed:
|
|
return kExternalTypedDataUint8ClampedArrayCid;
|
|
|
|
case kInt16ArrayGetIndexed:
|
|
case kInt16ArraySetIndexed:
|
|
return kTypedDataInt16ArrayCid;
|
|
|
|
case kUint16ArrayGetIndexed:
|
|
case kUint16ArraySetIndexed:
|
|
return kTypedDataUint16ArrayCid;
|
|
|
|
case kInt32ArrayGetIndexed:
|
|
case kInt32ArraySetIndexed:
|
|
return kTypedDataInt32ArrayCid;
|
|
|
|
case kUint32ArrayGetIndexed:
|
|
case kUint32ArraySetIndexed:
|
|
return kTypedDataUint32ArrayCid;
|
|
|
|
case kInt64ArrayGetIndexed:
|
|
case kInt64ArraySetIndexed:
|
|
return kTypedDataInt64ArrayCid;
|
|
|
|
case kUint64ArrayGetIndexed:
|
|
case kUint64ArraySetIndexed:
|
|
return kTypedDataUint64ArrayCid;
|
|
|
|
case kFloat32x4ArrayGetIndexed:
|
|
case kFloat32x4ArraySetIndexed:
|
|
return kTypedDataFloat32x4ArrayCid;
|
|
|
|
case kInt32x4ArrayGetIndexed:
|
|
case kInt32x4ArraySetIndexed:
|
|
return kTypedDataInt32x4ArrayCid;
|
|
|
|
case kFloat64x2ArrayGetIndexed:
|
|
case kFloat64x2ArraySetIndexed:
|
|
return kTypedDataFloat64x2ArrayCid;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
UNREACHABLE();
|
|
return kIllegalCid;
|
|
}
|
|
|
|
#define KIND_TO_STRING(class_name, function_name, enum_name, fp) #enum_name,
|
|
static const char* recognized_list_method_name[] = {
|
|
"Unknown", RECOGNIZED_LIST(KIND_TO_STRING)};
|
|
#undef KIND_TO_STRING
|
|
|
|
const char* MethodRecognizer::KindToCString(Kind kind) {
|
|
if (kind > kUnknown && kind < kNumRecognizedMethods)
|
|
return recognized_list_method_name[kind];
|
|
return "?";
|
|
}
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
void MethodRecognizer::InitializeState() {
|
|
GrowableArray<Library*> libs(3);
|
|
Libraries(&libs);
|
|
Function& func = Function::Handle();
|
|
|
|
#define SET_RECOGNIZED_KIND(class_name, function_name, enum_name, fp) \
|
|
func = Library::GetFunction(libs, #class_name, #function_name); \
|
|
if (!func.IsNull()) { \
|
|
CHECK_FINGERPRINT3(func, class_name, function_name, enum_name, fp); \
|
|
func.set_recognized_kind(k##enum_name); \
|
|
} else if (!FLAG_precompiled_mode) { \
|
|
OS::PrintErr("Missing %s::%s\n", #class_name, #function_name); \
|
|
UNREACHABLE(); \
|
|
}
|
|
|
|
RECOGNIZED_LIST(SET_RECOGNIZED_KIND);
|
|
|
|
#define SET_FUNCTION_BIT(class_name, function_name, dest, fp, setter, value) \
|
|
func = Library::GetFunction(libs, #class_name, #function_name); \
|
|
if (!func.IsNull()) { \
|
|
CHECK_FINGERPRINT3(func, class_name, function_name, dest, fp); \
|
|
func.setter(value); \
|
|
} else if (!FLAG_precompiled_mode) { \
|
|
OS::PrintErr("Missing %s::%s\n", #class_name, #function_name); \
|
|
UNREACHABLE(); \
|
|
}
|
|
|
|
#define SET_IS_ALWAYS_INLINE(class_name, function_name, dest, fp) \
|
|
SET_FUNCTION_BIT(class_name, function_name, dest, fp, set_always_inline, true)
|
|
|
|
#define SET_IS_NEVER_INLINE(class_name, function_name, dest, fp) \
|
|
SET_FUNCTION_BIT(class_name, function_name, dest, fp, set_is_inlinable, false)
|
|
|
|
#define SET_IS_POLYMORPHIC_TARGET(class_name, function_name, dest, fp) \
|
|
SET_FUNCTION_BIT(class_name, function_name, dest, fp, \
|
|
set_is_polymorphic_target, true)
|
|
|
|
INLINE_WHITE_LIST(SET_IS_ALWAYS_INLINE);
|
|
INLINE_BLACK_LIST(SET_IS_NEVER_INLINE);
|
|
POLYMORPHIC_TARGET_LIST(SET_IS_POLYMORPHIC_TARGET);
|
|
|
|
#undef SET_RECOGNIZED_KIND
|
|
#undef SET_IS_ALWAYS_INLINE
|
|
#undef SET_IS_POLYMORPHIC_TARGET
|
|
#undef SET_FUNCTION_BIT
|
|
}
|
|
|
|
void MethodRecognizer::Libraries(GrowableArray<Library*>* libs) {
|
|
libs->Add(&Library::ZoneHandle(Library::CoreLibrary()));
|
|
libs->Add(&Library::ZoneHandle(Library::CollectionLibrary()));
|
|
libs->Add(&Library::ZoneHandle(Library::MathLibrary()));
|
|
libs->Add(&Library::ZoneHandle(Library::TypedDataLibrary()));
|
|
libs->Add(&Library::ZoneHandle(Library::InternalLibrary()));
|
|
libs->Add(&Library::ZoneHandle(Library::DeveloperLibrary()));
|
|
libs->Add(&Library::ZoneHandle(Library::AsyncLibrary()));
|
|
}
|
|
|
|
RawGrowableObjectArray* MethodRecognizer::QueryRecognizedMethods(Zone* zone) {
|
|
const GrowableObjectArray& methods =
|
|
GrowableObjectArray::Handle(zone, GrowableObjectArray::New());
|
|
Function& func = Function::Handle(zone);
|
|
|
|
GrowableArray<Library*> libs(3);
|
|
Libraries(&libs);
|
|
|
|
#define ADD_RECOGNIZED_METHOD(class_name, function_name, enum_name, fp) \
|
|
func = Library::GetFunction(libs, #class_name, #function_name); \
|
|
methods.Add(func);
|
|
|
|
RECOGNIZED_LIST(ADD_RECOGNIZED_METHOD);
|
|
#undef ADD_RECOGNIZED_METHOD
|
|
|
|
return methods.raw();
|
|
}
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
Token::Kind MethodTokenRecognizer::RecognizeTokenKind(const String& name_) {
|
|
Thread* thread = Thread::Current();
|
|
REUSABLE_STRING_HANDLESCOPE(thread);
|
|
String& name = thread->StringHandle();
|
|
name = name_.raw();
|
|
ASSERT(name.IsSymbol());
|
|
if (Function::IsDynamicInvocationForwarderName(name)) {
|
|
name = Function::DemangleDynamicInvocationForwarderName(name);
|
|
}
|
|
if (name.raw() == Symbols::Plus().raw()) {
|
|
return Token::kADD;
|
|
} else if (name.raw() == Symbols::Minus().raw()) {
|
|
return Token::kSUB;
|
|
} else if (name.raw() == Symbols::Star().raw()) {
|
|
return Token::kMUL;
|
|
} else if (name.raw() == Symbols::Slash().raw()) {
|
|
return Token::kDIV;
|
|
} else if (name.raw() == Symbols::TruncDivOperator().raw()) {
|
|
return Token::kTRUNCDIV;
|
|
} else if (name.raw() == Symbols::Percent().raw()) {
|
|
return Token::kMOD;
|
|
} else if (name.raw() == Symbols::BitOr().raw()) {
|
|
return Token::kBIT_OR;
|
|
} else if (name.raw() == Symbols::Ampersand().raw()) {
|
|
return Token::kBIT_AND;
|
|
} else if (name.raw() == Symbols::Caret().raw()) {
|
|
return Token::kBIT_XOR;
|
|
} else if (name.raw() == Symbols::LeftShiftOperator().raw()) {
|
|
return Token::kSHL;
|
|
} else if (name.raw() == Symbols::RightShiftOperator().raw()) {
|
|
return Token::kSHR;
|
|
} else if (name.raw() == Symbols::Tilde().raw()) {
|
|
return Token::kBIT_NOT;
|
|
} else if (name.raw() == Symbols::UnaryMinus().raw()) {
|
|
return Token::kNEGATE;
|
|
} else if (name.raw() == Symbols::EqualOperator().raw()) {
|
|
return Token::kEQ;
|
|
} else if (name.raw() == Symbols::Token(Token::kNE).raw()) {
|
|
return Token::kNE;
|
|
} else if (name.raw() == Symbols::LAngleBracket().raw()) {
|
|
return Token::kLT;
|
|
} else if (name.raw() == Symbols::RAngleBracket().raw()) {
|
|
return Token::kGT;
|
|
} else if (name.raw() == Symbols::LessEqualOperator().raw()) {
|
|
return Token::kLTE;
|
|
} else if (name.raw() == Symbols::GreaterEqualOperator().raw()) {
|
|
return Token::kGTE;
|
|
} else if (Field::IsGetterName(name)) {
|
|
return Token::kGET;
|
|
} else if (Field::IsSetterName(name)) {
|
|
return Token::kSET;
|
|
}
|
|
return Token::kILLEGAL;
|
|
}
|
|
|
|
#define RECOGNIZE_FACTORY(symbol, class_name, constructor_name, cid, fp) \
|
|
{Symbols::k##symbol##Id, cid, fp, #symbol ", " #cid}, // NOLINT
|
|
|
|
static struct {
|
|
intptr_t symbol_id;
|
|
intptr_t cid;
|
|
intptr_t finger_print;
|
|
const char* name;
|
|
} factory_recognizer_list[] = {RECOGNIZED_LIST_FACTORY_LIST(RECOGNIZE_FACTORY){
|
|
Symbols::kIllegal, -1, -1, NULL}};
|
|
|
|
#undef RECOGNIZE_FACTORY
|
|
|
|
intptr_t FactoryRecognizer::ResultCid(const Function& factory) {
|
|
ASSERT(factory.IsFactory());
|
|
const Class& function_class = Class::Handle(factory.Owner());
|
|
const Library& lib = Library::Handle(function_class.library());
|
|
ASSERT((lib.raw() == Library::CoreLibrary()) ||
|
|
(lib.raw() == Library::TypedDataLibrary()));
|
|
const String& factory_name = String::Handle(factory.name());
|
|
for (intptr_t i = 0;
|
|
factory_recognizer_list[i].symbol_id != Symbols::kIllegal; i++) {
|
|
if (String::EqualsIgnoringPrivateKey(
|
|
factory_name,
|
|
Symbols::Symbol(factory_recognizer_list[i].symbol_id))) {
|
|
return factory_recognizer_list[i].cid;
|
|
}
|
|
}
|
|
return kDynamicCid;
|
|
}
|
|
|
|
} // namespace dart
|