[vm,bytecode] Generate calls for causal-async-stacks more consistently

In order to support causal-async-stacks, the following synthetic calls
are inserted: _asyncStackTraceHelper, _setAsyncThreadStackTrace and
_clearAsyncThreadStackTrace.

_asyncStackTraceHelper is always generated in the async transformation.
_setAsyncThreadStackTrace and _clearAsyncThreadStackTrace are inserted
during bytecode generation (or in the flow graph builder in case of AST
pipeline). If bytecode generation options are set inconsistently with
--causal-async-stacks VM option, then it was possible that
_asyncStackTraceHelper was generated but other calls were not generated,
causing incorrect (full, non-truncated) async stack traces. This also
causes performance regression as collecting full stack traces takes
much more time.

This change makes generation of all these calls more consistent: if
causal async stacks are disabled during bytecode generation, then all
three calls are omitted. Also, in case these calls were generated and
present in bytecode, but VM option --causal-async-stacks is disabled,
then bytecode flow graph builder turns all these three calls into no-ops.

Change-Id: I93eb1d83c675ee093799bb8e37ca3d60a3c5c19d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/120927
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
This commit is contained in:
Alexander Markov 2019-10-09 22:10:21 +00:00 committed by commit-bot@chromium.org
parent f4f0831c69
commit 62ee78eeec
3 changed files with 117 additions and 124 deletions

View file

