// 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