// 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/unit_test.h" namespace dart { static const intptr_t kPos = Scanner::kDummyTokenIndex; CODEGEN_TEST_GENERATE(StackmapCodegen, test) { Assembler assembler; const String& function_name = String::ZoneHandle(Symbols::New("test")); Class& cls = Class::ZoneHandle(); const Script& script = Script::Handle(); cls = Class::New(function_name, script, Scanner::kDummyTokenIndex); const Function& function = Function::ZoneHandle( Function::New(function_name, RawFunction::kRegularFunction, true, false, false, false, false, cls, 0)); function.set_result_type(Type::Handle(Type::DynamicType())); const Array& functions = Array::Handle(Array::New(1)); functions.SetAt(0, function); cls.SetFunctions(functions); Library& lib = Library::Handle(Library::CoreLibrary()); lib.AddClass(cls); ParsedFunction* parsed_function = new ParsedFunction(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->set_default_parameter_values(Object::null_array()); parsed_function->AllocateVariables(); bool retval; Isolate* isolate = Isolate::Current(); EXPECT(isolate != NULL); LongJump* base = isolate->long_jump_base(); LongJump jump; isolate->set_long_jump_base(&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(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); isolate->set_long_jump_base(base); } 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); 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(&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); EXPECT(ClassFinalizer::ProcessPendingClasses()); const String& name = String::Handle(String::New(TestCase::url())); const Library& lib = Library::Handle(Library::LookupLibrary(name)); EXPECT(!lib.IsNull()); Class& cls = Class::Handle( lib.LookupClass(String::Handle(Symbols::New("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; for (int i = 0; i < descriptors.Length(); ++i) { if (descriptors.DescriptorKind(i) == PcDescriptors::kUnoptStaticCall) { stackmap_table_builder->AddEntry(descriptors.PC(i) - code.EntryPoint(), 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()); } } // namespace dart