909ce02b12
CodeSourceMap now contains a small bytecode that describes changes to the token position and inlining stack as one moves through a function. This information is used by the disassembler to display inlining comments and by the profiler to properly blame ticks against source code. Slightly shrinks the total size of code metadata; on dart2js compiling hello world, the final heap is before 59.6MB total, 12.8MB List, 1.1MB CodeSourceMap after 59.4MB total, 12.1MB List, 1.5MB CodeSourceMap To be filtered and included in AOT code to expand inline frames in stack traces. R=johnmccutchan@google.com Review-Url: https://codereview.chromium.org/2670843006 .
323 lines
11 KiB
C++
323 lines
11 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/ast.h"
|
|
#include "vm/assembler.h"
|
|
#include "vm/code_descriptors.h"
|
|
#include "vm/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 const TokenPosition kPos = TokenPosition::kNoSource;
|
|
|
|
|
|
CODEGEN_TEST_GENERATE(StackMapCodegen, test) {
|
|
ParsedFunction* parsed_function =
|
|
new ParsedFunction(Thread::Current(), test->function());
|
|
LiteralNode* l = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(1)));
|
|
test->node_sequence()->Add(new ReturnNode(kPos, l));
|
|
l = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(2)));
|
|
test->node_sequence()->Add(new ReturnNode(kPos, l));
|
|
l = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3)));
|
|
test->node_sequence()->Add(new ReturnNode(kPos, l));
|
|
parsed_function->SetNodeSequence(test->node_sequence());
|
|
parsed_function->set_instantiator(NULL);
|
|
parsed_function->EnsureExpressionTemp();
|
|
test->node_sequence()->scope()->AddVariable(
|
|
parsed_function->expression_temp_var());
|
|
test->node_sequence()->scope()->AddVariable(
|
|
parsed_function->current_context_var());
|
|
parsed_function->AllocateVariables();
|
|
bool retval;
|
|
Isolate* isolate = Isolate::Current();
|
|
EXPECT(isolate != NULL);
|
|
LongJumpScope jump;
|
|
if (setjmp(*jump.Set()) == 0) {
|
|
// Build a stackmap table and some stackmap table entries.
|
|
const intptr_t kStackSlotCount = 11;
|
|
StackMapTableBuilder* stackmap_table_builder = new StackMapTableBuilder();
|
|
EXPECT(stackmap_table_builder != NULL);
|
|
|
|
BitmapBuilder* stack_bitmap = new BitmapBuilder();
|
|
EXPECT(stack_bitmap != NULL);
|
|
EXPECT_EQ(0, stack_bitmap->Length());
|
|
stack_bitmap->Set(0, true);
|
|
EXPECT_EQ(1, stack_bitmap->Length());
|
|
stack_bitmap->SetLength(kStackSlotCount);
|
|
EXPECT_EQ(kStackSlotCount, stack_bitmap->Length());
|
|
|
|
bool expectation0[kStackSlotCount] = {true};
|
|
for (intptr_t i = 0; i < kStackSlotCount; ++i) {
|
|
EXPECT_EQ(expectation0[i], stack_bitmap->Get(i));
|
|
}
|
|
// Add a stack map entry at pc offset 0.
|
|
stackmap_table_builder->AddEntry(0, stack_bitmap, 0);
|
|
|
|
stack_bitmap = new BitmapBuilder();
|
|
EXPECT(stack_bitmap != NULL);
|
|
EXPECT_EQ(0, stack_bitmap->Length());
|
|
stack_bitmap->Set(0, true);
|
|
stack_bitmap->Set(1, false);
|
|
stack_bitmap->Set(2, true);
|
|
EXPECT_EQ(3, stack_bitmap->Length());
|
|
stack_bitmap->SetLength(kStackSlotCount);
|
|
EXPECT_EQ(kStackSlotCount, stack_bitmap->Length());
|
|
|
|
bool expectation1[kStackSlotCount] = {true, false, true};
|
|
for (intptr_t i = 0; i < kStackSlotCount; ++i) {
|
|
EXPECT_EQ(expectation1[i], stack_bitmap->Get(i));
|
|
}
|
|
// Add a stack map entry at pc offset 1.
|
|
stackmap_table_builder->AddEntry(1, stack_bitmap, 0);
|
|
|
|
stack_bitmap = new BitmapBuilder();
|
|
EXPECT(stack_bitmap != NULL);
|
|
EXPECT_EQ(0, stack_bitmap->Length());
|
|
stack_bitmap->Set(0, true);
|
|
stack_bitmap->Set(1, false);
|
|
stack_bitmap->Set(2, true);
|
|
stack_bitmap->SetRange(3, 5, true);
|
|
EXPECT_EQ(6, stack_bitmap->Length());
|
|
stack_bitmap->SetLength(kStackSlotCount);
|
|
EXPECT_EQ(kStackSlotCount, stack_bitmap->Length());
|
|
|
|
bool expectation2[kStackSlotCount] = {true, false, true, true, true, true};
|
|
for (intptr_t i = 0; i < kStackSlotCount; ++i) {
|
|
EXPECT_EQ(expectation2[i], stack_bitmap->Get(i));
|
|
}
|
|
// Add a stack map entry at pc offset 2.
|
|
stackmap_table_builder->AddEntry(2, stack_bitmap, 0);
|
|
|
|
stack_bitmap = new BitmapBuilder();
|
|
EXPECT(stack_bitmap != NULL);
|
|
EXPECT_EQ(0, stack_bitmap->Length());
|
|
stack_bitmap->Set(0, true);
|
|
stack_bitmap->Set(1, false);
|
|
stack_bitmap->Set(2, true);
|
|
stack_bitmap->SetRange(3, 5, true);
|
|
stack_bitmap->SetRange(6, 9, false);
|
|
stack_bitmap->Set(10, true);
|
|
EXPECT_EQ(11, stack_bitmap->Length());
|
|
stack_bitmap->SetLength(kStackSlotCount);
|
|
EXPECT_EQ(kStackSlotCount, stack_bitmap->Length());
|
|
|
|
bool expectation3[kStackSlotCount] = {
|
|
true, false, true, true, true, true, false, false, false, false, true};
|
|
for (intptr_t i = 0; i < kStackSlotCount; ++i) {
|
|
EXPECT_EQ(expectation3[i], stack_bitmap->Get(i));
|
|
}
|
|
// Add a stack map entry at pc offset 3.
|
|
stackmap_table_builder->AddEntry(3, stack_bitmap, 0);
|
|
|
|
const Error& error =
|
|
Error::Handle(Compiler::CompileParsedFunction(parsed_function));
|
|
EXPECT(error.IsNull());
|
|
const Code& code = Code::Handle(test->function().CurrentCode());
|
|
|
|
const Array& stack_maps =
|
|
Array::Handle(stackmap_table_builder->FinalizeStackMaps(code));
|
|
code.set_stackmaps(stack_maps);
|
|
const Array& stack_map_list = Array::Handle(code.stackmaps());
|
|
EXPECT(!stack_map_list.IsNull());
|
|
StackMap& stack_map = StackMap::Handle();
|
|
EXPECT_EQ(4, stack_map_list.Length());
|
|
|
|
// Validate the first stack map entry.
|
|
stack_map ^= stack_map_list.At(0);
|
|
EXPECT_EQ(kStackSlotCount, stack_map.Length());
|
|
for (intptr_t i = 0; i < kStackSlotCount; ++i) {
|
|
EXPECT_EQ(expectation0[i], stack_map.IsObject(i));
|
|
}
|
|
|
|
// Validate the second stack map entry.
|
|
stack_map ^= stack_map_list.At(1);
|
|
EXPECT_EQ(kStackSlotCount, stack_map.Length());
|
|
for (intptr_t i = 0; i < kStackSlotCount; ++i) {
|
|
EXPECT_EQ(expectation1[i], stack_map.IsObject(i));
|
|
}
|
|
|
|
// Validate the third stack map entry.
|
|
stack_map ^= stack_map_list.At(2);
|
|
EXPECT_EQ(kStackSlotCount, stack_map.Length());
|
|
for (intptr_t i = 0; i < kStackSlotCount; ++i) {
|
|
EXPECT_EQ(expectation2[i], stack_map.IsObject(i));
|
|
}
|
|
|
|
// Validate the fourth stack map entry.
|
|
stack_map ^= stack_map_list.At(3);
|
|
EXPECT_EQ(kStackSlotCount, stack_map.Length());
|
|
for (intptr_t i = 0; i < kStackSlotCount; ++i) {
|
|
EXPECT_EQ(expectation3[i], stack_map.IsObject(i));
|
|
}
|
|
retval = true;
|
|
} else {
|
|
retval = false;
|
|
}
|
|
EXPECT(retval);
|
|
}
|
|
CODEGEN_TEST_RUN(StackMapCodegen, Smi::New(1))
|
|
|
|
|
|
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();"
|
|
" Expect.equals(30, i);"
|
|
" }\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.
|
|
StackMapTableBuilder* stackmap_table_builder = new StackMapTableBuilder();
|
|
EXPECT(stackmap_table_builder != NULL);
|
|
BitmapBuilder* stack_bitmap = new BitmapBuilder();
|
|
EXPECT(stack_bitmap != NULL);
|
|
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);
|
|
while (iter.MoveNext()) {
|
|
stackmap_table_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 Array& stack_maps =
|
|
Array::Handle(stackmap_table_builder->FinalizeStackMaps(code));
|
|
code.set_stackmaps(stack_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());
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
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::Print("[%" Pd "]: Expected: %" Pd " != %" Pd "\n", i,
|
|
token_positions[i], it.TokenPos().value());
|
|
}
|
|
EXPECT(token_positions[i] == it.TokenPos().value());
|
|
i++;
|
|
}
|
|
}
|
|
|
|
} // namespace dart
|