b951498903
Static methods and constructors may receive unboxed parameters. This change adds a bitmap to the RawFunction object indicating which parameters are unboxed and their types. Also, the stack frame bitmap was updated to include new bits for those parameters. The ArgumentsDescriptor object was updated to store not only the number of arguments, but also their size, since 32-bit platforms would use two words for each int64 and double value. In addition, static methods that return non-nullable integers and doubles may return these values unboxed. Dart AOT x64: TypedData.Uint8ClampedListViewVarBench +11.73% DeltaBlue +9.32% Meteor (Intel Xeon) +4.64% Flutter release android-armv8: velocity_tracker_bench -2.32% (less is better) flutter_gallery_readonlydata_size +0.18% flutter_gallery_total_size +0.13% flutter_gallery_instructions_size +0.12% Issue https://github.com/dart-lang/sdk/issues/33549 Change-Id: I97e310f8f30213920af2333fbdedf193d03db5c3 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/135313 Commit-Queue: Victor Agnez Lima <victoragnez@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
371 lines
12 KiB
C++
371 lines
12 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 {
|
|
|
|
intptr_t MethodRecognizer::NumArgsCheckedForStaticCall(
|
|
const Function& function) {
|
|
switch (function.recognized_kind()) {
|
|
case MethodRecognizer::kDoubleFromInteger:
|
|
case MethodRecognizer::kMathMin:
|
|
case MethodRecognizer::kMathMax:
|
|
return 2;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
intptr_t MethodRecognizer::ResultCidFromPragma(
|
|
const Object& function_or_field) {
|
|
auto T = Thread::Current();
|
|
auto Z = T->zone();
|
|
auto& option = Object::Handle(Z);
|
|
if (Library::FindPragma(T, /*only_core=*/true, 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(String::SubString(str, 0, library_end, Heap::kOld));
|
|
const auto& library = Library::Handle(Library::LookupLibrary(T, tmp));
|
|
if (!library.IsNull()) {
|
|
tmp = String::SubString(str, library_end + 1,
|
|
str.Length() - library_end - 1, Heap::kOld);
|
|
const auto& klass =
|
|
Class::Handle(library.LookupClassAllowPrivate(tmp));
|
|
if (!klass.IsNull()) {
|
|
return klass.id();
|
|
}
|
|
}
|
|
}
|
|
} else if (option.IsArray()) {
|
|
const Array& array = Array::Cast(option);
|
|
if (array.Length() > 0) {
|
|
const Object& type = Object::Handle(Array::Cast(option).At(0));
|
|
if (type.IsType()) {
|
|
return Type::Cast(type).type_class_id();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return kDynamicCid;
|
|
}
|
|
|
|
bool MethodRecognizer::HasNonNullableResultTypeFromPragma(
|
|
const Object& function_or_field) {
|
|
auto T = Thread::Current();
|
|
auto Z = T->zone();
|
|
auto& option = Object::Handle(Z);
|
|
if (Library::FindPragma(T, /*only_core=*/true, 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;
|
|
}
|
|
|
|
static const struct {
|
|
const char* const class_name;
|
|
const char* const function_name;
|
|
const char* const enum_name;
|
|
const uint32_t fp;
|
|
} recognized_methods[MethodRecognizer::kNumRecognizedMethods] = {
|
|
{"", "", "Unknown", 0},
|
|
#define RECOGNIZE_METHOD(class_name, function_name, enum_name, fp) \
|
|
{"" #class_name, "" #function_name, #enum_name, fp},
|
|
RECOGNIZED_LIST(RECOGNIZE_METHOD)
|
|
#undef RECOGNIZE_METHOD
|
|
};
|
|
|
|
const char* MethodRecognizer::KindToCString(Kind kind) {
|
|
if (kind >= kUnknown && kind < kNumRecognizedMethods)
|
|
return recognized_methods[kind].enum_name;
|
|
return "?";
|
|
}
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
void MethodRecognizer::InitializeState() {
|
|
GrowableArray<Library*> libs(3);
|
|
Libraries(&libs);
|
|
Function& func = Function::Handle();
|
|
|
|
for (intptr_t i = 1; i < MethodRecognizer::kNumRecognizedMethods; i++) {
|
|
const MethodRecognizer::Kind kind = static_cast<MethodRecognizer::Kind>(i);
|
|
func = Library::GetFunction(libs, recognized_methods[i].class_name,
|
|
recognized_methods[i].function_name);
|
|
if (!func.IsNull()) {
|
|
CHECK_FINGERPRINT3(func, recognized_methods[i].class_name,
|
|
recognized_methods[i].function_name,
|
|
recognized_methods[i].enum_name,
|
|
recognized_methods[i].fp);
|
|
func.set_recognized_kind(kind);
|
|
switch (kind) {
|
|
#define RECOGNIZE_METHOD(class_name, function_name, enum_name, fp) \
|
|
case MethodRecognizer::k##enum_name: \
|
|
func.reset_unboxed_parameters_and_return(); \
|
|
break;
|
|
ALL_INTRINSICS_LIST(RECOGNIZE_METHOD)
|
|
#undef RECOGNIZE_METHOD
|
|
default:
|
|
break;
|
|
}
|
|
} else if (!FLAG_precompiled_mode) {
|
|
FATAL2("Missing %s::%s\n", recognized_methods[i].class_name,
|
|
recognized_methods[i].function_name);
|
|
}
|
|
}
|
|
|
|
#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_POLYMORPHIC_TARGET(class_name, function_name, dest, fp) \
|
|
SET_FUNCTION_BIT(class_name, function_name, dest, fp, \
|
|
set_is_polymorphic_target, true)
|
|
|
|
POLYMORPHIC_TARGET_LIST(SET_IS_POLYMORPHIC_TARGET);
|
|
|
|
#undef SET_RECOGNIZED_KIND
|
|
#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()));
|
|
libs->Add(&Library::ZoneHandle(Library::FfiLibrary()));
|
|
}
|
|
#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 {
|
|
const intptr_t symbol_id;
|
|
const intptr_t cid;
|
|
const uint32_t finger_print;
|
|
const char* const name;
|
|
} factory_recognizer_list[] = {RECOGNIZED_LIST_FACTORY_LIST(RECOGNIZE_FACTORY){
|
|
Symbols::kIllegal, -1, 0, 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;
|
|
}
|
|
|
|
intptr_t FactoryRecognizer::GetResultCidOfListFactory(Zone* zone,
|
|
const Function& function,
|
|
intptr_t argument_count) {
|
|
if (!function.IsFactory()) {
|
|
return kDynamicCid;
|
|
}
|
|
|
|
const Class& owner = Class::Handle(zone, function.Owner());
|
|
if ((owner.library() != Library::CoreLibrary()) &&
|
|
(owner.library() != Library::TypedDataLibrary())) {
|
|
return kDynamicCid;
|
|
}
|
|
|
|
if ((owner.Name() == Symbols::List().raw()) &&
|
|
(function.name() == Symbols::ListFactory().raw())) {
|
|
ASSERT(argument_count == 1 || argument_count == 2);
|
|
return (argument_count == 1) ? kGrowableObjectArrayCid : kArrayCid;
|
|
}
|
|
|
|
return ResultCid(function);
|
|
}
|
|
|
|
} // namespace dart
|