aceeba7567
Right now `Script::yield_positions()` is an array mapping function start token positions to arrays. Those arrays contain token positions and are indexed by yield index. => This `Script::yield_positions()` is not available in AOT mode. The fast async stack implementation will need to be able to find out where an async closure was suspended. It does so by looking at the ":await_jump_var", which contains the yield index. It then needs to associate the yield index to token position of the yield. This CL adds an entry into the PcDescriptors for every yield in a async/async* function and removes `Script::yield_positions()`. The entry will associate the yield index with the token position. Flutter gallery total size impact for flutter-release: - armv7: +0.016% - armv8: +0.045% Issue https://github.com/dart-lang/sdk/issues/37668 Change-Id: I0b2ce41e85d8f5d590201bf2fb091578d7379890 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/125408 Commit-Queue: Martin Kustermann <kustermann@google.com> Reviewed-by: Clement Skau <cskau@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
168 lines
5.6 KiB
C++
168 lines
5.6 KiB
C++
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "platform/assert.h"
|
|
#include "vm/globals.h"
|
|
|
|
#include "vm/code_descriptors.h"
|
|
#include "vm/compiler/assembler/assembler.h"
|
|
#include "vm/compiler/jit/compiler.h"
|
|
#include "vm/dart_entry.h"
|
|
#include "vm/native_entry.h"
|
|
#include "vm/parser.h"
|
|
#include "vm/symbols.h"
|
|
#include "vm/thread.h"
|
|
#include "vm/unit_test.h"
|
|
|
|
namespace dart {
|
|
|
|
static void NativeFunc(Dart_NativeArguments args) {
|
|
Dart_Handle i = Dart_GetNativeArgument(args, 0);
|
|
Dart_Handle k = Dart_GetNativeArgument(args, 1);
|
|
int64_t value = -1;
|
|
EXPECT_VALID(Dart_IntegerToInt64(i, &value));
|
|
EXPECT_EQ(10, value);
|
|
EXPECT_VALID(Dart_IntegerToInt64(k, &value));
|
|
EXPECT_EQ(20, value);
|
|
{
|
|
TransitionNativeToVM transition(Thread::Current());
|
|
Isolate::Current()->heap()->CollectAllGarbage();
|
|
}
|
|
}
|
|
|
|
static Dart_NativeFunction native_resolver(Dart_Handle name,
|
|
int argument_count,
|
|
bool* auto_setup_scope) {
|
|
ASSERT(auto_setup_scope);
|
|
*auto_setup_scope = false;
|
|
return reinterpret_cast<Dart_NativeFunction>(&NativeFunc);
|
|
}
|
|
|
|
TEST_CASE(StackMapGC) {
|
|
const char* kScriptChars =
|
|
"class A {"
|
|
" static void func(var i, var k) native 'NativeFunc';"
|
|
" static foo() {"
|
|
" var i;"
|
|
" var s1;"
|
|
" var k;"
|
|
" var s2;"
|
|
" var s3;"
|
|
" i = 10; s1 = 'abcd'; k = 20; s2 = 'B'; s3 = 'C';"
|
|
" func(i, k);"
|
|
" return i + k; }"
|
|
" static int moo() {"
|
|
" var i = A.foo();"
|
|
" if (i != 30) throw '$i != 30';"
|
|
" }\n"
|
|
"}\n";
|
|
// First setup the script and compile the script.
|
|
TestCase::LoadTestScript(kScriptChars, native_resolver);
|
|
TransitionNativeToVM transition(thread);
|
|
|
|
EXPECT(ClassFinalizer::ProcessPendingClasses());
|
|
const String& name = String::Handle(String::New(TestCase::url()));
|
|
const Library& lib = Library::Handle(Library::LookupLibrary(thread, name));
|
|
EXPECT(!lib.IsNull());
|
|
Class& cls =
|
|
Class::Handle(lib.LookupClass(String::Handle(Symbols::New(thread, "A"))));
|
|
EXPECT(!cls.IsNull());
|
|
|
|
// Now compile the two functions 'A.foo' and 'A.moo'
|
|
String& function_moo_name = String::Handle(String::New("moo"));
|
|
Function& function_moo =
|
|
Function::Handle(cls.LookupStaticFunction(function_moo_name));
|
|
EXPECT(CompilerTest::TestCompileFunction(function_moo));
|
|
EXPECT(function_moo.HasCode());
|
|
|
|
String& function_foo_name = String::Handle(String::New("foo"));
|
|
Function& function_foo =
|
|
Function::Handle(cls.LookupStaticFunction(function_foo_name));
|
|
EXPECT(CompilerTest::TestCompileFunction(function_foo));
|
|
EXPECT(function_foo.HasCode());
|
|
|
|
// Build and setup a stackmap for the call to 'func' in 'A.foo' in order
|
|
// to test the traversal of stack maps when a GC happens.
|
|
BitmapBuilder* stack_bitmap = new BitmapBuilder();
|
|
EXPECT(stack_bitmap != nullptr);
|
|
stack_bitmap->Set(0, false); // var i.
|
|
stack_bitmap->Set(1, true); // var s1.
|
|
stack_bitmap->Set(2, false); // var k.
|
|
stack_bitmap->Set(3, true); // var s2.
|
|
stack_bitmap->Set(4, true); // var s3.
|
|
const Code& code = Code::Handle(function_foo.unoptimized_code());
|
|
// Search for the pc of the call to 'func'.
|
|
const PcDescriptors& descriptors =
|
|
PcDescriptors::Handle(code.pc_descriptors());
|
|
int call_count = 0;
|
|
PcDescriptors::Iterator iter(descriptors, RawPcDescriptors::kUnoptStaticCall);
|
|
CompressedStackMapsBuilder compressed_maps_builder;
|
|
while (iter.MoveNext()) {
|
|
compressed_maps_builder.AddEntry(iter.PcOffset(), stack_bitmap, 0);
|
|
++call_count;
|
|
}
|
|
// We can't easily check that we put the stackmap at the correct pc, but
|
|
// we did if there was exactly one call seen.
|
|
EXPECT(call_count == 1);
|
|
const auto& compressed_maps =
|
|
CompressedStackMaps::Handle(compressed_maps_builder.Finalize());
|
|
code.set_compressed_stackmaps(compressed_maps);
|
|
|
|
// Now invoke 'A.moo' and it will trigger a GC when the native function
|
|
// is called, this should then cause the stack map of function 'A.foo'
|
|
// to be traversed and the appropriate objects visited.
|
|
const Object& result = Object::Handle(
|
|
DartEntry::InvokeFunction(function_foo, Object::empty_array()));
|
|
EXPECT(!result.IsError());
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(DescriptorList_TokenPositions) {
|
|
DescriptorList* descriptors = new DescriptorList(64);
|
|
ASSERT(descriptors != NULL);
|
|
const intptr_t token_positions[] = {
|
|
kMinInt32,
|
|
5,
|
|
13,
|
|
13,
|
|
13,
|
|
13,
|
|
31,
|
|
23,
|
|
23,
|
|
23,
|
|
33,
|
|
33,
|
|
5,
|
|
5,
|
|
TokenPosition::kMinSourcePos,
|
|
TokenPosition::kMaxSourcePos,
|
|
};
|
|
const intptr_t num_token_positions =
|
|
sizeof(token_positions) / sizeof(token_positions[0]);
|
|
|
|
for (intptr_t i = 0; i < num_token_positions; i++) {
|
|
descriptors->AddDescriptor(RawPcDescriptors::kRuntimeCall, 0, 0,
|
|
TokenPosition(token_positions[i]), 0, 1);
|
|
}
|
|
|
|
const PcDescriptors& finalized_descriptors =
|
|
PcDescriptors::Handle(descriptors->FinalizePcDescriptors(0));
|
|
|
|
ASSERT(!finalized_descriptors.IsNull());
|
|
PcDescriptors::Iterator it(finalized_descriptors,
|
|
RawPcDescriptors::kRuntimeCall);
|
|
|
|
intptr_t i = 0;
|
|
while (it.MoveNext()) {
|
|
if (token_positions[i] != it.TokenPos().value()) {
|
|
OS::PrintErr("[%" Pd "]: Expected: %" Pd " != %" Pd "\n", i,
|
|
token_positions[i], it.TokenPos().value());
|
|
}
|
|
EXPECT(token_positions[i] == it.TokenPos().value());
|
|
i++;
|
|
}
|
|
}
|
|
|
|
} // namespace dart
|