| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32. | 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32. |
| 6 #if defined(TARGET_ARCH_IA32) | 6 #if defined(TARGET_ARCH_IA32) |
| 7 | 7 |
| 8 #include "vm/code_generator.h" | 8 #include "vm/code_generator.h" |
| 9 | 9 |
| 10 #include "lib/error.h" | 10 #include "lib/error.h" |
| (...skipping 1267 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1278 | 1278 |
| 1279 static const Class* CoreClass(const char* c_name) { | 1279 static const Class* CoreClass(const char* c_name) { |
| 1280 const String& class_name = String::Handle(String::NewSymbol(c_name)); | 1280 const String& class_name = String::Handle(String::NewSymbol(c_name)); |
| 1281 const Class& cls = Class::ZoneHandle(Library::Handle( | 1281 const Class& cls = Class::ZoneHandle(Library::Handle( |
| 1282 Library::CoreImplLibrary()).LookupClass(class_name)); | 1282 Library::CoreImplLibrary()).LookupClass(class_name)); |
| 1283 ASSERT(!cls.IsNull()); | 1283 ASSERT(!cls.IsNull()); |
| 1284 return &cls; | 1284 return &cls; |
| 1285 } | 1285 } |
| 1286 | 1286 |
| 1287 | 1287 |
| 1288 // Instance to test is in EAX, test if it is a subtype of type_class. | 1288 // Instance to test is in EAX. Test if its class is in subtype test cache array |
| 1289 // The instance may not be a Smi (test before calling this). | 1289 // and use the result in the array to jump to one of the labels. Fall through |
| 1290 // The instance may not be NULL (test before calling this). | 1290 // if the instance is not in the cache array. |
| 1291 // TODO(srdjan): Implement a quicker subtype check, as type test | 1291 // TODO(srdjan): Implement a quicker subtype check, as type test |
| 1292 // arrays can grow too high, but they may be useful when optimizing | 1292 // arrays can grow too high, but they may be useful when optimizing |
| 1293 // code (type-feedback). | 1293 // code (type-feedback). |
| 1294 void CodeGenerator::GenerateClassTestCache(intptr_t node_id, | 1294 void CodeGenerator::GenerateSubtypeTestCacheLookup(intptr_t node_id, |
| 1295 intptr_t token_index, | 1295 intptr_t token_index, |
| 1296 const Class& type_class, | 1296 const Class& type_class, |
| 1297 Label* is_instance_lbl, | 1297 Label* is_instance_lbl, |
| 1298 Label* is_not_instance_lbl) { | 1298 Label* is_not_instance_lbl) { |
| 1299 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | 1299 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); |
| 1300 const Immediate raw_null = | 1300 const Immediate raw_null = |
| 1301 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 1301 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 1302 Label loop, found_in_cache, runtime_call; | 1302 Label loop, found_in_cache, runtime_call; |
| 1303 // Check immediate equality. | 1303 // Check immediate equality. |
| 1304 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); | 1304 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); |
| 1305 // ECX: instance class. | 1305 // ECX: instance class. |
| 1306 __ CompareObject(ECX, type_class); | 1306 __ CompareObject(ECX, type_class); |
| 1307 __ j(EQUAL, is_instance_lbl); | 1307 __ j(EQUAL, is_instance_lbl); |
| 1308 | 1308 |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1356 is_not_instance_lbl); | 1356 is_not_instance_lbl); |
| 1357 // Fall through to runtime call. | 1357 // Fall through to runtime call. |
| 1358 } else { | 1358 } else { |
| 1359 GenerateInstantiatedTypeNoArgumentsTest(node_id, | 1359 GenerateInstantiatedTypeNoArgumentsTest(node_id, |
| 1360 token_index, | 1360 token_index, |
| 1361 type, | 1361 type, |
| 1362 is_instance_lbl, | 1362 is_instance_lbl, |
| 1363 is_not_instance_lbl); | 1363 is_not_instance_lbl); |
| 1364 // If test non-conclusive so far, try the inlined type-test cache. | 1364 // If test non-conclusive so far, try the inlined type-test cache. |
| 1365 // 'type' is known at compile time. | 1365 // 'type' is known at compile time. |
| 1366 GenerateClassTestCache(node_id, token_index, type_class, | 1366 GenerateSubtypeTestCacheLookup(node_id, token_index, type_class, |
| 1367 is_instance_lbl, is_not_instance_lbl); | 1367 is_instance_lbl, is_not_instance_lbl); |
| 1368 } | 1368 } |
| 1369 } else { | 1369 } else { |
| 1370 GenerateUninstantiatedTypeTest(type, | 1370 GenerateUninstantiatedTypeTest(type, |
| 1371 token_index, | 1371 token_index, |
| 1372 is_instance_lbl); | 1372 is_instance_lbl); |
| 1373 } | 1373 } |
| 1374 } | 1374 } |
| 1375 | 1375 |
| 1376 | 1376 |
| 1377 // If instanceof type test cannot be performed successfully at compile time and | 1377 // If instanceof type test cannot be performed successfully at compile time and |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1504 Label* is_not_instance_lbl) { | 1504 Label* is_not_instance_lbl) { |
| 1505 ASSERT(type.IsInstantiated()); | 1505 ASSERT(type.IsInstantiated()); |
| 1506 const Class& type_class = Class::ZoneHandle(type.type_class()); | 1506 const Class& type_class = Class::ZoneHandle(type.type_class()); |
| 1507 ASSERT(type_class.HasTypeArguments()); | 1507 ASSERT(type_class.HasTypeArguments()); |
| 1508 // A Smi object cannot be the instance of a parameterized class. | 1508 // A Smi object cannot be the instance of a parameterized class. |
| 1509 // A class equality check is only applicable with a dst type of a | 1509 // A class equality check is only applicable with a dst type of a |
| 1510 // non-parameterized class or with a raw dst type of a parameterized class. | 1510 // non-parameterized class or with a raw dst type of a parameterized class. |
| 1511 __ testl(EAX, Immediate(kSmiTagMask)); | 1511 __ testl(EAX, Immediate(kSmiTagMask)); |
| 1512 __ j(ZERO, is_not_instance_lbl); | 1512 __ j(ZERO, is_not_instance_lbl); |
| 1513 const AbstractTypeArguments& type_arguments = | 1513 const AbstractTypeArguments& type_arguments = |
| 1514 AbstractTypeArguments::Handle(type.arguments()); | 1514 AbstractTypeArguments::ZoneHandle(type.arguments()); |
| 1515 const bool is_raw_type = type_arguments.IsNull() || | 1515 const bool is_raw_type = type_arguments.IsNull() || |
| 1516 type_arguments.IsRaw(type_arguments.Length()); | 1516 type_arguments.IsRaw(type_arguments.Length()); |
| 1517 if (is_raw_type) { | 1517 if (is_raw_type) { |
| 1518 // Dynamic type argument, check only classes. | 1518 // Dynamic type argument, check only classes. |
| 1519 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); | 1519 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); |
| 1520 if (type.IsListInterface()) { | 1520 if (type.IsListInterface()) { |
| 1521 // TODO(srdjan) also accept List<Object>. | 1521 // TODO(srdjan) also accept List<Object>. |
| 1522 __ CompareObject(ECX, *CoreClass("ObjectArray")); | 1522 __ CompareObject(ECX, *CoreClass("ObjectArray")); |
| 1523 __ j(EQUAL, is_instance_lbl); | 1523 __ j(EQUAL, is_instance_lbl); |
| 1524 __ CompareObject(ECX, *CoreClass("GrowableObjectArray")); | 1524 __ CompareObject(ECX, *CoreClass("GrowableObjectArray")); |
| 1525 __ j(EQUAL, is_instance_lbl); | 1525 __ j(EQUAL, is_instance_lbl); |
| 1526 } | 1526 } |
| 1527 GenerateClassTestCache(node_id, token_index, type_class, | 1527 GenerateSubtypeTestCacheLookup(node_id, token_index, type_class, |
| 1528 is_instance_lbl, is_not_instance_lbl); | 1528 is_instance_lbl, is_not_instance_lbl); |
| 1529 return; |
| 1529 } | 1530 } |
| 1530 // TODO(srdjan): do type test on type arguments. | 1531 // Note that the test below must be synced with the tests in |
| 1531 // Fall through if type test is not conclusive | 1532 // CodeGenerator::UpdateTestCache. |
| 1533 // Inline checks for one type argument only. |
| 1534 if (type_arguments.Length() != 1) { |
| 1535 return; |
| 1536 } |
| 1537 const AbstractType& tp_argument = |
| 1538 AbstractType::ZoneHandle(type_arguments.TypeAt(0)); |
| 1539 if (!tp_argument.IsType()) { |
| 1540 // E.g., it is a TypeParameter. |
| 1541 return; |
| 1542 } |
| 1543 // Malformed type has been caught in the caller chain of this function. |
| 1544 ASSERT(tp_argument.HasResolvedTypeClass()); |
| 1545 // Check if type argument is dynamic or Object. |
| 1546 const Type& object_type = |
| 1547 Type::Handle(Isolate::Current()->object_store()->object_type()); |
| 1548 Error& malformed_error = Error::Handle(); |
| 1549 if (object_type.IsSubtypeOf(tp_argument, &malformed_error)) { |
| 1550 // Instance class test only necessary. |
| 1551 GenerateSubtypeTestCacheLookup(node_id, token_index, type_class, |
| 1552 is_instance_lbl, is_not_instance_lbl); |
| 1553 return; |
| 1554 } |
| 1555 // Efficient type argument check can be done only on known classes. |
| 1556 const Type& list_type = |
| 1557 Type::Handle(Isolate::Current()->object_store()->list_interface()); |
| 1558 Label inlined_check, fall_through; |
| 1559 if (!list_type.IsSubtypeOf(type, &malformed_error)) { |
| 1560 // Since we support only known subtypes of List, no need to emit code |
| 1561 // otherwise. |
| 1562 return; |
| 1563 } |
| 1564 // TODO(srdjan): Recognize somehow classes that are 'regular' i.e., which |
| 1565 // type-parameters are simple structure. |
| 1566 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); |
| 1567 __ CompareObject(ECX, *CoreClass("ObjectArray")); |
| 1568 __ j(EQUAL, &inlined_check); |
| 1569 __ CompareObject(ECX, *CoreClass("GrowableObjectArray")); |
| 1570 __ j(NOT_EQUAL, &fall_through); |
| 1571 __ Bind(&inlined_check); |
| 1572 |
| 1573 // First step is to check instance class. |
| 1574 Label check_type_argument; |
| 1575 GenerateSubtypeTestCacheLookup(node_id, token_index, type_class, |
| 1576 &check_type_argument, is_not_instance_lbl); |
| 1577 // Class test not conclusive (fall-through). |
| 1578 __ jmp(&fall_through); |
| 1579 __ Bind(&check_type_argument); |
| 1580 // Get type argument of instance. |
| 1581 const Class& tp_argument_class = Class::ZoneHandle(tp_argument.type_class()); |
| 1582 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); |
| 1583 __ movl(EDI, FieldAddress(ECX, |
| 1584 tp_argument_class.type_arguments_instance_field_offset_offset())); |
| 1585 // EDI: intptr_t offset of type arguments in instance. |
| 1586 __ cmpl(EDI, Immediate(Class::kNoTypeArguments)); |
| 1587 __ j(EQUAL, is_instance_lbl); |
| 1588 __ movl(EDI, FieldAddress(EAX, EDI, TIMES_1, 0)); |
| 1589 // EDI: type arguments of the instance. |
| 1590 // Is type argument dynamic? |
| 1591 const Immediate raw_null = |
| 1592 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 1593 __ cmpl(EDI, raw_null); |
| 1594 __ j(EQUAL, is_instance_lbl); |
| 1595 // Type arguments may be canonicalized: are they equal? |
| 1596 __ CompareObject(EDI, type_arguments); |
| 1597 __ j(EQUAL, is_instance_lbl); |
| 1598 |
| 1599 // We can handle only type of class TypeArguments. |
| 1600 __ movl(EDX, FieldAddress(EDI, Object::class_offset())); |
| 1601 __ CompareObject(EDX, |
| 1602 Object::ZoneHandle(Object::type_arguments_class())); |
| 1603 __ j(NOT_EQUAL, &fall_through); |
| 1604 // EDI: instance of class 'TypeArguments'. |
| 1605 __ movl(EDX, FieldAddress(EDI, TypeArguments::length_offset())); |
| 1606 // Handling only tests with one type argument. |
| 1607 Immediate smi_one_imm = |
| 1608 Immediate(reinterpret_cast<int32_t>(Smi::New(1))); |
| 1609 __ cmpl(EDX, smi_one_imm); |
| 1610 __ j(NOT_EQUAL, &fall_through); |
| 1611 __ movl(ECX, FieldAddress(EDI, TypeArguments::type_at_offset(0))); |
| 1612 __ CompareObject(ECX, tp_argument); |
| 1613 __ j(EQUAL, is_instance_lbl); |
| 1614 // If the type is not parameterized do the subclass check. |
| 1615 if (!tp_argument_class.HasTypeArguments()) { |
| 1616 __ LoadObject(EDX, tp_argument_class); |
| 1617 __ movl(ECX, FieldAddress(ECX, Type::type_class_offset())); |
| 1618 __ cmpl(ECX, EDX); |
| 1619 __ j(EQUAL, is_instance_lbl); |
| 1620 // A non-parameterized class is in EDX, compare with class in ECX |
| 1621 // EAX, EDX are preserved in stub. |
| 1622 __ call(&StubCode::IsRawSubTypeLabel()); |
| 1623 // Result in EBX: 1 is raw subtype. |
| 1624 __ cmpl(EBX, Immediate(1)); |
| 1625 __ j(EQUAL, is_instance_lbl); |
| 1626 } |
| 1627 // Fall through if type test is not conclusive. |
| 1628 __ Bind(&fall_through); |
| 1532 } | 1629 } |
| 1533 | 1630 |
| 1534 | 1631 |
| 1535 // EAX: instance to test. | 1632 // EAX: instance to test. |
| 1536 // Clobbers: EBX, ECX, EDX. | 1633 // Clobbers: EBX, ECX, EDX. |
| 1537 void CodeGenerator::GenerateInstantiatedTypeNoArgumentsTest( | 1634 void CodeGenerator::GenerateInstantiatedTypeNoArgumentsTest( |
| 1538 intptr_t node_id, | 1635 intptr_t node_id, |
| 1539 intptr_t token_index, | 1636 intptr_t token_index, |
| 1540 const AbstractType& type, | 1637 const AbstractType& type, |
| 1541 Label* is_instance_lbl, | 1638 Label* is_instance_lbl, |
| (...skipping 1377 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2919 const Error& error = Error::Handle( | 3016 const Error& error = Error::Handle( |
| 2920 Parser::FormatError(script, token_index, "Error", format, args)); | 3017 Parser::FormatError(script, token_index, "Error", format, args)); |
| 2921 va_end(args); | 3018 va_end(args); |
| 2922 Isolate::Current()->long_jump_base()->Jump(1, error); | 3019 Isolate::Current()->long_jump_base()->Jump(1, error); |
| 2923 UNREACHABLE(); | 3020 UNREACHABLE(); |
| 2924 } | 3021 } |
| 2925 | 3022 |
| 2926 } // namespace dart | 3023 } // namespace dart |
| 2927 | 3024 |
| 2928 #endif // defined TARGET_ARCH_IA32 | 3025 #endif // defined TARGET_ARCH_IA32 |
| OLD | NEW |