[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);