[vm/bytecode] Support native methods in BytecodeFlowGraphBuilder
Change-Id: I7cd08a36a738c9c74a5c44854b60160484cb9f2f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/96989
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Auto-Submit: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Aart Bik <ajcbik@google.com>
diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
index 651ac5a..d134dc1 100644
--- a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
+++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
@@ -702,6 +702,12 @@
return Fragment(instr);
}
+Fragment BaseFlowGraphBuilder::LoadClassId() {
+ LoadClassIdInstr* load = new (Z) LoadClassIdInstr(Pop());
+ Push(load);
+ return Fragment(load);
+}
+
} // namespace kernel
} // namespace dart
diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.h b/runtime/vm/compiler/frontend/base_flow_graph_builder.h
index 69ff503..fa4ff07 100644
--- a/runtime/vm/compiler/frontend/base_flow_graph_builder.h
+++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.h
@@ -265,6 +265,7 @@
Fragment CreateArray();
Fragment InstantiateType(const AbstractType& type);
Fragment InstantiateTypeArguments(const TypeArguments& type_arguments);
+ Fragment LoadClassId();
// Returns true if we are building a graph for inlining of a call site that
// enters the function through the unchecked entry.
diff --git a/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc b/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc
index 3853076..f5f3c3a 100644
--- a/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc
+++ b/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc
@@ -809,8 +809,193 @@
UNIMPLEMENTED(); // TODO(alexmarkov): interpreter
}
- // Default flow graph builder is used to compile native methods.
- UNREACHABLE();
+ ASSERT(function().is_native());
+
+ // TODO(alexmarkov): find a way to avoid code duplication with
+ // FlowGraphBuilder::NativeFunctionBody.
+ const MethodRecognizer::Kind kind =
+ MethodRecognizer::RecognizeKind(function());
+ switch (kind) {
+ case MethodRecognizer::kObjectEquals:
+ ASSERT((function().NumParameters() == 2) && !function().IsGeneric());
+ code_ += B->StrictCompare(Token::kEQ_STRICT);
+ break;
+ case MethodRecognizer::kStringBaseLength:
+ case MethodRecognizer::kStringBaseIsEmpty:
+ ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
+ code_ += B->LoadNativeField(Slot::String_length());
+ if (kind == MethodRecognizer::kStringBaseIsEmpty) {
+ code_ += B->IntConstant(0);
+ code_ += B->StrictCompare(Token::kEQ_STRICT);
+ }
+ break;
+ case MethodRecognizer::kGrowableArrayLength:
+ ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
+ code_ += B->LoadNativeField(Slot::GrowableObjectArray_length());
+ break;
+ case MethodRecognizer::kObjectArrayLength:
+ case MethodRecognizer::kImmutableArrayLength:
+ ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
+ code_ += B->LoadNativeField(Slot::Array_length());
+ break;
+ case MethodRecognizer::kTypedDataLength:
+ ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
+ code_ += B->LoadNativeField(Slot::TypedData_length());
+ break;
+ case MethodRecognizer::kClassIDgetID:
+ ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
+ code_ += B->LoadClassId();
+ break;
+ case MethodRecognizer::kGrowableArrayCapacity:
+ ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
+ code_ += B->LoadNativeField(Slot::GrowableObjectArray_data());
+ code_ += B->LoadNativeField(Slot::Array_length());
+ break;
+ case MethodRecognizer::kListFactory: {
+ ASSERT((function().NumParameters() == 2) && !function().IsGeneric() &&
+ function().HasOptionalParameters());
+ ASSERT(scratch_var_ != nullptr);
+ // Generate code that performs:
+ //
+ // factory List<E>([int length]) {
+ // return (:arg_desc.positional_count == 2) ? new _List<E>(length)
+ // : new _GrowableList<E>(0);
+ // }
+ const auto& core_lib = Library::Handle(Z, Library::CoreLibrary());
+
+ TargetEntryInstr *allocate_non_growable, *allocate_growable;
+
+ code_ += B->Drop(); // Drop 'length'.
+ code_ += B->Drop(); // Drop 'type arguments'.
+ code_ += B->LoadArgDescriptor();
+ code_ += B->LoadNativeField(Slot::ArgumentsDescriptor_positional_count());
+ code_ += B->IntConstant(2);
+ code_ +=
+ B->BranchIfStrictEqual(&allocate_non_growable, &allocate_growable);
+
+ JoinEntryInstr* join = B->BuildJoinEntry();
+
+ {
+ const auto& cls = Class::Handle(
+ Z, core_lib.LookupClass(
+ Library::PrivateCoreLibName(Symbols::_List())));
+ ASSERT(!cls.IsNull());
+ const auto& func = Function::ZoneHandle(
+ Z, cls.LookupFactoryAllowPrivate(Symbols::_ListFactory()));
+ ASSERT(!func.IsNull());
+
+ code_ = Fragment(allocate_non_growable);
+ code_ += B->LoadLocal(LocalVariableAt(0));
+ code_ += B->LoadLocal(LocalVariableAt(1));
+ auto* call = new (Z) StaticCallInstr(
+ TokenPosition::kNoSource, func, 0, Array::null_array(),
+ GetArguments(2), *ic_data_array_, B->GetNextDeoptId(),
+ ICData::kStatic);
+ code_ <<= call;
+ B->Push(call);
+ code_ += B->StoreLocal(TokenPosition::kNoSource, scratch_var_);
+ code_ += B->Drop();
+ code_ += B->Goto(join);
+ }
+
+ {
+ const auto& cls = Class::Handle(
+ Z, core_lib.LookupClass(
+ Library::PrivateCoreLibName(Symbols::_GrowableList())));
+ ASSERT(!cls.IsNull());
+ const auto& func = Function::ZoneHandle(
+ Z, cls.LookupFactoryAllowPrivate(Symbols::_GrowableListFactory()));
+ ASSERT(!func.IsNull());
+
+ code_ = Fragment(allocate_growable);
+ code_ += B->LoadLocal(LocalVariableAt(0));
+ code_ += B->IntConstant(0);
+ auto* call = new (Z) StaticCallInstr(
+ TokenPosition::kNoSource, func, 0, Array::null_array(),
+ GetArguments(2), *ic_data_array_, B->GetNextDeoptId(),
+ ICData::kStatic);
+ code_ <<= call;
+ B->Push(call);
+ code_ += B->StoreLocal(TokenPosition::kNoSource, scratch_var_);
+ code_ += B->Drop();
+ code_ += B->Goto(join);
+ }
+
+ code_ = Fragment(join);
+ code_ += B->LoadLocal(scratch_var_);
+ break;
+ }
+ case MethodRecognizer::kObjectArrayAllocate:
+ ASSERT((function().NumParameters() == 2) && !function().IsGeneric());
+ code_ += B->CreateArray();
+ break;
+ case MethodRecognizer::kLinkedHashMap_getIndex:
+ ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
+ code_ += B->LoadNativeField(Slot::LinkedHashMap_index());
+ break;
+ case MethodRecognizer::kLinkedHashMap_setIndex:
+ ASSERT((function().NumParameters() == 2) && !function().IsGeneric());
+ code_ += B->StoreInstanceField(TokenPosition::kNoSource,
+ Slot::LinkedHashMap_index());
+ code_ += B->NullConstant();
+ break;
+ case MethodRecognizer::kLinkedHashMap_getData:
+ ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
+ code_ += B->LoadNativeField(Slot::LinkedHashMap_data());
+ break;
+ case MethodRecognizer::kLinkedHashMap_setData:
+ ASSERT((function().NumParameters() == 2) && !function().IsGeneric());
+ code_ += B->StoreInstanceField(TokenPosition::kNoSource,
+ Slot::LinkedHashMap_data());
+ code_ += B->NullConstant();
+ break;
+ case MethodRecognizer::kLinkedHashMap_getHashMask:
+ ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
+ code_ += B->LoadNativeField(Slot::LinkedHashMap_hash_mask());
+ break;
+ case MethodRecognizer::kLinkedHashMap_setHashMask:
+ ASSERT((function().NumParameters() == 2) && !function().IsGeneric());
+ code_ += B->StoreInstanceField(TokenPosition::kNoSource,
+ Slot::LinkedHashMap_hash_mask(),
+ kNoStoreBarrier);
+ code_ += B->NullConstant();
+ break;
+ case MethodRecognizer::kLinkedHashMap_getUsedData:
+ ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
+ code_ += B->LoadNativeField(Slot::LinkedHashMap_used_data());
+ break;
+ case MethodRecognizer::kLinkedHashMap_setUsedData:
+ ASSERT((function().NumParameters() == 2) && !function().IsGeneric());
+ code_ += B->StoreInstanceField(TokenPosition::kNoSource,
+ Slot::LinkedHashMap_used_data(),
+ kNoStoreBarrier);
+ code_ += B->NullConstant();
+ break;
+ case MethodRecognizer::kLinkedHashMap_getDeletedKeys:
+ ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
+ code_ += B->LoadNativeField(Slot::LinkedHashMap_deleted_keys());
+ break;
+ case MethodRecognizer::kLinkedHashMap_setDeletedKeys:
+ ASSERT((function().NumParameters() == 2) && !function().IsGeneric());
+ code_ += B->StoreInstanceField(TokenPosition::kNoSource,
+ Slot::LinkedHashMap_deleted_keys(),
+ kNoStoreBarrier);
+ code_ += B->NullConstant();
+ break;
+ default: {
+ B->InlineBailout("BytecodeFlowGraphBuilder::BuildNativeCall");
+ const auto& name = String::ZoneHandle(Z, function().native_name());
+ const intptr_t num_args =
+ function().NumParameters() + (function().IsGeneric() ? 1 : 0);
+ ArgumentArray arguments = GetArguments(num_args);
+ auto* call =
+ new (Z) NativeCallInstr(&name, &function(), FLAG_link_natives_lazily,
+ function().end_token_pos(), arguments);
+ code_ <<= call;
+ B->Push(call);
+ break;
+ }
+ }
}
void BytecodeFlowGraphBuilder::BuildAllocate() {
@@ -1471,6 +1656,9 @@
return KernelBytecode::DecodeC(instr) > 0;
case KernelBytecode::kEqualsNull:
return true;
+ case KernelBytecode::kNativeCall:
+ return MethodRecognizer::RecognizeKind(function()) ==
+ MethodRecognizer::kListFactory;
default:
return false;
}
@@ -1547,9 +1735,6 @@
}
FlowGraph* BytecodeFlowGraphBuilder::BuildGraph() {
- // Use default flow graph builder for native methods.
- ASSERT(!function().is_native());
-
const Bytecode& bytecode = Bytecode::Handle(Z, function().bytecode());
object_pool_ = bytecode.object_pool();
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 35477d9..95b52d3 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -925,7 +925,7 @@
SetOffset(kernel_offset);
if ((FLAG_use_bytecode_compiler || FLAG_enable_interpreter) &&
- function.IsBytecodeAllowed(Z) && !function.is_native()) {
+ function.IsBytecodeAllowed(Z)) {
if (!function.HasBytecode()) {
bytecode_metadata_helper_.ReadMetadata(function);
}
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 1c9371b..fece005 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -431,12 +431,6 @@
return instructions;
}
-Fragment FlowGraphBuilder::LoadClassId() {
- LoadClassIdInstr* load = new (Z) LoadClassIdInstr(Pop());
- Push(load);
- return Fragment(load);
-}
-
Fragment FlowGraphBuilder::LoadLocal(LocalVariable* variable) {
if (variable->is_captured()) {
Fragment instructions;
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.h b/runtime/vm/compiler/frontend/kernel_to_il.h
index 6212ac2..6e15186 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.h
+++ b/runtime/vm/compiler/frontend/kernel_to_il.h
@@ -154,7 +154,6 @@
const ZoneGrowableArray<Location>& arg_locs);
Fragment RethrowException(TokenPosition position, int catch_try_index);
- Fragment LoadClassId();
Fragment LoadLocal(LocalVariable* variable);
Fragment InitStaticField(const Field& field);
Fragment NativeCall(const String* name, const Function* function);