8779faa131
TEST=ci Change-Id: Ib4aa1a48fdcf47f6e27c21428628d06d4de9fd9e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/490348 Reviewed-by: Alexander Aprelev <aam@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
366 lines
14 KiB
C++
366 lines
14 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/log.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(),
|
|
/*multiple=*/false, &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;
|
|
}
|
|
|
|
intptr_t MethodRecognizer::MethodKindToReceiverCid(Kind kind) {
|
|
switch (kind) {
|
|
case kObjectArrayGetIndexed:
|
|
case kObjectArraySetIndexed:
|
|
case kObjectArraySetIndexedUnchecked:
|
|
return kArrayCid;
|
|
|
|
case kGrowableArrayGetIndexed:
|
|
case kGrowableArraySetIndexed:
|
|
case kGrowableArraySetIndexedUnchecked:
|
|
return kGrowableObjectArrayCid;
|
|
|
|
#define TYPED_DATA_GET_SET_INDEXED_CASES(clazz) \
|
|
case k##clazz##ArrayGetIndexed: \
|
|
case k##clazz##ArraySetIndexed: \
|
|
return kTypedData##clazz##ArrayCid; \
|
|
case kExternal##clazz##ArrayGetIndexed: \
|
|
return kExternalTypedData##clazz##ArrayCid; \
|
|
case k##clazz##ArrayViewGetIndexed: \
|
|
return kTypedData##clazz##ArrayViewCid;
|
|
|
|
DART_CLASS_LIST_TYPED_DATA(TYPED_DATA_GET_SET_INDEXED_CASES);
|
|
#undef TYPED_DATA_GET_SET_INDEXED_CASES
|
|
|
|
case kExternalUint8ArraySetIndexed:
|
|
return kExternalTypedDataUint8ArrayCid;
|
|
|
|
case kExternalUint8ClampedArraySetIndexed:
|
|
return kExternalTypedDataUint8ClampedArrayCid;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
UNREACHABLE();
|
|
return kIllegalCid;
|
|
}
|
|
|
|
static const struct {
|
|
const char* const function_name;
|
|
const char* const enum_name;
|
|
} recognized_methods[MethodRecognizer::kNumRecognizedMethods] = {
|
|
{"", "Unknown"},
|
|
#define RECOGNIZE_METHOD(library, class_name, function_name, enum_name, fp) \
|
|
{"" #function_name, #enum_name},
|
|
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 "?";
|
|
}
|
|
|
|
const char* MethodRecognizer::KindToFunctionNameCString(Kind kind) {
|
|
if (kind >= kUnknown && kind < kNumRecognizedMethods)
|
|
return recognized_methods[kind].function_name;
|
|
return "?";
|
|
}
|
|
|
|
// Is this method marked with the vm:recognized pragma?
|
|
bool MethodRecognizer::IsMarkedAsRecognized(const Function& function,
|
|
const char* kind) {
|
|
const Function* functionp =
|
|
function.IsDynamicInvocationForwarder()
|
|
? &Function::Handle(function.ForwardingTarget())
|
|
: &function;
|
|
Object& options = Object::Handle();
|
|
bool is_recognized = Library::FindPragma(
|
|
Thread::Current(), /*only_core=*/true, *functionp,
|
|
Symbols::vm_recognized(), /*multiple=*/false, &options);
|
|
if (!is_recognized) return false;
|
|
if (kind == nullptr) return true;
|
|
|
|
ASSERT(options.IsString());
|
|
ASSERT(String::Cast(options).Equals("asm-intrinsic") ||
|
|
String::Cast(options).Equals("graph-intrinsic") ||
|
|
String::Cast(options).Equals("other"));
|
|
return String::Cast(options).Equals(kind);
|
|
}
|
|
|
|
static bool IsAssemblerIntrinsic(MethodRecognizer::Kind kind) {
|
|
switch (kind) {
|
|
#define RECOGNIZE_METHOD(library, class_name, function_name, enum_name, fp) \
|
|
case MethodRecognizer::k##enum_name:
|
|
ASM_INTRINSICS_LIST(RECOGNIZE_METHOD)
|
|
#undef RECOGNIZE_METHOD
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool IsGraphIntrinsic(MethodRecognizer::Kind kind) {
|
|
switch (kind) {
|
|
#define RECOGNIZE_METHOD(library, class_name, function_name, enum_name, fp) \
|
|
case MethodRecognizer::k##enum_name:
|
|
GRAPH_INTRINSICS_LIST(RECOGNIZE_METHOD)
|
|
#undef RECOGNIZE_METHOD
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void MethodRecognizer::InitializeState() {
|
|
Library& lib = Library::Handle();
|
|
Function& func = Function::Handle();
|
|
bool fingerprints_match = true;
|
|
|
|
#define RECOGNIZE_METHOD(library, class_name, function_name, enum_name, fp) \
|
|
lib = Library::library(); \
|
|
func = Library::GetFunction(lib, #class_name, #function_name); \
|
|
if (!func.IsNull()) { \
|
|
fingerprints_match = \
|
|
func.CheckSourceFingerprint(fp) && fingerprints_match; \
|
|
func.set_recognized_kind(k##enum_name); \
|
|
if (IsAssemblerIntrinsic(k##enum_name)) { \
|
|
func.reset_unboxed_parameters_and_return(); \
|
|
func.set_is_intrinsic(true); \
|
|
} else if (IsGraphIntrinsic(k##enum_name)) { \
|
|
func.set_is_intrinsic(true); \
|
|
} \
|
|
} else if (!FLAG_precompiled_mode) { \
|
|
fingerprints_match = false; \
|
|
OS::PrintErr("Missing %s %s::%s\n", #library, #class_name, \
|
|
#function_name); \
|
|
}
|
|
RECOGNIZED_LIST(RECOGNIZE_METHOD)
|
|
#undef RECOGNIZE_METHOD
|
|
|
|
#define SET_FUNCTION_BIT(library, class_name, function_name, dest, fp, setter, \
|
|
value) \
|
|
lib = Library::library(); \
|
|
func = Library::GetFunction(lib, #class_name, #function_name); \
|
|
if (!func.IsNull()) { \
|
|
fingerprints_match = \
|
|
func.CheckSourceFingerprint(fp) && fingerprints_match; \
|
|
func.setter(value); \
|
|
} else if (!FLAG_precompiled_mode) { \
|
|
OS::PrintErr("Missing %s::%s\n", #class_name, #function_name); \
|
|
fingerprints_match = false; \
|
|
}
|
|
|
|
#define SET_IS_POLYMORPHIC_TARGET(library, class_name, function_name, dest, \
|
|
fp) \
|
|
SET_FUNCTION_BIT(library, 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
|
|
|
|
if (!fingerprints_match) {
|
|
// Private names are mangled. Mangling depends on Library::private_key_.
|
|
// If registering a new bootstrap library, add at the end.
|
|
FATAL(
|
|
"FP mismatch while recognizing methods. If the behavior of "
|
|
"these functions has changed, then changes are also needed in "
|
|
"the VM's compiler. Otherwise the fingerprint can simply be "
|
|
"updated in recognized_methods_list.h\n");
|
|
}
|
|
}
|
|
|
|
static Token::Kind RecognizeTokenKindHelper(const String& name) {
|
|
if (name.ptr() == Symbols::Plus().ptr()) {
|
|
return Token::kADD;
|
|
} else if (name.ptr() == Symbols::Minus().ptr()) {
|
|
return Token::kSUB;
|
|
} else if (name.ptr() == Symbols::Star().ptr()) {
|
|
return Token::kMUL;
|
|
} else if (name.ptr() == Symbols::Slash().ptr()) {
|
|
return Token::kDIV;
|
|
} else if (name.ptr() == Symbols::TruncDivOperator().ptr()) {
|
|
return Token::kTRUNCDIV;
|
|
} else if (name.ptr() == Symbols::Percent().ptr()) {
|
|
return Token::kMOD;
|
|
} else if (name.ptr() == Symbols::BitOr().ptr()) {
|
|
return Token::kBIT_OR;
|
|
} else if (name.ptr() == Symbols::Ampersand().ptr()) {
|
|
return Token::kBIT_AND;
|
|
} else if (name.ptr() == Symbols::Caret().ptr()) {
|
|
return Token::kBIT_XOR;
|
|
} else if (name.ptr() == Symbols::LeftShiftOperator().ptr()) {
|
|
return Token::kSHL;
|
|
} else if (name.ptr() == Symbols::RightShiftOperator().ptr()) {
|
|
return Token::kSHR;
|
|
} else if (name.ptr() == Symbols::UnsignedRightShiftOperator().ptr()) {
|
|
return Token::kUSHR;
|
|
} else if (name.ptr() == Symbols::Tilde().ptr()) {
|
|
return Token::kBIT_NOT;
|
|
} else if (name.ptr() == Symbols::UnaryMinus().ptr()) {
|
|
return Token::kNEGATE;
|
|
} else if (name.ptr() == Symbols::EqualOperator().ptr()) {
|
|
return Token::kEQ;
|
|
} else if (name.ptr() == Symbols::NotEqualOperator().ptr()) {
|
|
return Token::kNE;
|
|
} else if (name.ptr() == Symbols::LAngleBracket().ptr()) {
|
|
return Token::kLT;
|
|
} else if (name.ptr() == Symbols::RAngleBracket().ptr()) {
|
|
return Token::kGT;
|
|
} else if (name.ptr() == Symbols::LessEqualOperator().ptr()) {
|
|
return Token::kLTE;
|
|
} else if (name.ptr() == Symbols::GreaterEqualOperator().ptr()) {
|
|
return Token::kGTE;
|
|
} else if (Field::IsGetterName(name)) {
|
|
return Token::kGET;
|
|
} else if (Field::IsSetterName(name)) {
|
|
return Token::kSET;
|
|
}
|
|
return Token::kILLEGAL;
|
|
}
|
|
|
|
Token::Kind MethodTokenRecognizer::RecognizeTokenKind(const String& name) {
|
|
ASSERT(name.IsSymbol());
|
|
if (Function::IsDynamicInvocationForwarderName(name)) {
|
|
Thread* thread = Thread::Current();
|
|
const auto& demangled_name = String::Handle(
|
|
thread->zone(), Function::DemangleDynamicInvocationForwarderName(name));
|
|
return RecognizeTokenKindHelper(demangled_name);
|
|
} else {
|
|
return RecognizeTokenKindHelper(name);
|
|
}
|
|
}
|
|
|
|
#define RECOGNIZE_FACTORY(symbol, library, class_name, constructor_name, cid, \
|
|
fp) \
|
|
{Symbols::k##symbol##Id, cid, fp, #symbol ", " #cid}, // NOLINT
|
|
|
|
static const 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::kEmptyId, kIllegalCid, 0, nullptr}};
|
|
|
|
#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.ptr() == Library::CoreLibrary()) ||
|
|
(lib.ptr() == Library::TypedDataLibrary()));
|
|
const String& factory_name = String::Handle(factory.name());
|
|
for (intptr_t i = 0;
|
|
factory_recognizer_list[i].symbol_id != Symbols::kEmptyId; 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().ptr()) {
|
|
if (function.name() == Symbols::ListFactory().ptr()) {
|
|
ASSERT(argument_count == 1 || argument_count == 2);
|
|
return (argument_count == 1) ? kGrowableObjectArrayCid : kArrayCid;
|
|
} else if (function.name() == Symbols::ListFilledFactory().ptr()) {
|
|
ASSERT(argument_count == 3 || argument_count == 4);
|
|
return (argument_count == 3) ? kArrayCid : kDynamicCid;
|
|
}
|
|
}
|
|
|
|
return ResultCid(function);
|
|
}
|
|
|
|
} // namespace dart
|