| // 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/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 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->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();" |
| " 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. |
| 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 |