| // 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: | |
| return 1; | |
| 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 |