From 12b33ada442821c9d1ff62ac05d0da98cc5f3bab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Crelier?= Date: Wed, 13 Jun 2018 23:30:44 +0000 Subject: [PATCH] [VM interpreter] Support NativeCall kernel bytecode. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I89c905d17fb48447239ee4b0c1c8c50060239b88 Reviewed-on: https://dart-review.googlesource.com/59560 Commit-Queue: Régis Crelier Reviewed-by: Zach Anderson Reviewed-by: Alexander Markov --- runtime/vm/bootstrap.h | 2 +- runtime/vm/bootstrap_natives.cc | 2 +- runtime/vm/compiler/aot/precompiler.cc | 1 + runtime/vm/compiler/backend/il.cc | 2 +- .../frontend/kernel_binary_flowgraph.cc | 89 +++++++++ .../frontend/kernel_binary_flowgraph.h | 2 + runtime/vm/constants_kbc.h | 8 +- runtime/vm/interpreter.cc | 176 ++++++++++++++++-- runtime/vm/native_entry.cc | 4 +- runtime/vm/native_entry.h | 2 +- runtime/vm/object_store.cc | 6 + runtime/vm/object_store.h | 1 + 12 files changed, 269 insertions(+), 26 deletions(-) diff --git a/runtime/vm/bootstrap.h b/runtime/vm/bootstrap.h index fe782ce7a9d..88e240750b6 100644 --- a/runtime/vm/bootstrap.h +++ b/runtime/vm/bootstrap.h @@ -28,7 +28,7 @@ class Bootstrap : public AllStatic { intptr_t kernel_buffer_size); static void SetupNativeResolver(); - static bool IsBootstapResolver(Dart_NativeEntryResolver resolver); + static bool IsBootstrapResolver(Dart_NativeEntryResolver resolver); // Source path mapping for library URI and 'parts'. static const char* async_source_paths_[]; diff --git a/runtime/vm/bootstrap_natives.cc b/runtime/vm/bootstrap_natives.cc index 0b6b193345a..fb3d08baa42 100644 --- a/runtime/vm/bootstrap_natives.cc +++ b/runtime/vm/bootstrap_natives.cc @@ -136,7 +136,7 @@ void Bootstrap::SetupNativeResolver() { library.set_native_entry_symbol_resolver(symbol_resolver); } -bool Bootstrap::IsBootstapResolver(Dart_NativeEntryResolver resolver) { +bool Bootstrap::IsBootstrapResolver(Dart_NativeEntryResolver resolver) { return (resolver == reinterpret_cast(BootstrapNatives::Lookup)); } diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc index ff286b6d226..fb0068da106 100644 --- a/runtime/vm/compiler/aot/precompiler.cc +++ b/runtime/vm/compiler/aot/precompiler.cc @@ -344,6 +344,7 @@ void Precompiler::DoCompileAll( I->object_store()->set_completer_class(null_class); I->object_store()->set_symbol_class(null_class); I->object_store()->set_compiletime_error_class(null_class); + I->object_store()->set_growable_list_factory(null_function); I->object_store()->set_simple_instance_of_function(null_function); I->object_store()->set_simple_instance_of_true_function(null_function); I->object_store()->set_simple_instance_of_false_function(null_function); diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc index d58201a4c44..9890b36dae6 100644 --- a/runtime/vm/compiler/backend/il.cc +++ b/runtime/vm/compiler/backend/il.cc @@ -4630,7 +4630,7 @@ void NativeCallInstr::SetupNative() { const Library& library = Library::Handle(zone, cls.library()); Dart_NativeEntryResolver resolver = library.native_entry_resolver(); - bool is_bootstrap_native = Bootstrap::IsBootstapResolver(resolver); + bool is_bootstrap_native = Bootstrap::IsBootstrapResolver(resolver); set_is_bootstrap_native(is_bootstrap_native); const int num_params = diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc index b1007196bce..be23f788bd2 100644 --- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc +++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc @@ -3,11 +3,14 @@ // BSD-style license that can be found in the LICENSE file. #include "vm/compiler/frontend/kernel_binary_flowgraph.h" + +#include "vm/bootstrap.h" #include "vm/code_descriptors.h" #include "vm/compiler/aot/precompiler.h" #include "vm/compiler/assembler/disassembler_kbc.h" #include "vm/compiler/frontend/prologue_builder.h" #include "vm/compiler/jit/compiler.h" +#include "vm/dart_entry.h" #include "vm/longjump.h" #include "vm/object_store.h" #include "vm/resolver.h" @@ -994,6 +997,7 @@ intptr_t BytecodeMetadataHelper::ReadPoolEntries(const Function& function, kContextOffset, kClosureFunction, kEndClosureFunctionScope, + kNativeEntry, }; enum InvocationKind { @@ -1332,6 +1336,10 @@ intptr_t BytecodeMetadataHelper::ReadPoolEntries(const Function& function, pool.SetObjectAt(i, obj); return i; // The caller will close the scope. } break; + case ConstantPoolTag::kNativeEntry: { + name = H.DartString(builder_->ReadStringReference()).raw(); + obj = NativeEntry(function, name); + } break; default: UNREACHABLE(); } @@ -1412,6 +1420,87 @@ void BytecodeMetadataHelper::ReadExceptionsTable(const Code& bytecode) { bytecode.set_exception_handlers(Object::empty_exception_handlers()); } } + +RawTypedData* BytecodeMetadataHelper::NativeEntry(const Function& function, + const String& external_name) { + Zone* zone = builder_->zone_; + MethodRecognizer::Kind kind = MethodRecognizer::RecognizeKind(function); + // This list of recognized methods must be kept in sync with the list of + // methods handled specially by the NativeCall bytecode in the interpreter. + switch (kind) { + case MethodRecognizer::kObjectEquals: + case MethodRecognizer::kStringBaseLength: + case MethodRecognizer::kStringBaseIsEmpty: + case MethodRecognizer::kGrowableArrayLength: + case MethodRecognizer::kObjectArrayLength: + case MethodRecognizer::kImmutableArrayLength: + case MethodRecognizer::kTypedDataLength: + case MethodRecognizer::kClassIDgetID: + case MethodRecognizer::kGrowableArrayCapacity: + case MethodRecognizer::kListFactory: + case MethodRecognizer::kObjectArrayAllocate: + case MethodRecognizer::kLinkedHashMap_getIndex: + case MethodRecognizer::kLinkedHashMap_setIndex: + case MethodRecognizer::kLinkedHashMap_getData: + case MethodRecognizer::kLinkedHashMap_setData: + case MethodRecognizer::kLinkedHashMap_getHashMask: + case MethodRecognizer::kLinkedHashMap_setHashMask: + case MethodRecognizer::kLinkedHashMap_getUsedData: + case MethodRecognizer::kLinkedHashMap_setUsedData: + case MethodRecognizer::kLinkedHashMap_getDeletedKeys: + case MethodRecognizer::kLinkedHashMap_setDeletedKeys: + break; + default: + kind = MethodRecognizer::kUnknown; + } + NativeFunctionWrapper trampoline = NULL; + NativeFunction native_function = NULL; + intptr_t argc_tag = 0; + if (kind == MethodRecognizer::kUnknown) { + if (FLAG_link_natives_lazily) { + trampoline = &NativeEntry::BootstrapNativeCallWrapper; + native_function = + reinterpret_cast(&NativeEntry::LinkNativeCall); + } else { + const Class& cls = Class::Handle(zone, function.Owner()); + const Library& library = Library::Handle(zone, cls.library()); + Dart_NativeEntryResolver resolver = library.native_entry_resolver(); + const bool is_bootstrap_native = Bootstrap::IsBootstrapResolver(resolver); + const int num_params = + NativeArguments::ParameterCountForResolution(function); + bool is_auto_scope = true; + native_function = NativeEntry::ResolveNative(library, external_name, + num_params, &is_auto_scope); + ASSERT(native_function != NULL); // TODO(regis): Should we throw instead? + if (is_bootstrap_native) { + trampoline = &NativeEntry::BootstrapNativeCallWrapper; + } else if (is_auto_scope) { + trampoline = &NativeEntry::AutoScopeNativeCallWrapper; + } else { + trampoline = &NativeEntry::NoScopeNativeCallWrapper; + } + } + argc_tag = NativeArguments::ComputeArgcTag(function); + } + // TODO(regis): Introduce a new VM class subclassing Object and containing + // these four untagged values. +#ifdef ARCH_IS_32_BIT + const TypedData& native_entry = TypedData::Handle( + zone, TypedData::New(kTypedDataUint32ArrayCid, 4, Heap::kOld)); + native_entry.SetUint32(0 << 2, static_cast(kind)); + native_entry.SetUint32(1 << 2, reinterpret_cast(trampoline)); + native_entry.SetUint32(2 << 2, reinterpret_cast(native_function)); + native_entry.SetUint32(3 << 2, static_cast(argc_tag)); +#else + const TypedData& native_entry = TypedData::Handle( + zone, TypedData::New(kTypedDataUint64ArrayCid, 4, Heap::kOld)); + native_entry.SetUint64(0 << 3, static_cast(kind)); + native_entry.SetUint64(1 << 3, reinterpret_cast(trampoline)); + native_entry.SetUint64(2 << 3, reinterpret_cast(native_function)); + native_entry.SetUint64(3 << 3, static_cast(argc_tag)); +#endif + return native_entry.raw(); +} #endif // defined(DART_USE_INTERPRETER) StreamingScopeBuilder::StreamingScopeBuilder(ParsedFunction* parsed_function) diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h index 662514f28e9..7c2b7b5b8f3 100644 --- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h +++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h @@ -686,6 +686,8 @@ class BytecodeMetadataHelper : public MetadataHelper { intptr_t from_index); RawCode* ReadBytecode(const ObjectPool& pool); void ReadExceptionsTable(const Code& bytecode); + RawTypedData* NativeEntry(const Function& function, + const String& external_name); #endif }; diff --git a/runtime/vm/constants_kbc.h b/runtime/vm/constants_kbc.h index 52ceebf2b18..312ca7fd4a0 100644 --- a/runtime/vm/constants_kbc.h +++ b/runtime/vm/constants_kbc.h @@ -165,10 +165,10 @@ namespace dart { // with arguments SP[-(1+ArgC)], ..., SP[-1]. // The ICData indicates whether the first argument is a type argument vector. // -// - NativeCall ArgA, ArgB, ArgC +// - NativeCall D // -// Invoke native function at pool[ArgB] with argc_tag at pool[ArgC] using -// wrapper at pool[ArgA]. +// Invoke native function described by array at pool[D]. +// array[0] is wrapper, array[1] is function, array[2] is argc_tag. // // - PushPolymorphicInstanceCall ArgC, D // @@ -791,7 +791,7 @@ namespace dart { V(InstanceCall2Opt, A_D, num, num, ___) \ V(PushPolymorphicInstanceCall, A_D, num, num, ___) \ V(PushPolymorphicInstanceCallByRange, A_D, num, num, ___) \ - V(NativeCall, A_B_C, num, num, num) \ + V(NativeCall, D, lit, ___, ___) \ V(OneByteStringFromCharCode, A_X, reg, xeg, ___) \ V(StringToCharCode, A_X, reg, xeg, ___) \ V(AddTOS, 0, ___, ___, ___) \ diff --git a/runtime/vm/interpreter.cc b/runtime/vm/interpreter.cc index c61a2d548af..4d587326263 100644 --- a/runtime/vm/interpreter.cc +++ b/runtime/vm/interpreter.cc @@ -2135,25 +2135,169 @@ RawObject* Interpreter::Call(const Code& code, } { - BYTECODE(NativeCall, A_B_C); - NativeFunctionWrapper trampoline = - reinterpret_cast(LOAD_CONSTANT(rA)); - Dart_NativeFunction function = - reinterpret_cast(LOAD_CONSTANT(rB)); - intptr_t argc_tag = reinterpret_cast(LOAD_CONSTANT(rC)); - const intptr_t num_arguments = NativeArguments::ArgcBits::decode(argc_tag); + BYTECODE(NativeCall, __D); + RawTypedData* native_entry = static_cast(LOAD_CONSTANT(rD)); + // TODO(regis): Introduce a new VM class subclassing Object and containing + // the four untagged values currently stored as TypeData array elements. + MethodRecognizer::Kind kind = + static_cast(*(reinterpret_cast( + native_entry->ptr()->data() + (0 << kWordSizeLog2)))); + switch (kind) { + case MethodRecognizer::kObjectEquals: { + SP[-1] = SP[-1] == SP[0] ? Bool::True().raw() : Bool::False().raw(); + SP--; + } break; + case MethodRecognizer::kStringBaseLength: + case MethodRecognizer::kStringBaseIsEmpty: { + RawInstance* instance = reinterpret_cast(SP[0]); + SP[0] = reinterpret_cast( + instance->ptr())[String::length_offset() / kWordSize]; + if (kind == MethodRecognizer::kStringBaseIsEmpty) { + SP[0] = + SP[0] == Smi::New(0) ? Bool::True().raw() : Bool::False().raw(); + } + } break; + case MethodRecognizer::kGrowableArrayLength: { + RawInstance* instance = reinterpret_cast(SP[0]); + SP[0] = reinterpret_cast( + instance->ptr())[GrowableObjectArray::length_offset() / kWordSize]; + } break; + case MethodRecognizer::kObjectArrayLength: + case MethodRecognizer::kImmutableArrayLength: { + RawInstance* instance = reinterpret_cast(SP[0]); + SP[0] = reinterpret_cast( + instance->ptr())[Array::length_offset() / kWordSize]; + } break; + case MethodRecognizer::kTypedDataLength: { + RawInstance* instance = reinterpret_cast(SP[0]); + SP[0] = reinterpret_cast( + instance->ptr())[TypedData::length_offset() / kWordSize]; + } break; + case MethodRecognizer::kClassIDgetID: { + SP[0] = InterpreterHelpers::GetClassIdAsSmi(SP[0]); + } break; + case MethodRecognizer::kGrowableArrayCapacity: { + RawInstance* instance = reinterpret_cast(SP[0]); + instance = reinterpret_cast( + instance->ptr())[GrowableObjectArray::data_offset() / kWordSize]; + SP[0] = reinterpret_cast( + instance->ptr())[Array::length_offset() / kWordSize]; + } break; + case MethodRecognizer::kListFactory: { + // factory List([int length]) { + // return (:arg_desc.positional_count == 2) ? new _List(length) + // : new _GrowableList(0); + // } + if (InterpreterHelpers::ArgDescPosCount(argdesc_) == 2) { + SP[1] = SP[0]; // length + SP[2] = SP[-1]; // type + Exit(thread, FP, SP + 3, pc); + NativeArguments native_args(thread, 2, SP + 1, SP - 1); + INVOKE_RUNTIME(DRT_AllocateArray, native_args); + SP -= 1; // Result is in SP - 1. + } else { + // SP[0] is type. + *++SP = Smi::New(0); // len + *++SP = thread->isolate()->object_store()->growable_list_factory(); + argdesc_ = ArgumentsDescriptor::New(0, 2); // Returns a cached desc. + if (!Invoke(thread, SP - 2, SP, &pc, &FP, &SP)) { + HANDLE_EXCEPTION; + } + } + } break; + case MethodRecognizer::kObjectArrayAllocate: { + SP[1] = SP[0]; // length + SP[2] = SP[-1]; // type + Exit(thread, FP, SP + 3, pc); + NativeArguments native_args(thread, 2, SP + 1, SP - 1); + INVOKE_RUNTIME(DRT_AllocateArray, native_args); + SP -= 1; // Result is in SP - 1. + } break; + case MethodRecognizer::kLinkedHashMap_getIndex: { + RawInstance* instance = reinterpret_cast(SP[0]); + SP[0] = reinterpret_cast( + instance->ptr())[LinkedHashMap::index_offset() / kWordSize]; + } break; + case MethodRecognizer::kLinkedHashMap_setIndex: { + RawInstance* instance = reinterpret_cast(SP[-1]); + reinterpret_cast( + instance->ptr())[LinkedHashMap::index_offset() / kWordSize] = SP[0]; + *--SP = null_value; + } break; + case MethodRecognizer::kLinkedHashMap_getData: { + RawInstance* instance = reinterpret_cast(SP[0]); + SP[0] = reinterpret_cast( + instance->ptr())[LinkedHashMap::data_offset() / kWordSize]; + } break; + case MethodRecognizer::kLinkedHashMap_setData: { + RawInstance* instance = reinterpret_cast(SP[-1]); + reinterpret_cast( + instance->ptr())[LinkedHashMap::data_offset() / kWordSize] = SP[0]; + *--SP = null_value; + } break; + case MethodRecognizer::kLinkedHashMap_getHashMask: { + RawInstance* instance = reinterpret_cast(SP[0]); + SP[0] = reinterpret_cast( + instance->ptr())[LinkedHashMap::hash_mask_offset() / kWordSize]; + } break; + case MethodRecognizer::kLinkedHashMap_setHashMask: { + RawInstance* instance = reinterpret_cast(SP[-1]); + reinterpret_cast( + instance->ptr())[LinkedHashMap::hash_mask_offset() / kWordSize] = + SP[0]; + *--SP = null_value; + } break; + case MethodRecognizer::kLinkedHashMap_getUsedData: { + RawInstance* instance = reinterpret_cast(SP[0]); + SP[0] = reinterpret_cast( + instance->ptr())[LinkedHashMap::used_data_offset() / kWordSize]; + } break; + case MethodRecognizer::kLinkedHashMap_setUsedData: { + RawInstance* instance = reinterpret_cast(SP[-1]); + reinterpret_cast( + instance->ptr())[LinkedHashMap::used_data_offset() / kWordSize] = + SP[0]; + *--SP = null_value; + } break; + case MethodRecognizer::kLinkedHashMap_getDeletedKeys: { + RawInstance* instance = reinterpret_cast(SP[0]); + SP[0] = reinterpret_cast( + instance->ptr())[LinkedHashMap::deleted_keys_offset() / kWordSize]; + } break; + case MethodRecognizer::kLinkedHashMap_setDeletedKeys: { + RawInstance* instance = reinterpret_cast(SP[-1]); + reinterpret_cast( + instance->ptr())[LinkedHashMap::deleted_keys_offset() / kWordSize] = + SP[0]; + *--SP = null_value; + } break; + default: { + NativeFunctionWrapper trampoline = + reinterpret_cast( + *(reinterpret_cast(native_entry->ptr()->data() + + (1 << kWordSizeLog2)))); + Dart_NativeFunction function = reinterpret_cast( + *(reinterpret_cast(native_entry->ptr()->data() + + (2 << kWordSizeLog2)))); + intptr_t argc_tag = + static_cast(*(reinterpret_cast( + native_entry->ptr()->data() + (3 << kWordSizeLog2)))); + const intptr_t num_arguments = + NativeArguments::ArgcBits::decode(argc_tag); - *++SP = null_value; // Result slot. + *++SP = null_value; // Result slot. - RawObject** incoming_args = SP - num_arguments; - RawObject** return_slot = SP; - Exit(thread, FP, SP, pc); - NativeArguments args(thread, argc_tag, incoming_args, return_slot); - INVOKE_NATIVE(trampoline, function, - reinterpret_cast(&args)); + RawObject** incoming_args = SP - num_arguments; + RawObject** return_slot = SP; + Exit(thread, FP, SP, pc); + NativeArguments args(thread, argc_tag, incoming_args, return_slot); + INVOKE_NATIVE(trampoline, function, + reinterpret_cast(&args)); - *(SP - num_arguments) = *return_slot; - SP -= num_arguments; + *(SP - num_arguments) = *return_slot; + SP -= num_arguments; + } + } DISPATCH(); } diff --git a/runtime/vm/native_entry.cc b/runtime/vm/native_entry.cc index d9939219e6a..3aaf67ebb13 100644 --- a/runtime/vm/native_entry.cc +++ b/runtime/vm/native_entry.cc @@ -102,7 +102,7 @@ void NativeEntry::PropagateErrors(NativeArguments* arguments) { UNREACHABLE(); } -#if defined(TARGET_ARCH_DBC) +#if defined(TARGET_ARCH_DBC) || defined(DART_USE_INTERPRETER) uword NativeEntry::BootstrapNativeCallWrapperEntry() { uword entry = reinterpret_cast(NativeEntry::BootstrapNativeCallWrapper); @@ -223,7 +223,7 @@ static NativeFunction ResolveNativeFunction(Zone* zone, const Library& library = Library::Handle(zone, cls.library()); *is_bootstrap_native = - Bootstrap::IsBootstapResolver(library.native_entry_resolver()); + Bootstrap::IsBootstrapResolver(library.native_entry_resolver()); const String& native_name = String::Handle(zone, func.native_name()); ASSERT(!native_name.IsNull()); diff --git a/runtime/vm/native_entry.h b/runtime/vm/native_entry.h index 048c12ef756..638d1c1f047 100644 --- a/runtime/vm/native_entry.h +++ b/runtime/vm/native_entry.h @@ -129,7 +129,7 @@ class NativeEntry : public AllStatic { uword pc); static const uint8_t* ResolveSymbol(uword pc); -#if defined(TARGET_ARCH_DBC) +#if defined(TARGET_ARCH_DBC) || defined(DART_USE_INTERPRETER) static uword BootstrapNativeCallWrapperEntry(); static void BootstrapNativeCallWrapper(Dart_NativeArguments args, Dart_NativeFunction func); diff --git a/runtime/vm/object_store.cc b/runtime/vm/object_store.cc index b9eeebac69f..dd182ff11e7 100644 --- a/runtime/vm/object_store.cc +++ b/runtime/vm/object_store.cc @@ -221,6 +221,12 @@ void ObjectStore::InitKnownObjects() { ASSERT(!cls.IsNull()); set_pragma_class(cls); + cls = core_lib.LookupClassAllowPrivate(Symbols::_GrowableList()); + ASSERT(!cls.IsNull()); + growable_list_factory_ = + cls.LookupFactoryAllowPrivate(Symbols::_GrowableListFactory()); + ASSERT(growable_list_factory_ != Function::null()); + // Cache the core private functions used for fast instance of checks. simple_instance_of_function_ = PrivateObjectLookup(Symbols::_simpleInstanceOf()); diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h index 01b5a52853d..cf0f8076157 100644 --- a/runtime/vm/object_store.h +++ b/runtime/vm/object_store.h @@ -108,6 +108,7 @@ class ObjectPointerVisitor; RW(Function, lookup_port_handler) \ RW(TypedData, empty_uint32_array) \ RW(Function, handle_message_function) \ + RW(Function, growable_list_factory) \ RW(Function, simple_instance_of_function) \ RW(Function, simple_instance_of_true_function) \ RW(Function, simple_instance_of_false_function) \