[vm/compiler] Avoid runtime call for _asyncStackTraceHelper if !FLAG_causal_async_stacks

Even though the FLAG_causal_async_stacks can be used to disable causal
async stack traces, the generated code would still call to the runtime,
which would simply return `null`.

To avoid this runtime call we recognize the method in the 2 flow graph builders.

Issue https://github.com/dart-lang/sdk/issues/37668

Change-Id: I896807060db911714d47a462c34696a0a3def62f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/110919
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Martin Kustermann 2019-07-31 19:49:51 +00:00 committed by commit-bot@chromium.org
parent 1e16b74b5b
commit 130c654dcd
8 changed files with 42 additions and 3 deletions

View file

@ -296,13 +296,19 @@ void _completeOnAsyncReturn(Completer completer, Object value) {
/// Returns a [StackTrace] object containing the synchronous prefix for this
/// asynchronous method.
//
// This method is recognized. It performs a runtime call if
// FLAG_causal_async_stacks is enabled or returns `null` otherwise.
@pragma("vm:prefer-inline")
Object _asyncStackTraceHelper(Function async_op)
native "StackTrace_asyncStackTraceHelper";
// This method is asm intrinsified.
@pragma("vm:entry-point", "call")
void _clearAsyncThreadStackTrace()
native "StackTrace_clearAsyncThreadStackTrace";
// This method is asm intrinsified.
@pragma("vm:entry-point", "call")
void _setAsyncThreadStackTrace(StackTrace stackTrace)
native "StackTrace_setAsyncThreadStackTrace";

View file

@ -108,7 +108,10 @@ DEFINE_NATIVE_ENTRY(StackTrace_current, 0, 0) {
DEFINE_NATIVE_ENTRY(StackTrace_asyncStackTraceHelper, 0, 1) {
if (!FLAG_causal_async_stacks) {
return Object::null();
// If causal async stacks are not enabled we should recognize this method
// and never call to the NOP runtime.
// See kernel_to_il.cc/bytecode_reader.cc/interpreter.cc.
UNREACHABLE();
}
#if !defined(PRODUCT)
GET_NATIVE_ARGUMENT(Closure, async_op, arguments->NativeArgAt(0));

View file

@ -810,6 +810,15 @@ void BytecodeFlowGraphBuilder::BuildDirectCall() {
}
}
if (!FLAG_causal_async_stacks &&
target.recognized_kind() == MethodRecognizer::kAsyncStackTraceHelper) {
ASSERT(argc == 1);
// Drop the ignored parameter to _asyncStackTraceHelper(:async_op).
code_ += B->Drop();
code_ += B->NullConstant();
return;
}
const Array& arg_desc_array =
Array::Cast(ConstantAt(DecodeOperandD(), 1).value());
const ArgumentsDescriptor arg_desc(arg_desc_array);

View file

@ -1040,6 +1040,13 @@ RawTypedData* BytecodeReaderHelper::NativeEntry(const Function& function,
case MethodRecognizer::kLinkedHashMap_getDeletedKeys:
case MethodRecognizer::kLinkedHashMap_setDeletedKeys:
break;
case MethodRecognizer::kAsyncStackTraceHelper:
// If causal async stacks are disabled the interpreter.cc will handle this
// native call specially.
if (!FLAG_causal_async_stacks) {
break;
}
FALL_THROUGH;
default:
kind = MethodRecognizer::kUnknown;
}

View file

@ -3056,6 +3056,10 @@ Fragment StreamingFlowGraphBuilder::BuildStaticInvocation(bool is_const,
Fragment instructions;
LocalVariable* instance_variable = NULL;
const bool special_case_nop_async_stack_trace_helper =
!FLAG_causal_async_stacks &&
target.recognized_kind() == MethodRecognizer::kAsyncStackTraceHelper;
const bool special_case_unchecked_cast =
klass.IsTopLevel() && (klass.library() == Library::InternalLibrary()) &&
(target.name() == Symbols::UnsafeCast().raw());
@ -3064,8 +3068,9 @@ Fragment StreamingFlowGraphBuilder::BuildStaticInvocation(bool is_const,
klass.IsTopLevel() && (klass.library() == Library::CoreLibrary()) &&
(target.name() == Symbols::Identical().raw());
const bool special_case =
special_case_identical || special_case_unchecked_cast;
const bool special_case = special_case_identical ||
special_case_unchecked_cast ||
special_case_nop_async_stack_trace_helper;
// If we cross the Kernel -> VM core library boundary, a [StaticInvocation]
// can appear, but the thing we're calling is not a static method, but a
@ -3130,6 +3135,10 @@ Fragment StreamingFlowGraphBuilder::BuildStaticInvocation(bool is_const,
ASSERT(argument_count == 2);
instructions +=
StrictCompare(position, Token::kEQ_STRICT, /*number_check=*/true);
} else if (special_case_nop_async_stack_trace_helper) {
ASSERT(argument_count == 1);
instructions += Drop();
instructions += NullConstant();
} else if (special_case_unchecked_cast) {
// Simply do nothing: the result value is already pushed on the stack.
} else {

View file

@ -141,6 +141,7 @@ namespace dart {
V(_HashVMBase, get:_deletedKeys, LinkedHashMap_getDeletedKeys, 0x558481c2) \
V(_HashVMBase, set:_deletedKeys, LinkedHashMap_setDeletedKeys, 0x5aa9888d) \
V(::, _classRangeCheck, ClassRangeCheck, 0x2ae76b84) \
V(::, _asyncStackTraceHelper, AsyncStackTraceHelper, 0) \
// List of intrinsics:
// (class-name, function-name, intrinsification method, fingerprint).

View file

@ -2124,6 +2124,9 @@ SwitchDispatch:
case MethodRecognizer::kClassIDgetID: {
SP[0] = InterpreterHelpers::GetClassIdAsSmi(SP[0]);
} break;
case MethodRecognizer::kAsyncStackTraceHelper: {
SP[0] = Object::null();
} break;
case MethodRecognizer::kGrowableArrayCapacity: {
RawGrowableObjectArray* instance =
reinterpret_cast<RawGrowableObjectArray*>(SP[0]);

View file

@ -12524,6 +12524,7 @@ void Library::CheckFunctionFingerprints() {
CORE_LIB_INTRINSIC_LIST(CHECK_FINGERPRINTS2);
CORE_INTEGER_LIB_INTRINSIC_LIST(CHECK_FINGERPRINTS2);
all_libs.Add(&Library::ZoneHandle(Library::AsyncLibrary()));
all_libs.Add(&Library::ZoneHandle(Library::MathLibrary()));
all_libs.Add(&Library::ZoneHandle(Library::TypedDataLibrary()));
all_libs.Add(&Library::ZoneHandle(Library::CollectionLibrary()));