[vm, interpreter] Handle closure case of invoke-field-dispatchers as bytecode.
This will appear to regress the "interp-only" mode on Golem because it will no longer cheat by inlining into a machine code version of the closure call method.
Bug: https://github.com/dart-lang/sdk/issues/36410
Change-Id: I5daa2cffb6d18b6dd2da61ce08d5bc90092a250a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/99561
Reviewed-by: RĂ©gis Crelier <regis@google.com>
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 6e26e97..73012f7 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -4693,6 +4693,8 @@
"<implicit static getter>");
AddBaseObject(Object::method_extractor_bytecode().raw(), "Bytecode",
"<method extractor>");
+ AddBaseObject(Object::invoke_closure_bytecode().raw(), "Bytecode",
+ "<invoke closure>");
for (intptr_t i = 0; i < ArgumentsDescriptor::kCachedDescriptorCount; i++) {
AddBaseObject(ArgumentsDescriptor::cached_args_descriptors_[i],
@@ -5152,6 +5154,7 @@
AddBaseObject(Object::implicit_setter_bytecode().raw());
AddBaseObject(Object::implicit_static_getter_bytecode().raw());
AddBaseObject(Object::method_extractor_bytecode().raw());
+ AddBaseObject(Object::invoke_closure_bytecode().raw());
for (intptr_t i = 0; i < ArgumentsDescriptor::kCachedDescriptorCount; i++) {
AddBaseObject(ArgumentsDescriptor::cached_args_descriptors_[i]);
diff --git a/runtime/vm/compiler/frontend/bytecode_reader.cc b/runtime/vm/compiler/frontend/bytecode_reader.cc
index a9b468a..726069c 100644
--- a/runtime/vm/compiler/frontend/bytecode_reader.cc
+++ b/runtime/vm/compiler/frontend/bytecode_reader.cc
@@ -67,6 +67,12 @@
case RawFunction::kMethodExtractor:
function.AttachBytecode(Object::method_extractor_bytecode());
return;
+ case RawFunction::kInvokeFieldDispatcher:
+ if (Class::Handle(function.Owner()).id() != kClosureCid) {
+ break;
+ }
+ function.AttachBytecode(Object::invoke_closure_bytecode());
+ return;
default: {
}
}
@@ -110,7 +116,8 @@
(function.kind() != RawFunction::kImplicitGetter) &&
(function.kind() != RawFunction::kImplicitSetter) &&
(function.kind() != RawFunction::kImplicitStaticGetter) &&
- (function.kind() != RawFunction::kMethodExtractor)) {
+ (function.kind() != RawFunction::kMethodExtractor) &&
+ (function.kind() != RawFunction::kInvokeFieldDispatcher)) {
return;
}
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 5380d10..d1ea633 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -969,7 +969,8 @@
(function.kind() != RawFunction::kImplicitGetter) &&
(function.kind() != RawFunction::kImplicitSetter) &&
(function.kind() != RawFunction::kImplicitStaticGetter) &&
- (function.kind() != RawFunction::kMethodExtractor)) {
+ (function.kind() != RawFunction::kMethodExtractor) &&
+ (function.kind() != RawFunction::kInvokeFieldDispatcher)) {
BytecodeFlowGraphBuilder bytecode_compiler(
flow_graph_builder_, parsed_function(),
&(flow_graph_builder_->ic_data_array_));
diff --git a/runtime/vm/constants_kbc.h b/runtime/vm/constants_kbc.h
index 79886bf..eee357c 100644
--- a/runtime/vm/constants_kbc.h
+++ b/runtime/vm/constants_kbc.h
@@ -533,6 +533,7 @@
V(VMInternal_ImplicitSetter, 0, ___, ___, ___) \
V(VMInternal_ImplicitStaticGetter, 0, ___, ___, ___) \
V(VMInternal_MethodExtractor, 0, ___, ___, ___) \
+ V(VMInternal_InvokeClosure, 0, ___, ___, ___) \
#define KERNEL_BYTECODES_LIST(V) \
PUBLIC_KERNEL_BYTECODES_LIST(V) \
diff --git a/runtime/vm/interpreter.cc b/runtime/vm/interpreter.cc
index e427b9a..fc8a28d 100644
--- a/runtime/vm/interpreter.cc
+++ b/runtime/vm/interpreter.cc
@@ -3370,6 +3370,83 @@
DISPATCH();
}
+ {
+ BYTECODE(VMInternal_InvokeClosure, 0);
+
+ const intptr_t type_args_len =
+ InterpreterHelpers::ArgDescTypeArgsLen(argdesc_);
+ const intptr_t receiver_idx = type_args_len > 0 ? 1 : 0;
+ const intptr_t argc =
+ InterpreterHelpers::ArgDescArgCount(argdesc_) + receiver_idx;
+
+ RawClosure* receiver =
+ Closure::RawCast(FrameArguments(FP, argc)[receiver_idx]);
+ RawFunction* function = receiver->ptr()->function_;
+
+ if (LIKELY(Function::HasBytecode(function))) {
+ goto TailCallBytecode;
+ }
+ if (LIKELY(Function::HasCode(function))) {
+ goto CallMachineCode;
+ }
+
+ {
+ // Compile the function to either generate code or load bytecode.
+ SP[1] = argdesc_;
+ SP[2] = 0; // Code result.
+ SP[3] = function;
+ Exit(thread, FP, SP + 4, pc);
+ NativeArguments native_args(thread, 1, /* argv */ SP + 3,
+ /* retval */ SP + 2);
+ if (!InvokeRuntime(thread, this, DRT_CompileFunction, native_args)) {
+ HANDLE_EXCEPTION;
+ }
+ function = Function::RawCast(SP[3]);
+ argdesc_ = Array::RawCast(SP[1]);
+ }
+
+ if (LIKELY(Function::HasBytecode(function))) {
+ goto TailCallBytecode;
+ }
+ if (LIKELY(Function::HasCode(function))) {
+ goto CallMachineCode;
+ }
+
+ UNREACHABLE();
+
+ {
+ TailCallBytecode:
+ ASSERT(function->IsFunction());
+ RawBytecode* bytecode = function->ptr()->bytecode_;
+ ASSERT(bytecode->IsBytecode());
+ FP[kKBCFunctionSlotFromFp] = function;
+ FP[kKBCPcMarkerSlotFromFp] = bytecode;
+ pp_ = bytecode->ptr()->object_pool_;
+ pc = reinterpret_cast<uint32_t*>(bytecode->ptr()->instructions_);
+ NOT_IN_PRODUCT(pc_ = pc); // For the profiler.
+ DISPATCH();
+ }
+
+ {
+ CallMachineCode:
+ RawObject** argv = FrameArguments(FP, argc);
+ for (intptr_t i = 0; i < argc; i++) {
+ *++SP = argv[i];
+ }
+
+ RawObject** call_base = SP - argc + 1;
+ RawObject** call_top = SP + 1;
+ call_top[0] = function;
+ if (!InvokeCompiled(thread, function, call_base, call_top, &pc, &FP,
+ &SP)) {
+ HANDLE_EXCEPTION;
+ } else {
+ HANDLE_RETURN;
+ }
+ DISPATCH();
+ }
+ }
+
// Helper used to handle noSuchMethod on closures.
{
ClosureNoSuchMethod:
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 557002e..0a8d677 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -925,6 +925,17 @@
method_extractor_bytecode_->set_exception_handlers(
Object::empty_exception_handlers());
+ static const KBCInstr invoke_closure_instr[2] = {
+ KernelBytecode::Encode(KernelBytecode::kVMInternal_InvokeClosure),
+ KernelBytecode::Encode(KernelBytecode::kReturnTOS),
+ };
+ *invoke_closure_bytecode_ = Bytecode::New(
+ reinterpret_cast<uword>(invoke_closure_instr),
+ sizeof(invoke_closure_instr), -1, Object::empty_object_pool());
+ invoke_closure_bytecode_->set_pc_descriptors(Object::empty_descriptors());
+ invoke_closure_bytecode_->set_exception_handlers(
+ Object::empty_exception_handlers());
+
// Some thread fields need to be reinitialized as null constants have not been
// initialized until now.
Thread* thr = Thread::Current();
@@ -990,6 +1001,8 @@
ASSERT(implicit_static_getter_bytecode_->IsBytecode());
ASSERT(!method_extractor_bytecode_->IsSmi());
ASSERT(method_extractor_bytecode_->IsBytecode());
+ ASSERT(!invoke_closure_bytecode_->IsSmi());
+ ASSERT(invoke_closure_bytecode_->IsBytecode());
}
void Object::FinishInit(Isolate* isolate) {
@@ -5707,12 +5720,13 @@
}
switch (kind()) {
case RawFunction::kNoSuchMethodDispatcher:
- case RawFunction::kInvokeFieldDispatcher:
case RawFunction::kDynamicInvocationForwarder:
case RawFunction::kImplicitClosureFunction:
case RawFunction::kIrregexpFunction:
case RawFunction::kFfiTrampoline:
return false;
+ case RawFunction::kInvokeFieldDispatcher:
+ return Class::Handle(zone, Owner()).id() == kClosureCid;
default:
return true;
}
@@ -15168,6 +15182,8 @@
return "[Bytecode Stub] VMInternal_ImplicitStaticGetter";
} else if (bytecode.raw() == Object::method_extractor_bytecode().raw()) {
return "[Bytecode Stub] VMInternal_MethodExtractor";
+ } else if (bytecode.raw() == Object::invoke_closure_bytecode().raw()) {
+ return "[Bytecode Stub] VMInternal_InvokeClosure";
}
return "[unknown stub]";
}
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 354c7940..8a191b4 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -400,6 +400,7 @@
V(Bytecode, implicit_setter_bytecode) \
V(Bytecode, implicit_static_getter_bytecode) \
V(Bytecode, method_extractor_bytecode) \
+ V(Bytecode, invoke_closure_bytecode) \
V(Instance, sentinel) \
V(Instance, transition_sentinel) \
V(Instance, unknown_constant) \