ad95bb6f89
This is a reland of commit a7e2dce0cc
Fixes the crash during code generation when typed data view
LoadIndexed was used on an internal typed data array
(in the unreachable code path) without loading its payload.
TEST=runtime/tests/vm/dart/regress_b_334128316_test.dart
TEST=manually verified repro from b/334128316
Original change's description:
> [vm] Implement typed data view GetIndexed in flow graph builder
>
> Typed data view 'operator []' is now implemented in the flow
> graph builder. This unifies all typed data GetIndexed operations
> and removes unnecessary address calculations.
>
> TEST=ci
>
> Change-Id: I8d2ca6e7c7bf18b2590536a643f92cad5beb6d95
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/361283
> Reviewed-by: Tess Strickland <sstrickl@google.com>
> Commit-Queue: Alexander Markov <alexmarkov@google.com>
> Reviewed-by: Slava Egorov <vegorov@google.com>
Change-Id: I3786fa17e971f8ba6cd01a57f1a68504bf24bc47
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/362821
Reviewed-by: Tess Strickland <sstrickl@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
375 lines
14 KiB
C++
375 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;
|
|
}
|
|
|
|
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(),
|
|
/*multiple=*/false, &option)) {
|
|
return true;
|
|
}
|
|
|
|
// If nothing said otherwise, the return type is nullable.
|
|
return false;
|
|
}
|
|
|
|
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 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 "?";
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void MethodRecognizer::InitializeState() {
|
|
GrowableArray<Library*> libs(3);
|
|
Libraries(&libs);
|
|
Function& func = Function::Handle();
|
|
bool fingerprints_match = true;
|
|
|
|
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()) {
|
|
fingerprints_match =
|
|
func.CheckSourceFingerprint(recognized_methods[i].fp) &&
|
|
fingerprints_match;
|
|
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) {
|
|
fingerprints_match = false;
|
|
OS::PrintErr("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()) { \
|
|
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(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
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
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::ConvertLibrary()));
|
|
libs->Add(&Library::ZoneHandle(Library::InternalLibrary()));
|
|
libs->Add(&Library::ZoneHandle(Library::IsolateLibrary()));
|
|
libs->Add(&Library::ZoneHandle(Library::DeveloperLibrary()));
|
|
libs->Add(&Library::ZoneHandle(Library::AsyncLibrary()));
|
|
libs->Add(&Library::ZoneHandle(Library::FfiLibrary()));
|
|
libs->Add(&Library::ZoneHandle(Library::NativeWrappersLibrary()));
|
|
}
|
|
|
|
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::Token(Token::kNE).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, 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::kIllegal, -1, 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::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().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
|