@ -3511,6 +3511,19 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
_generateNode(args.positional.single);
return;
}
if (!options.causalAsyncStacks &&
target == coreTypes.asyncStackTraceHelperProcedure) {
// Eliminate calls to _asyncStackTraceHelper as causal async stacks are
// disabled. These calls are inserted by async transformation
// (pkg/kernel/lib/transformations/continuation.dart), but they should be
// consistent with _setAsyncThreadStackTrace and
// _clearAsyncThreadStackTrace calls generated by bytecode generator.
//
// Push null as _asyncStackTraceHelper call should leave result
// on the stack.
asm.emitPushNull();
return;
}
if (target.isFactory) {
final constructedClass = target.enclosingClass;
if (hasInstantiatorTypeArguments(constructedClass)) {

View file

@ -63,17 +63,15 @@ ConstantPool {
[26] = Reserved
[27] = InstanceField dart:core::_Closure::_function (field)
[28] = Reserved
[29] = DirectCall 'dart:async::_asyncStackTraceHelper', ArgDesc num-args 1, num-type-args 0, names []
[29] = DirectCall 'dart:async::_asyncThenWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[30] = Reserved
[31] = DirectCall 'dart:async::_asyncThenWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[31] = DirectCall 'dart:async::_asyncErrorWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[32] = Reserved
[33] = DirectCall 'dart:async::_asyncErrorWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[33] = DynamicCall 'start', ArgDesc num-args 2, num-type-args 0, names []
[34] = Reserved
[35] = DynamicCall 'start', ArgDesc num-args 2, num-type-args 0, names []
[35] = InterfaceCall 'dart:async::Completer::get:future', ArgDesc num-args 1, num-type-args 0, names []
[36] = Reserved
[37] = InterfaceCall 'dart:async::Completer::get:future', ArgDesc num-args 1, num-type-args 0, names []
[38] = Reserved
[39] = EndClosureFunctionScope
[37] = EndClosureFunctionScope
}
Closure #lib::asyncInFieldInitializer (field)::'<anonymous closure>' async (dart:async::Future < dart:core::int > x) -> dart:async::Future < dart:core::Null >
ClosureCode {
@ -144,29 +142,27 @@ L1:
Push r0
StoreFieldTOS CP#1
StoreContextVar 0, 8
Push r0
LoadContextVar 0, 8
DirectCall CP#29, 1
PushNull
PopLocal r3
Push r0
Push r0
LoadContextVar 0, 8
DirectCall CP#31, 1
DirectCall CP#29, 1
StoreContextVar 0, 3
Push r0
Push r0
LoadContextVar 0, 8
DirectCall CP#33, 1
DirectCall CP#31, 1
StoreContextVar 0, 4
Push r0
LoadContextVar 0, 1
Push r0
LoadContextVar 0, 8
DynamicCall CP#35, 2
DynamicCall CP#33, 2
Drop1
Push r0
LoadContextVar 0, 1
InterfaceCall CP#37, 1
InterfaceCall CP#35, 1
ReturnTOS
}
@ -309,23 +305,22 @@ Bytecode {
Push r0
StoreFieldTOS CP#6
PopLocal r6
Push r6
DirectCall CP#23, 1
PushNull
PopLocal r3
Push r6
DirectCall CP#25, 1
DirectCall CP#23, 1
PopLocal r4
Push r6
DirectCall CP#27, 1
DirectCall CP#25, 1
PopLocal r5
Push r0
LoadContextVar 0, 0
Push r6
DynamicCall CP#29, 2
DynamicCall CP#27, 2
Drop1
Push r0
LoadContextVar 0, 0
InterfaceCall CP#31, 1
InterfaceCall CP#29, 1
ReturnTOS
}
ConstantPool {
@ -352,16 +347,14 @@ ConstantPool {
[20] = Reserved
[21] = InstanceField dart:core::_Closure::_function (field)
[22] = Reserved
[23] = DirectCall 'dart:async::_asyncStackTraceHelper', ArgDesc num-args 1, num-type-args 0, names []
[23] = DirectCall 'dart:async::_asyncThenWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[24] = Reserved
[25] = DirectCall 'dart:async::_asyncThenWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[25] = DirectCall 'dart:async::_asyncErrorWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[26] = Reserved
[27] = DirectCall 'dart:async::_asyncErrorWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[27] = DynamicCall 'start', ArgDesc num-args 2, num-type-args 0, names []
[28] = Reserved
[29] = DynamicCall 'start', ArgDesc num-args 2, num-type-args 0, names []
[29] = InterfaceCall 'dart:async::Completer::get:future', ArgDesc num-args 1, num-type-args 0, names []
[30] = Reserved
[31] = InterfaceCall 'dart:async::Completer::get:future', ArgDesc num-args 1, num-type-args 0, names []
[32] = Reserved
}
Closure #lib::foo::':async_op' ([ dynamic :result, dynamic :exception, dynamic :stack_trace ]) -> dynamic
ClosureCode {
@ -487,29 +480,27 @@ Bytecode {
Push r0
StoreFieldTOS CP#6
StoreContextVar 0, 10
Push r0
LoadContextVar 0, 10
DirectCall CP#27, 1
PushNull
PopLocal r3
Push r0
Push r0
LoadContextVar 0, 10
DirectCall CP#29, 1
DirectCall CP#27, 1
StoreContextVar 0, 4
Push r0
Push r0
LoadContextVar 0, 10
DirectCall CP#31, 1
DirectCall CP#29, 1
StoreContextVar 0, 5
Push r0
LoadContextVar 0, 2
Push r0
LoadContextVar 0, 10
DynamicCall CP#33, 2
DynamicCall CP#31, 2
Drop1
Push r0
LoadContextVar 0, 2
InterfaceCall CP#35, 1
InterfaceCall CP#33, 1
ReturnTOS
}
ConstantPool {
@ -540,16 +531,14 @@ ConstantPool {
[24] = Reserved
[25] = InstanceField dart:core::_Closure::_function (field)
[26] = Reserved
[27] = DirectCall 'dart:async::_asyncStackTraceHelper', ArgDesc num-args 1, num-type-args 0, names []
[27] = DirectCall 'dart:async::_asyncThenWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[28] = Reserved
[29] = DirectCall 'dart:async::_asyncThenWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[29] = DirectCall 'dart:async::_asyncErrorWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[30] = Reserved
[31] = DirectCall 'dart:async::_asyncErrorWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[31] = DynamicCall 'start', ArgDesc num-args 2, num-type-args 0, names []
[32] = Reserved
[33] = DynamicCall 'start', ArgDesc num-args 2, num-type-args 0, names []
[33] = InterfaceCall 'dart:async::Completer::get:future', ArgDesc num-args 1, num-type-args 0, names []
[34] = Reserved
[35] = InterfaceCall 'dart:async::Completer::get:future', ArgDesc num-args 1, num-type-args 0, names []
[36] = Reserved
}
Closure #lib::simpleAsyncAwait::':async_op' ([ dynamic :result, dynamic :exception, dynamic :stack_trace ]) -> dynamic
ClosureCode {
@ -742,29 +731,27 @@ Bytecode {
Push r0
StoreFieldTOS CP#6
StoreContextVar 0, 10
Push r0
LoadContextVar 0, 10
DirectCall CP#35, 1
PushNull
PopLocal r3
Push r0
Push r0
LoadContextVar 0, 10
DirectCall CP#37, 1
DirectCall CP#35, 1
StoreContextVar 0, 3
Push r0
Push r0
LoadContextVar 0, 10
DirectCall CP#39, 1
DirectCall CP#37, 1
StoreContextVar 0, 4
Push r0
LoadContextVar 0, 1
Push r0
LoadContextVar 0, 10
DynamicCall CP#41, 2
DynamicCall CP#39, 2
Drop1
Push r0
LoadContextVar 0, 1
InterfaceCall CP#43, 1
InterfaceCall CP#41, 1
ReturnTOS
}
ConstantPool {
@ -803,16 +790,14 @@ ConstantPool {
[32] = Reserved
[33] = InstanceField dart:core::_Closure::_function (field)
[34] = Reserved
[35] = DirectCall 'dart:async::_asyncStackTraceHelper', ArgDesc num-args 1, num-type-args 0, names []
[35] = DirectCall 'dart:async::_asyncThenWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[36] = Reserved
[37] = DirectCall 'dart:async::_asyncThenWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[37] = DirectCall 'dart:async::_asyncErrorWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[38] = Reserved
[39] = DirectCall 'dart:async::_asyncErrorWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[39] = DynamicCall 'start', ArgDesc num-args 2, num-type-args 0, names []
[40] = Reserved
[41] = DynamicCall 'start', ArgDesc num-args 2, num-type-args 0, names []
[41] = InterfaceCall 'dart:async::Completer::get:future', ArgDesc num-args 1, num-type-args 0, names []
[42] = Reserved
[43] = InterfaceCall 'dart:async::Completer::get:future', ArgDesc num-args 1, num-type-args 0, names []
[44] = Reserved
}
Closure #lib::loops::':async_op' ([ dynamic :result, dynamic :exception, dynamic :stack_trace ]) -> dynamic
ClosureCode {
@ -1142,29 +1127,27 @@ Bytecode {
Push r0
StoreFieldTOS CP#6
StoreContextVar 0, 17
Push r0
LoadContextVar 0, 17
DirectCall CP#33, 1
PushNull
PopLocal r3
Push r0
Push r0
LoadContextVar 0, 17
DirectCall CP#35, 1
DirectCall CP#33, 1
StoreContextVar 0, 5
Push r0
Push r0
LoadContextVar 0, 17
DirectCall CP#37, 1
DirectCall CP#35, 1
StoreContextVar 0, 6
Push r0
LoadContextVar 0, 3
Push r0
LoadContextVar 0, 17
DynamicCall CP#39, 2
DynamicCall CP#37, 2
Drop1
Push r0
LoadContextVar 0, 3
InterfaceCall CP#41, 1
InterfaceCall CP#39, 1
ReturnTOS
}
ConstantPool {
@ -1201,16 +1184,14 @@ ConstantPool {
[30] = Reserved
[31] = InstanceField dart:core::_Closure::_function (field)
[32] = Reserved
[33] = DirectCall 'dart:async::_asyncStackTraceHelper', ArgDesc num-args 1, num-type-args 0, names []
[33] = DirectCall 'dart:async::_asyncThenWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[34] = Reserved
[35] = DirectCall 'dart:async::_asyncThenWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[35] = DirectCall 'dart:async::_asyncErrorWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[36] = Reserved
[37] = DirectCall 'dart:async::_asyncErrorWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[37] = DynamicCall 'start', ArgDesc num-args 2, num-type-args 0, names []
[38] = Reserved
[39] = DynamicCall 'start', ArgDesc num-args 2, num-type-args 0, names []
[39] = InterfaceCall 'dart:async::Completer::get:future', ArgDesc num-args 1, num-type-args 0, names []
[40] = Reserved
[41] = InterfaceCall 'dart:async::Completer::get:future', ArgDesc num-args 1, num-type-args 0, names []
[42] = Reserved
}
Closure #lib::tryCatchRethrow::':async_op' ([ dynamic :result, dynamic :exception, dynamic :stack_trace ]) -> dynamic
ClosureCode {
@ -1698,17 +1679,15 @@ ConstantPool {
[26] = Reserved
[27] = InstanceField dart:core::_Closure::_function (field)
[28] = Reserved
[29] = DirectCall 'dart:async::_asyncStackTraceHelper', ArgDesc num-args 1, num-type-args 0, names []
[29] = DirectCall 'dart:async::_asyncThenWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[30] = Reserved
[31] = DirectCall 'dart:async::_asyncThenWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[31] = DirectCall 'dart:async::_asyncErrorWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[32] = Reserved
[33] = DirectCall 'dart:async::_asyncErrorWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[33] = DynamicCall 'start', ArgDesc num-args 2, num-type-args 0, names []
[34] = Reserved
[35] = DynamicCall 'start', ArgDesc num-args 2, num-type-args 0, names []
[35] = InterfaceCall 'dart:async::Completer::get:future', ArgDesc num-args 1, num-type-args 0, names []
[36] = Reserved
[37] = InterfaceCall 'dart:async::Completer::get:future', ArgDesc num-args 1, num-type-args 0, names []
[38] = Reserved
[39] = EndClosureFunctionScope
[37] = EndClosureFunctionScope
}
Closure #lib::closure::'nested' async () -> dart:async::Future < dart:core::int >
ClosureCode {
@ -1774,29 +1753,27 @@ ClosureCode {
Push r0
StoreFieldTOS CP#1
StoreContextVar 1, 8
Push r0
LoadContextVar 1, 8
DirectCall CP#29, 1
PushNull
PopLocal r3
Push r0
Push r0
LoadContextVar 1, 8
DirectCall CP#31, 1
DirectCall CP#29, 1
StoreContextVar 1, 2
Push r0
Push r0
LoadContextVar 1, 8
DirectCall CP#33, 1
DirectCall CP#31, 1
StoreContextVar 1, 3
Push r0
LoadContextVar 1, 0
Push r0
LoadContextVar 1, 8
DynamicCall CP#35, 2
DynamicCall CP#33, 2
Drop1
Push r0
LoadContextVar 1, 0
InterfaceCall CP#37, 1
InterfaceCall CP#35, 1
ReturnTOS
}
@ -2018,29 +1995,27 @@ Bytecode {
Push r0
StoreFieldTOS CP#6
StoreContextVar 0, 8
Push r0
LoadContextVar 0, 8
DirectCall CP#29, 1
PushNull
PopLocal r3
Push r0
Push r0
LoadContextVar 0, 8
DirectCall CP#31, 1
DirectCall CP#29, 1
StoreContextVar 0, 3
Push r0
Push r0
LoadContextVar 0, 8
DirectCall CP#33, 1
DirectCall CP#31, 1
StoreContextVar 0, 4
Push r0
LoadContextVar 0, 1
Push r0
LoadContextVar 0, 8
DynamicCall CP#35, 2
DynamicCall CP#33, 2
Drop1
Push r0
LoadContextVar 0, 1
InterfaceCall CP#37, 1
InterfaceCall CP#35, 1
ReturnTOS
}
ConstantPool {
@ -2073,16 +2048,14 @@ ConstantPool {
[26] = Reserved
[27] = InstanceField dart:core::_Closure::_function (field)
[28] = Reserved
[29] = DirectCall 'dart:async::_asyncStackTraceHelper', ArgDesc num-args 1, num-type-args 0, names []
[29] = DirectCall 'dart:async::_asyncThenWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[30] = Reserved
[31] = DirectCall 'dart:async::_asyncThenWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[31] = DirectCall 'dart:async::_asyncErrorWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[32] = Reserved
[33] = DirectCall 'dart:async::_asyncErrorWrapperHelper', ArgDesc num-args 1, num-type-args 0, names []
[33] = DynamicCall 'start', ArgDesc num-args 2, num-type-args 0, names []
[34] = Reserved
[35] = DynamicCall 'start', ArgDesc num-args 2, num-type-args 0, names []
[35] = InterfaceCall 'dart:async::Completer::get:future', ArgDesc num-args 1, num-type-args 0, names []
[36] = Reserved
[37] = InterfaceCall 'dart:async::Completer::get:future', ArgDesc num-args 1, num-type-args 0, names []
[38] = Reserved
}
Closure #lib::testAssert::':async_op' ([ dynamic :result, dynamic :exception, dynamic :stack_trace ]) -> dynamic
ClosureCode {

View file

@ -845,41 +845,48 @@ void BytecodeFlowGraphBuilder::BuildDirectCallCommon(bool is_unchecked_call) {
const intptr_t argc = DecodeOperandF().value();
const auto recognized_kind = MethodRecognizer::RecognizeKind(target);
if (recognized_kind == MethodRecognizer::kFfiAsFunctionInternal) {
BuildFfiAsFunction();
return;
} else if (FLAG_precompiled_mode &&
recognized_kind == MethodRecognizer::kFfiNativeCallbackFunction) {
BuildFfiNativeCallbackFunction();
return;
}
// Recognize identical() call.
// Note: similar optimization is performed in AST flow graph builder - see
// StreamingFlowGraphBuilder::BuildStaticInvocation, special_case_identical.
// TODO(alexmarkov): find a better place for this optimization.
if (target.name() == Symbols::Identical().raw()) {
const auto& owner = Class::Handle(Z, target.Owner());
if (owner.IsTopLevel() && (owner.library() == Library::CoreLibrary())) {
switch (recognized_kind) {
case MethodRecognizer::kFfiAsFunctionInternal:
BuildFfiAsFunction();
return;
case MethodRecognizer::kFfiNativeCallbackFunction:
if (FLAG_precompiled_mode) {
BuildFfiNativeCallbackFunction();
return;
}
break;
case MethodRecognizer::kObjectIdentical:
// Note: similar optimization is performed in AST flow graph builder -
// see StreamingFlowGraphBuilder::BuildStaticInvocation,
// special_case_identical.
// TODO(alexmarkov): find a better place for this optimization.
ASSERT(argc == 2);
code_ += B->StrictCompare(Token::kEQ_STRICT, /*number_check=*/true);
return;
}
}
if (!FLAG_causal_async_stacks &&
recognized_kind == MethodRecognizer::kAsyncStackTraceHelper) {
ASSERT(argc == 1);
// Drop the ignored parameter to _asyncStackTraceHelper(:async_op).
code_ += B->Drop();
code_ += B->NullConstant();
return;
}
if (recognized_kind == MethodRecognizer::kStringBaseInterpolate) {
ASSERT(argc == 1);
code_ += B->StringInterpolate(position_);
return;
case MethodRecognizer::kAsyncStackTraceHelper:
case MethodRecognizer::kSetAsyncThreadStackTrace:
if (!FLAG_causal_async_stacks) {
ASSERT(argc == 1);
// Drop the ignored parameter to _asyncStackTraceHelper(:async_op) or
// _setAsyncThreadStackTrace(stackTrace).
code_ += B->Drop();
code_ += B->NullConstant();
return;
}
break;
case MethodRecognizer::kClearAsyncThreadStackTrace:
if (!FLAG_causal_async_stacks) {
ASSERT(argc == 0);
code_ += B->NullConstant();
return;
}
break;
case MethodRecognizer::kStringBaseInterpolate:
ASSERT(argc == 1);
code_ += B->StringInterpolate(position_);
return;
default:
break;
}
const Array& arg_desc_array =