| 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 // Inline tests according to the 'type' being tested. Jump to labels | |
| 1289 // if we can compute the type-test, otherwise fallthrough. | |
| 1290 // EAX: instance to be tested. | |
| 1291 // Clobbers many registers. | |
| 1292 void CodeGenerator::GenerateInlineInstanceof(intptr_t node_id, | |
| 1293 intptr_t token_index, | |
| 1294 const AbstractType& type, | |
| 1295 Label* is_instance_lbl, | |
| 1296 Label* is_not_instance_lbl) { | |
| 1297 if (type.IsInstantiated()) { | |
| 1298 const Class& type_class = Class::ZoneHandle(type.type_class()); | |
| 1299 // A Smi object cannot be the instance of a parameterized class. | |
| 1300 // A class equality check is only applicable with a dst type of a | |
| 1301 // non-parameterized class or with a raw dst type of a parameterized class. | |
| 1302 if (type_class.HasTypeArguments()) { | |
| 1303 GenerateInstantiatedTypeWithArgumentsTest(type, | |
| 1304 is_instance_lbl, | |
| 1305 is_not_instance_lbl); | |
| 1306 // Fall through to runtime call. | |
| 1307 } else { | |
| 1308 GenerateInstantiatedTypeNoArgumentsTest(node_id, | |
| 1309 token_index, | |
| 1310 type, | |
| 1311 is_instance_lbl, | |
| 1312 is_not_instance_lbl); | |
| 1313 // If test non-conclusive so far, try the inlined type-test cache. | |
| 1314 // 'type' is known at compile time. | |
| 1315 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | |
| 1316 const Immediate raw_null = | |
| 1317 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 1318 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); | |
| 1319 AddCurrentDescriptor(PcDescriptors::kTypeTest, node_id, token_index); | |
| 1320 __ LoadObject(EDX, Array::ZoneHandle(Array::New(2))); | |
| 1321 __ addl(EDX, Immediate(Array::data_offset() - kHeapObjectTag)); | |
| 1322 Label loop, found_in_cache, runtime_call; | |
| 1323 __ Bind(&loop); | |
| 1324 __ movl(EBX, Address(EDX, 0)); | |
| 1325 __ cmpl(ECX, EBX); | |
| 1326 __ j(EQUAL, &found_in_cache, Assembler::kNearJump); | |
| 1327 __ addl(EDX, Immediate(kWordSize * 2)); | |
| 1328 __ cmpl(EBX, raw_null); | |
| 1329 __ j(NOT_EQUAL, &loop, Assembler::kNearJump); | |
| 1330 __ jmp(&runtime_call, Assembler::kNearJump); | |
| 1331 | |
| 1332 __ Bind(&found_in_cache); | |
| 1333 __ movl(EDX, Address(EDX, kWordSize)); | |
| 1334 __ CompareObject(EDX, bool_true); | |
| 1335 __ j(EQUAL, is_instance_lbl); | |
| 1336 __ jmp(is_not_instance_lbl); | |
| 1337 __ Bind(&runtime_call); | |
| 1338 } | |
| 1339 } else { | |
| 1340 GenerateUninstantiatedTypeTest(type, | |
| 1341 token_index, | |
| 1342 is_instance_lbl); | |
| 1343 } | |
| 1344 } | |
| 1345 | |
| 1346 | |
| 1347 // If instanceof type test cannot be performed successfully at compile time and | 1288 // If instanceof type test cannot be performed successfully at compile time and |
| 1348 // therefore eliminated, optimize it by adding inlined tests for: | 1289 // therefore eliminated, optimize it by adding inlined tests for: |
| 1349 // - NULL -> return false. | 1290 // - NULL -> return false. |
| 1350 // - Smi -> compile time subtype check (only if dst class is not parameterized). | 1291 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
| 1351 // - Class equality (only if class is not parameterized). | 1292 // - Class equality (only if class is not parameterized). |
| 1352 // Inputs: | 1293 // Inputs: |
| 1353 // - EAX: object. | 1294 // - EAX: object. |
| 1354 // Destroys ECX. | 1295 // Destroys ECX. |
| 1355 // Returns: | 1296 // Returns: |
| 1356 // - true or false on stack. | 1297 // - true or false on stack. |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1393 &malformed_error)) { | 1334 &malformed_error)) { |
| 1394 __ PushObject(negate_result ? bool_false : bool_true); | 1335 __ PushObject(negate_result ? bool_false : bool_true); |
| 1395 } else { | 1336 } else { |
| 1396 ASSERT(malformed_error.IsNull()); | 1337 ASSERT(malformed_error.IsNull()); |
| 1397 __ PushObject(negate_result ? bool_true : bool_false); | 1338 __ PushObject(negate_result ? bool_true : bool_false); |
| 1398 } | 1339 } |
| 1399 } | 1340 } |
| 1400 return; | 1341 return; |
| 1401 } | 1342 } |
| 1402 | 1343 |
| 1403 Label is_instance_of, is_not_instance_of; | |
| 1404 const Immediate raw_null = | 1344 const Immediate raw_null = |
| 1405 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 1345 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 1406 Label done; | 1346 Label done, check_negate_done; |
| 1407 // If type is instantiated and non-parameterized, we can inline code | 1347 // If type is instantiated and non-parameterized, we can inline code |
| 1408 // checking whether the tested instance is a Smi. | 1348 // checking whether the tested instance is a Smi. |
| 1409 if (type.IsInstantiated()) { | 1349 if (type.IsInstantiated()) { |
| 1410 // A null object is only an instance of Object and Dynamic, which has | 1350 // A null object is only an instance of Object and Dynamic, which has |
| 1411 // already been checked above (if the type is instantiated). So we can | 1351 // already been checked above (if the type is instantiated). So we can |
| 1412 // return false here if the instance is null (and if the type is | 1352 // return false here if the instance is null (and if the type is |
| 1413 // instantiated). | 1353 // instantiated). |
| 1414 // We can only inline this null check if the type is instantiated at compile | 1354 // We can only inline this null check if the type is instantiated at compile |
| 1415 // time, since an uninstantiated type at compile time could be Object or | 1355 // time, since an uninstantiated type at compile time could be Object or |
| 1416 // Dynamic at run time. | 1356 // Dynamic at run time. |
| 1417 Label non_null; | 1357 Label non_null; |
| 1418 __ cmpl(EAX, raw_null); | 1358 __ cmpl(EAX, raw_null); |
| 1419 __ j(NOT_EQUAL, &non_null, Assembler::kNearJump); | 1359 __ j(NOT_EQUAL, &non_null, Assembler::kNearJump); |
| 1420 __ PushObject(negate_result ? bool_true : bool_false); | 1360 __ PushObject(negate_result ? bool_true : bool_false); |
| 1421 __ jmp(&done); | 1361 __ jmp(&done, Assembler::kNearJump); |
| 1362 |
| 1422 __ Bind(&non_null); | 1363 __ Bind(&non_null); |
| 1364 |
| 1365 const Class& type_class = Class::ZoneHandle(type.type_class()); |
| 1366 const bool requires_type_arguments = type_class.HasTypeArguments(); |
| 1367 // A Smi object cannot be the instance of a parameterized class. |
| 1368 // A class equality check is only applicable with a dst type of a |
| 1369 // non-parameterized class or with a raw dst type of a parameterized class. |
| 1370 if (requires_type_arguments) { |
| 1371 const AbstractTypeArguments& type_arguments = |
| 1372 AbstractTypeArguments::Handle(type.arguments()); |
| 1373 const bool is_raw_type = type_arguments.IsNull() || |
| 1374 type_arguments.IsRaw(type_arguments.Length()); |
| 1375 Label runtime_call; |
| 1376 __ testl(EAX, Immediate(kSmiTagMask)); |
| 1377 __ j(ZERO, &runtime_call, Assembler::kNearJump); |
| 1378 // Object not Smi. |
| 1379 if (is_raw_type) { |
| 1380 if (type.IsListInterface()) { |
| 1381 Label push_result; |
| 1382 // TODO(srdjan) also accept List<Object>. |
| 1383 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); |
| 1384 __ CompareObject(ECX, *CoreClass("ObjectArray")); |
| 1385 __ j(EQUAL, &push_result, Assembler::kNearJump); |
| 1386 __ CompareObject(ECX, *CoreClass("GrowableObjectArray")); |
| 1387 __ j(NOT_EQUAL, &runtime_call, Assembler::kNearJump); |
| 1388 __ Bind(&push_result); |
| 1389 __ PushObject(negate_result ? bool_false : bool_true); |
| 1390 __ jmp(&done, Assembler::kNearJump); |
| 1391 } else if (!type_class.is_interface()) { |
| 1392 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); |
| 1393 __ CompareObject(ECX, type_class); |
| 1394 __ j(NOT_EQUAL, &runtime_call, Assembler::kNearJump); |
| 1395 __ PushObject(negate_result ? bool_false : bool_true); |
| 1396 __ jmp(&done, Assembler::kNearJump); |
| 1397 } |
| 1398 } |
| 1399 __ Bind(&runtime_call); |
| 1400 // Fall through to runtime call. |
| 1401 } else { |
| 1402 ASSERT(!requires_type_arguments); |
| 1403 // Test if object is Smi and for a couple known test-classes. |
| 1404 Label compare_classes; |
| 1405 __ testl(EAX, Immediate(kSmiTagMask)); |
| 1406 __ j(NOT_ZERO, &compare_classes, Assembler::kNearJump); |
| 1407 // Object is Smi. |
| 1408 const Class& smi_class = Class::Handle(Smi::Class()); |
| 1409 // TODO(regis): We should introduce a SmiType. |
| 1410 Error& malformed_error = Error::Handle(); |
| 1411 if (smi_class.IsSubtypeOf(TypeArguments::Handle(), |
| 1412 type_class, |
| 1413 TypeArguments::Handle(), |
| 1414 &malformed_error)) { |
| 1415 __ PushObject(negate_result ? bool_false : bool_true); |
| 1416 } else { |
| 1417 __ PushObject(negate_result ? bool_true : bool_false); |
| 1418 } |
| 1419 __ jmp(&done, Assembler::kNearJump); |
| 1420 |
| 1421 // Compare if the classes are equal. |
| 1422 __ Bind(&compare_classes); |
| 1423 // Note that in instanceof both positive and negative tests must be fast. |
| 1424 if (type.IsBoolInterface()) { |
| 1425 // Bool interface has only bool class. |
| 1426 Label is_bool; |
| 1427 const Class & bool_class = Class::ZoneHandle( |
| 1428 Isolate::Current()->object_store()->bool_class()); |
| 1429 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); |
| 1430 __ CompareObject(ECX, bool_class); |
| 1431 __ j(EQUAL, &is_bool, Assembler::kNearJump); |
| 1432 __ PushObject(negate_result ? bool_true : bool_false); |
| 1433 __ jmp(&done); |
| 1434 __ Bind(&is_bool); |
| 1435 __ PushObject(negate_result ? bool_false : bool_true); |
| 1436 __ Bind(&done); |
| 1437 return; |
| 1438 } |
| 1439 |
| 1440 // EAX: instance being tested. |
| 1441 // Optional quick equality check and then the type test cache test. |
| 1442 Label loop, runtime_call, found_in_cache, cache_test; |
| 1443 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); |
| 1444 if (!type_class.is_interface()) { |
| 1445 // Class equality test first. |
| 1446 __ CompareObject(ECX, type_class); |
| 1447 __ j(NOT_EQUAL, &cache_test, Assembler::kNearJump); |
| 1448 // Equal! |
| 1449 __ PushObject(negate_result ? bool_false : bool_true); |
| 1450 __ jmp(&done, Assembler::kNearJump); |
| 1451 } |
| 1452 __ Bind(&cache_test); |
| 1453 // TODO(srdjan): Convert other tests to this simple form. |
| 1454 // Class we are testing against has no type arguments. |
| 1455 // TODO(srdjan): Canonicalize initial array? |
| 1456 // TODO(srdjan): Account for 'negate_result' in cache. |
| 1457 // The type test array is null-terminated. Two consecutive |
| 1458 // array elements correspond to one test: |
| 1459 // array[i + 0] : class. |
| 1460 // array[i + 1] : result for the class. |
| 1461 AddCurrentDescriptor(PcDescriptors::kTypeTest, node_id, token_index); |
| 1462 __ LoadObject(EDX, Array::ZoneHandle(Array::New(2))); |
| 1463 __ addl(EDX, Immediate(Array::data_offset() - kHeapObjectTag)); |
| 1464 __ Bind(&loop); |
| 1465 __ movl(EBX, Address(EDX, 0)); |
| 1466 __ cmpl(ECX, EBX); |
| 1467 __ j(EQUAL, &found_in_cache, Assembler::kNearJump); |
| 1468 __ addl(EDX, Immediate(kWordSize * 2)); |
| 1469 __ cmpl(EBX, raw_null); |
| 1470 __ j(NOT_EQUAL, &loop, Assembler::kNearJump); |
| 1471 __ jmp(&runtime_call, Assembler::kNearJump); |
| 1472 __ Bind(&found_in_cache); |
| 1473 __ pushl(Address(EDX, kWordSize)); |
| 1474 __ jmp(&check_negate_done); |
| 1475 __ Bind(&runtime_call); |
| 1476 } |
| 1423 } | 1477 } |
| 1424 | |
| 1425 GenerateInlineInstanceof(node_id, token_index, type, | |
| 1426 &is_instance_of, &is_not_instance_of); | |
| 1427 | |
| 1428 __ PushObject(Object::ZoneHandle()); // Make room for the result. | 1478 __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| 1429 const Immediate location = | 1479 const Immediate location = |
| 1430 Immediate(reinterpret_cast<int32_t>(Smi::New(token_index))); | 1480 Immediate(reinterpret_cast<int32_t>(Smi::New(token_index))); |
| 1431 const Immediate node_id_as_smi = | 1481 const Immediate node_id_as_smi = |
| 1432 Immediate(reinterpret_cast<int32_t>(Smi::New(node_id))); | 1482 Immediate(reinterpret_cast<int32_t>(Smi::New(node_id))); |
| 1433 __ pushl(location); // Push the source location. | 1483 __ pushl(location); // Push the source location. |
| 1434 __ pushl(node_id_as_smi); // node-id. | 1484 __ pushl(node_id_as_smi); // node-id. |
| 1435 __ pushl(EAX); // Push the instance. | 1485 __ pushl(EAX); // Push the instance. |
| 1436 __ PushObject(type); // Push the type. | 1486 __ PushObject(type); // Push the type. |
| 1437 if (!type.IsInstantiated()) { | 1487 if (!type.IsInstantiated()) { |
| 1438 GenerateInstantiatorTypeArguments(token_index); | 1488 GenerateInstantiatorTypeArguments(token_index); |
| 1439 } else { | 1489 } else { |
| 1440 __ pushl(raw_null); // Null instantiator. | 1490 __ pushl(raw_null); // Null instantiator. |
| 1441 } | 1491 } |
| 1442 GenerateCallRuntime(node_id, token_index, kInstanceofRuntimeEntry); | 1492 GenerateCallRuntime(node_id, token_index, kInstanceofRuntimeEntry); |
| 1443 // Pop the two parameters supplied to the runtime entry. The result of the | 1493 // Pop the two parameters supplied to the runtime entry. The result of the |
| 1444 // instanceof runtime call will be left as the result of the operation. | 1494 // instanceof runtime call will be left as the result of the operation. |
| 1445 __ addl(ESP, Immediate(5 * kWordSize)); | 1495 __ addl(ESP, Immediate(5 * kWordSize)); |
| 1446 __ jmp(&done); | 1496 __ Bind(&check_negate_done); |
| 1447 | 1497 if (negate_result) { |
| 1448 __ Bind(&is_instance_of); | 1498 Label negate_done; |
| 1449 __ PushObject(negate_result ? bool_false: bool_true); | 1499 __ popl(EDX); |
| 1450 __ jmp(&done); | 1500 __ LoadObject(EAX, bool_true); |
| 1451 __ Bind(&is_not_instance_of); | 1501 __ cmpl(EDX, EAX); |
| 1452 __ PushObject(negate_result ? bool_true: bool_false); | 1502 __ j(NOT_EQUAL, &negate_done, Assembler::kNearJump); |
| 1503 __ LoadObject(EAX, bool_false); |
| 1504 __ Bind(&negate_done); |
| 1505 __ pushl(EAX); |
| 1506 } |
| 1453 __ Bind(&done); | 1507 __ Bind(&done); |
| 1454 } | 1508 } |
| 1455 | 1509 |
| 1456 | 1510 |
| 1457 // EAX: instance to test. | 1511 // Jumps to label if ECX equals the given class. |
| 1458 // Clobbers: ECX. | 1512 // Inputs: |
| 1459 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if | 1513 // - ECX: tested class. |
| 1460 // type test is conclusive, otherwise fallthrough if a type test could not | 1514 void CodeGenerator::TestClassAndJump(const Class& cls, Label* label) { |
| 1461 // be completed. | 1515 __ CompareObject(ECX, cls); |
| 1462 void CodeGenerator::GenerateInstantiatedTypeWithArgumentsTest( | 1516 __ j(EQUAL, label, Assembler::kNearJump); |
| 1463 const AbstractType& type, | |
| 1464 Label* is_instance_lbl, | |
| 1465 Label* is_not_instance_lbl) { | |
| 1466 ASSERT(type.IsInstantiated()); | |
| 1467 const Class& type_class = Class::ZoneHandle(type.type_class()); | |
| 1468 ASSERT(type_class.HasTypeArguments()); | |
| 1469 // A Smi object cannot be the instance of a parameterized class. | |
| 1470 // A class equality check is only applicable with a dst type of a | |
| 1471 // non-parameterized class or with a raw dst type of a parameterized class. | |
| 1472 __ testl(EAX, Immediate(kSmiTagMask)); | |
| 1473 __ j(ZERO, is_not_instance_lbl); | |
| 1474 const AbstractTypeArguments& type_arguments = | |
| 1475 AbstractTypeArguments::Handle(type.arguments()); | |
| 1476 const bool is_raw_type = type_arguments.IsNull() || | |
| 1477 type_arguments.IsRaw(type_arguments.Length()); | |
| 1478 if (is_raw_type) { | |
| 1479 // Dynamic type argument, check only classes. | |
| 1480 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); | |
| 1481 if (type.IsListInterface()) { | |
| 1482 // TODO(srdjan) also accept List<Object>. | |
| 1483 __ CompareObject(ECX, *CoreClass("ObjectArray")); | |
| 1484 __ j(EQUAL, is_instance_lbl); | |
| 1485 __ CompareObject(ECX, *CoreClass("GrowableObjectArray")); | |
| 1486 __ j(EQUAL, is_instance_lbl); | |
| 1487 } else if (!type_class.is_interface()) { | |
| 1488 __ CompareObject(ECX, type_class); | |
| 1489 __ j(EQUAL, is_instance_lbl); | |
| 1490 } | |
| 1491 } else { | |
| 1492 // TODO(srdjan): do type test on type arguments. | |
| 1493 } | |
| 1494 // Fall through if type test is not conclusive | |
| 1495 } | |
| 1496 | |
| 1497 | |
| 1498 // EAX: instance to test. | |
| 1499 // Clobbers: EBX, ECX, EDX. | |
| 1500 void CodeGenerator::GenerateInstantiatedTypeNoArgumentsTest( | |
| 1501 intptr_t node_id, | |
| 1502 intptr_t token_index, | |
| 1503 const AbstractType& type, | |
| 1504 Label* is_instance_lbl, | |
| 1505 Label* is_not_instance_lbl) { | |
| 1506 ASSERT(type.IsInstantiated()); | |
| 1507 const Class& type_class = Class::ZoneHandle(type.type_class()); | |
| 1508 ASSERT(!type_class.HasTypeArguments()); | |
| 1509 | |
| 1510 Label compare_classes; | |
| 1511 __ testl(EAX, Immediate(kSmiTagMask)); | |
| 1512 __ j(NOT_ZERO, &compare_classes, Assembler::kNearJump); | |
| 1513 // Instance is Smi, check directly. | |
| 1514 const Class& smi_class = Class::Handle(Smi::Class()); | |
| 1515 // TODO(regis): We should introduce a SmiType. | |
| 1516 Error& malformed_error = Error::Handle(); | |
| 1517 if (smi_class.IsSubtypeOf(TypeArguments::Handle(), | |
| 1518 type_class, | |
| 1519 TypeArguments::Handle(), | |
| 1520 &malformed_error)) { | |
| 1521 __ jmp(is_instance_lbl); | |
| 1522 } else { | |
| 1523 __ jmp(is_not_instance_lbl); | |
| 1524 } | |
| 1525 // Compare if the classes are equal. | |
| 1526 __ Bind(&compare_classes); | |
| 1527 | |
| 1528 // Checking against interface. | |
| 1529 // However, for specific core library interfaces, we can check for | |
| 1530 // specific core library classes. | |
| 1531 if (type.IsBoolInterface()) { | |
| 1532 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); | |
| 1533 const Class& bool_class = Class::ZoneHandle( | |
| 1534 Isolate::Current()->object_store()->bool_class()); | |
| 1535 __ CompareObject(ECX, bool_class); | |
| 1536 __ j(EQUAL, is_instance_lbl); | |
| 1537 return; | |
| 1538 } | |
| 1539 // If type is an interface, we can skip the class equality check, | |
| 1540 // because instances cannot be of an interface type. | |
| 1541 if (!type_class.is_interface()) { | |
| 1542 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); | |
| 1543 __ CompareObject(ECX, type_class); | |
| 1544 __ j(EQUAL, is_instance_lbl); | |
| 1545 } | |
| 1546 if (type.IsSubtypeOf( | |
| 1547 Type::Handle(Type::NumberInterface()), &malformed_error)) { | |
| 1548 // Custom checking for numbers (Smi, Mint, Bigint and Double) | |
| 1549 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); | |
| 1550 if (type.IsIntInterface() || type.IsNumberInterface()) { | |
| 1551 // We already checked for Smi above. | |
| 1552 const Class& mint_class = Class::ZoneHandle( | |
| 1553 Isolate::Current()->object_store()->mint_class()); | |
| 1554 const Class& bigint_class = Class::ZoneHandle( | |
| 1555 Isolate::Current()->object_store()->bigint_class()); | |
| 1556 __ CompareObject(ECX, mint_class); | |
| 1557 __ j(EQUAL, is_instance_lbl); | |
| 1558 __ CompareObject(ECX, bigint_class); | |
| 1559 __ j(EQUAL, is_instance_lbl); | |
| 1560 } | |
| 1561 if (type.IsDoubleInterface() || type.IsNumberInterface()) { | |
| 1562 const Class& double_class = Class::ZoneHandle( | |
| 1563 Isolate::Current()->object_store()->double_class()); | |
| 1564 __ CompareObject(ECX, double_class); | |
| 1565 __ j(EQUAL, is_instance_lbl); | |
| 1566 } | |
| 1567 } else if (type.IsStringInterface()) { | |
| 1568 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); | |
| 1569 const Class& one_byte_string_class = Class::ZoneHandle( | |
| 1570 Isolate::Current()->object_store()->one_byte_string_class()); | |
| 1571 const Class& two_byte_string_class = Class::ZoneHandle( | |
| 1572 Isolate::Current()->object_store()->two_byte_string_class()); | |
| 1573 const Class& four_byte_string_class = Class::ZoneHandle( | |
| 1574 Isolate::Current()->object_store()->four_byte_string_class()); | |
| 1575 __ CompareObject(ECX, one_byte_string_class); | |
| 1576 __ j(EQUAL, is_instance_lbl); | |
| 1577 __ CompareObject(ECX, two_byte_string_class); | |
| 1578 __ j(EQUAL, is_instance_lbl); | |
| 1579 __ CompareObject(ECX, four_byte_string_class); | |
| 1580 __ j(EQUAL, is_instance_lbl); | |
| 1581 } else if (type.IsFunctionInterface()) { | |
| 1582 // Check if instance is a closure. | |
| 1583 const Immediate raw_null = | |
| 1584 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 1585 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); | |
| 1586 __ movl(ECX, FieldAddress(ECX, Class::signature_function_offset())); | |
| 1587 __ cmpl(ECX, raw_null); | |
| 1588 __ j(NOT_EQUAL, is_instance_lbl); | |
| 1589 } | |
| 1590 // Otherwise fallthrough. | |
| 1591 } | |
| 1592 | |
| 1593 | |
| 1594 // EAX: instance to test. | |
| 1595 // Clobbers: EBX, EDX, ECX. | |
| 1596 void CodeGenerator::GenerateUninstantiatedTypeTest( | |
| 1597 const AbstractType& type, | |
| 1598 intptr_t token_index, | |
| 1599 Label* is_instance_lbl) { | |
| 1600 ASSERT(!type.IsInstantiated()); | |
| 1601 // Skip check if destination is a dynamic type. | |
| 1602 if (type.IsTypeParameter()) { | |
| 1603 const Immediate raw_null = | |
| 1604 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 1605 // EAX must be preserved! | |
| 1606 Label fall_through; | |
| 1607 GenerateInstantiatorTypeArguments(token_index); | |
| 1608 // Type arguments are on stack. | |
| 1609 __ popl(EBX); | |
| 1610 // Check if type argument is dynamic. | |
| 1611 __ cmpl(EBX, raw_null); | |
| 1612 __ j(EQUAL, is_instance_lbl); | |
| 1613 | |
| 1614 // For now handle only TypeArguments and bail out if InstantiatedTypeArgs. | |
| 1615 __ movl(EDX, FieldAddress(EBX, Object::class_offset())); | |
| 1616 __ CompareObject(EDX, Object::ZoneHandle(Object::type_arguments_class())); | |
| 1617 __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump); | |
| 1618 | |
| 1619 // EBX: Instance of TypeArguments. | |
| 1620 __ movl(EDX, | |
| 1621 FieldAddress(EBX, TypeArguments::type_at_offset(type.Index()))); | |
| 1622 // EDX: concrete type of type. | |
| 1623 // Check if type argument is dynamic. | |
| 1624 __ CompareObject(EDX, Type::ZoneHandle(Type::DynamicType())); | |
| 1625 __ j(EQUAL, is_instance_lbl); | |
| 1626 | |
| 1627 // Check if the type has type parameters, if not do the class comparison. | |
| 1628 Label not_smi; | |
| 1629 __ testl(EAX, Immediate(kSmiTagMask)); // Value is Smi? | |
| 1630 __ j(NOT_ZERO, ¬_smi, Assembler::kNearJump); | |
| 1631 // For Smi check quickly against int and num interfaces. | |
| 1632 __ CompareObject(EDX, Type::ZoneHandle(Type::IntInterface())); | |
| 1633 __ j(EQUAL, is_instance_lbl); | |
| 1634 __ CompareObject(EDX, Type::ZoneHandle(Type::NumberInterface())); | |
| 1635 __ j(EQUAL, is_instance_lbl); | |
| 1636 __ jmp(&fall_through); | |
| 1637 | |
| 1638 __ Bind(¬_smi); | |
| 1639 // The instantiated type parameter may not be a Type, but could be an | |
| 1640 // InstantiatedType. It is therefore necessary to check its class. | |
| 1641 __ movl(ECX, FieldAddress(EDX, Object::class_offset())); | |
| 1642 __ CompareObject(ECX, Object::ZoneHandle(Object::type_class())); | |
| 1643 __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump); | |
| 1644 | |
| 1645 __ movl(EDX, FieldAddress(EDX, Type::type_class_offset())); | |
| 1646 __ movl(ECX, FieldAddress(EDX, Class::type_parameters_offset())); | |
| 1647 // Check that class of type has no type parameters. | |
| 1648 __ cmpl(ECX, raw_null); | |
| 1649 __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump); | |
| 1650 // We have a non-parameterized class in EDX, compare with class of | |
| 1651 // value in EAX. EAX, EDX are preserved in stub. | |
| 1652 __ call(&StubCode::IsRawSubTypeLabel()); | |
| 1653 // Result in EBX: 1 is raw subtype. | |
| 1654 __ cmpl(EBX, Immediate(1)); | |
| 1655 __ j(EQUAL, is_instance_lbl); | |
| 1656 | |
| 1657 // Test not conclusive. | |
| 1658 __ Bind(&fall_through); | |
| 1659 } | |
| 1660 } | 1517 } |
| 1661 | 1518 |
| 1662 | 1519 |
| 1663 // If type check cannot be performed successfully at compile time and therefore | 1520 // If type check cannot be performed successfully at compile time and therefore |
| 1664 // eliminated, optimize it by adding inlined tests for: | 1521 // eliminated, optimize it by adding inlined tests for: |
| 1665 // - NULL -> return NULL. | 1522 // - NULL -> return NULL. |
| 1666 // - Smi -> compile time subtype check (only if dst class is not parameterized). | 1523 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
| 1667 // - Class equality (only if class is not parameterized). | 1524 // - Class equality (only if class is not parameterized). |
| 1668 // Inputs: | 1525 // Inputs: |
| 1669 // - EAX: object. | 1526 // - EAX: object. |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1738 __ PushObject(dst_name); // Push the name of the destination. | 1595 __ PushObject(dst_name); // Push the name of the destination. |
| 1739 __ PushObject(error_message); | 1596 __ PushObject(error_message); |
| 1740 GenerateCallRuntime(node_id, token_index, kMalformedTypeErrorRuntimeEntry); | 1597 GenerateCallRuntime(node_id, token_index, kMalformedTypeErrorRuntimeEntry); |
| 1741 // We should never return here. | 1598 // We should never return here. |
| 1742 __ int3(); | 1599 __ int3(); |
| 1743 | 1600 |
| 1744 __ Bind(&done); // For a null object. | 1601 __ Bind(&done); // For a null object. |
| 1745 return; | 1602 return; |
| 1746 } | 1603 } |
| 1747 | 1604 |
| 1748 GenerateInlineInstanceof(node_id, token_index, dst_type, | 1605 // If dst_type is instantiated and non-parameterized, we can inline code |
| 1749 &done, &runtime_call); | 1606 // checking whether the assigned instance is a Smi. |
| 1607 if (dst_type.IsInstantiated()) { |
| 1608 const Class& dst_type_class = Class::ZoneHandle(dst_type.type_class()); |
| 1609 const bool dst_class_has_type_arguments = dst_type_class.HasTypeArguments(); |
| 1610 // A Smi object cannot be the instance of a parameterized class. |
| 1611 // A class equality check is only applicable with a dst type of a |
| 1612 // non-parameterized class or with a raw dst type of a parameterized class. |
| 1613 if (dst_class_has_type_arguments) { |
| 1614 const AbstractTypeArguments& dst_type_arguments = |
| 1615 AbstractTypeArguments::Handle(dst_type.arguments()); |
| 1616 const bool is_raw_dst_type = dst_type_arguments.IsNull() || |
| 1617 dst_type_arguments.IsRaw(dst_type_arguments.Length()); |
| 1618 if (is_raw_dst_type) { |
| 1619 // Dynamic type argument, check only classes. |
| 1620 if (dst_type.IsListInterface()) { |
| 1621 // TODO(srdjan) also accept List<Object>. |
| 1622 __ testl(EAX, Immediate(kSmiTagMask)); |
| 1623 __ j(ZERO, &runtime_call, Assembler::kNearJump); |
| 1624 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); |
| 1625 TestClassAndJump(*CoreClass("ObjectArray"), &done); |
| 1626 TestClassAndJump(*CoreClass("GrowableObjectArray"), &done); |
| 1627 } else if (!dst_type_class.is_interface()) { |
| 1628 __ testl(EAX, Immediate(kSmiTagMask)); |
| 1629 __ j(ZERO, &runtime_call, Assembler::kNearJump); |
| 1630 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); |
| 1631 TestClassAndJump(dst_type_class, &done); |
| 1632 } |
| 1633 // Fall through to runtime class. |
| 1634 } |
| 1635 } else { // dst_type has NO type arguments. |
| 1636 Label compare_classes; |
| 1637 __ testl(EAX, Immediate(kSmiTagMask)); |
| 1638 __ j(NOT_ZERO, &compare_classes, Assembler::kNearJump); |
| 1639 // Object is Smi. |
| 1640 const Class& smi_class = Class::Handle(Smi::Class()); |
| 1641 // TODO(regis): We should introduce a SmiType. |
| 1642 Error& malformed_error = Error::Handle(); |
| 1643 if (smi_class.IsSubtypeOf(TypeArguments::Handle(), |
| 1644 dst_type_class, |
| 1645 TypeArguments::Handle(), |
| 1646 &malformed_error)) { |
| 1647 // Successful assignable type check: return object in EAX. |
| 1648 __ jmp(&done, Assembler::kNearJump); |
| 1649 } else { |
| 1650 // Failed assignable type check: call runtime to throw TypeError. |
| 1651 __ jmp(&runtime_call, Assembler::kNearJump); |
| 1652 } |
| 1653 // Compare if the classes are equal. |
| 1654 __ Bind(&compare_classes); |
| 1655 // If dst_type is an interface, we can skip the class equality check, |
| 1656 // because instances cannot be of an interface type. |
| 1657 if (!dst_type_class.is_interface()) { |
| 1658 __ LoadObject(EDX, dst_type_class); |
| 1659 __ cmpl(EDX, ECX); |
| 1660 __ j(EQUAL, &done, Assembler::kNearJump); |
| 1661 // EAX, EDX are preserved in stub, result is in EBX. |
| 1662 __ call(&StubCode::IsRawSubTypeLabel()); |
| 1663 // Result in EBX: 1 is raw subtype. |
| 1664 __ cmpl(EBX, Immediate(1)); |
| 1665 __ j(EQUAL, &done, Assembler::kNearJump); |
| 1666 // Otherwise fallthrough |
| 1667 } else { |
| 1668 // However, for specific core library interfaces, we can check for |
| 1669 // specific core library classes. |
| 1670 Error& malformed_error = Error::Handle(); |
| 1671 if (dst_type.IsBoolInterface()) { |
| 1672 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); |
| 1673 const Class& bool_class = Class::ZoneHandle( |
| 1674 Isolate::Current()->object_store()->bool_class()); |
| 1675 TestClassAndJump(bool_class, &done); |
| 1676 } else if (dst_type.IsSubtypeOf( |
| 1677 Type::Handle(Type::NumberInterface()), &malformed_error)) { |
| 1678 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); |
| 1679 if (dst_type.IsIntInterface() || dst_type.IsNumberInterface()) { |
| 1680 // We already checked for Smi above. |
| 1681 const Class& mint_class = Class::ZoneHandle( |
| 1682 Isolate::Current()->object_store()->mint_class()); |
| 1683 TestClassAndJump(mint_class, &done); |
| 1684 const Class& bigint_class = Class::ZoneHandle( |
| 1685 Isolate::Current()->object_store()->bigint_class()); |
| 1686 TestClassAndJump(bigint_class, &done); |
| 1687 } |
| 1688 if (dst_type.IsDoubleInterface() || dst_type.IsNumberInterface()) { |
| 1689 const Class& double_class = Class::ZoneHandle( |
| 1690 Isolate::Current()->object_store()->double_class()); |
| 1691 TestClassAndJump(double_class, &done); |
| 1692 } |
| 1693 } else if (dst_type.IsStringInterface()) { |
| 1694 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); |
| 1695 const Class& one_byte_string_class = Class::ZoneHandle( |
| 1696 Isolate::Current()->object_store()->one_byte_string_class()); |
| 1697 TestClassAndJump(one_byte_string_class, &done); |
| 1698 const Class& two_byte_string_class = Class::ZoneHandle( |
| 1699 Isolate::Current()->object_store()->two_byte_string_class()); |
| 1700 TestClassAndJump(two_byte_string_class, &done); |
| 1701 const Class& four_byte_string_class = Class::ZoneHandle( |
| 1702 Isolate::Current()->object_store()->four_byte_string_class()); |
| 1703 TestClassAndJump(four_byte_string_class, &done); |
| 1704 } else if (dst_type.IsFunctionInterface()) { |
| 1705 __ movl(ECX, FieldAddress(EAX, Object::class_offset())); |
| 1706 __ movl(ECX, FieldAddress(ECX, Class::signature_function_offset())); |
| 1707 __ cmpl(ECX, raw_null); |
| 1708 __ j(NOT_EQUAL, &done, Assembler::kNearJump); |
| 1709 } else { |
| 1710 __ LoadObject(EDX, dst_type_class); |
| 1711 // EAX: Instance (preserved). |
| 1712 // EDX: test class (preserved). |
| 1713 __ call(&StubCode::IsRawSubTypeLabel()); |
| 1714 // Result in EBX: 1 is raw subtype. |
| 1715 __ cmpl(EBX, Immediate(1)); |
| 1716 __ j(EQUAL, &done, Assembler::kNearJump); |
| 1717 // Otherwise fallthrough |
| 1718 } |
| 1719 } |
| 1720 } |
| 1721 } else { |
| 1722 ASSERT(!dst_type.IsInstantiated()); |
| 1723 // Skip check if destination is a dynamic type. |
| 1724 if (dst_type.IsTypeParameter()) { |
| 1725 // EAX must be preserved! |
| 1726 Label fall_through; |
| 1727 GenerateInstantiatorTypeArguments(token_index); |
| 1728 // Type arguments are on stack |
| 1729 __ popl(EBX); |
| 1730 // Check if dynamic. |
| 1731 __ cmpl(EBX, raw_null); |
| 1732 __ j(EQUAL, &done, Assembler::kNearJump); |
| 1750 | 1733 |
| 1734 // For now handle only TypeArguments and bail out if InstantiatedTypeArgs. |
| 1735 __ movl(EDX, FieldAddress(EBX, Object::class_offset())); |
| 1736 __ CompareObject(EDX, Object::ZoneHandle(Object::type_arguments_class())); |
| 1737 __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump); |
| 1738 // EBX: Instance of TypeArguments. |
| 1739 __ movl(EDX, |
| 1740 FieldAddress(EBX, TypeArguments::type_at_offset(dst_type.Index()))); |
| 1741 // EDX: concrete type of dst_type. |
| 1742 __ CompareObject(EDX, Type::ZoneHandle(Type::DynamicType())); |
| 1743 __ j(EQUAL, &done, Assembler::kNearJump); |
| 1744 // Check if the type has type parameters, if not do the class comparison. |
| 1745 Label not_smi; |
| 1746 __ testl(EAX, Immediate(kSmiTagMask)); // Value is Smi? |
| 1747 __ j(NOT_ZERO, ¬_smi, Assembler::kNearJump); |
| 1748 __ CompareObject(EDX, Type::ZoneHandle(Type::IntInterface())); |
| 1749 __ j(EQUAL, &done, Assembler::kNearJump); |
| 1750 __ CompareObject(EDX, Type::ZoneHandle(Type::NumberInterface())); |
| 1751 __ j(EQUAL, &done, Assembler::kNearJump); |
| 1752 __ Bind(¬_smi); |
| 1753 // The instantiated type parameter may not be a Type, but could be an |
| 1754 // InstantiatedType. It is therefore necessary to check its class. |
| 1755 __ movl(ECX, FieldAddress(EDX, Object::class_offset())); |
| 1756 __ CompareObject(ECX, Object::ZoneHandle(Object::type_class())); |
| 1757 __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump); |
| 1758 __ movl(EDX, FieldAddress(EDX, Type::type_class_offset())); |
| 1759 __ movl(ECX, FieldAddress(EDX, Class::type_parameters_offset())); |
| 1760 // Check that class of dst_type has no type parameters. |
| 1761 __ cmpl(ECX, raw_null); |
| 1762 __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump); |
| 1763 // We have a non-parameterized class in EDX, compare with class of |
| 1764 // value in EAX. EAX, EDX are preserved in stub. |
| 1765 __ call(&StubCode::IsRawSubTypeLabel()); |
| 1766 // Result in EBX: 1 is raw subtype. |
| 1767 __ cmpl(EBX, Immediate(1)); |
| 1768 __ j(EQUAL, &done, Assembler::kNearJump); |
| 1769 __ Bind(&fall_through); |
| 1770 } |
| 1771 } |
| 1751 __ Bind(&runtime_call); | 1772 __ Bind(&runtime_call); |
| 1752 __ PushObject(Object::ZoneHandle()); // Make room for the result. | 1773 __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| 1753 const Immediate location = | 1774 const Immediate location = |
| 1754 Immediate(reinterpret_cast<int32_t>(Smi::New(token_index))); | 1775 Immediate(reinterpret_cast<int32_t>(Smi::New(token_index))); |
| 1755 const Immediate node_id_as_smi = | |
| 1756 Immediate(reinterpret_cast<int32_t>(Smi::New(node_id))); | |
| 1757 __ pushl(location); // Push the source location. | 1776 __ pushl(location); // Push the source location. |
| 1758 __ pushl(node_id_as_smi); // node-id. | |
| 1759 __ pushl(EAX); // Push the source object. | 1777 __ pushl(EAX); // Push the source object. |
| 1760 __ PushObject(dst_type); // Push the type of the destination. | 1778 __ PushObject(dst_type); // Push the type of the destination. |
| 1761 if (dst_type.IsInstantiated()) { | 1779 if (dst_type.IsInstantiated()) { |
| 1762 __ pushl(raw_null); // Null instantiator. | 1780 __ pushl(raw_null); // Null instantiator. |
| 1763 } else { | 1781 } else { |
| 1764 GenerateInstantiatorTypeArguments(token_index); | 1782 GenerateInstantiatorTypeArguments(token_index); |
| 1765 } | 1783 } |
| 1766 __ PushObject(dst_name); // Push the name of the destination. | 1784 __ PushObject(dst_name); // Push the name of the destination. |
| 1767 GenerateCallRuntime(node_id, token_index, kTypeCheckRuntimeEntry); | 1785 GenerateCallRuntime(node_id, token_index, kTypeCheckRuntimeEntry); |
| 1768 // Pop the parameters supplied to the runtime entry. The result of the | 1786 // Pop the parameters supplied to the runtime entry. The result of the |
| 1769 // type check runtime call is the checked value. | 1787 // type check runtime call is the checked value. |
| 1770 __ addl(ESP, Immediate(6 * kWordSize)); | 1788 __ addl(ESP, Immediate(5 * kWordSize)); |
| 1771 __ popl(EAX); | 1789 __ popl(EAX); |
| 1772 | 1790 |
| 1773 // EAX: value. | 1791 // EAX: value. |
| 1774 __ Bind(&done); | 1792 __ Bind(&done); |
| 1775 } | 1793 } |
| 1776 | 1794 |
| 1777 | 1795 |
| 1778 void CodeGenerator::GenerateArgumentTypeChecks() { | 1796 void CodeGenerator::GenerateArgumentTypeChecks() { |
| 1779 const Function& function = parsed_function_.function(); | 1797 const Function& function = parsed_function_.function(); |
| 1780 const SequenceNode& sequence_node = *parsed_function_.node_sequence(); | 1798 const SequenceNode& sequence_node = *parsed_function_.node_sequence(); |
| (...skipping 1093 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2874 const Error& error = Error::Handle( | 2892 const Error& error = Error::Handle( |
| 2875 Parser::FormatError(script, token_index, "Error", format, args)); | 2893 Parser::FormatError(script, token_index, "Error", format, args)); |
| 2876 va_end(args); | 2894 va_end(args); |
| 2877 Isolate::Current()->long_jump_base()->Jump(1, error); | 2895 Isolate::Current()->long_jump_base()->Jump(1, error); |
| 2878 UNREACHABLE(); | 2896 UNREACHABLE(); |
| 2879 } | 2897 } |
| 2880 | 2898 |
| 2881 } // namespace dart | 2899 } // namespace dart |
| 2882 | 2900 |
| 2883 #endif // defined TARGET_ARCH_IA32 | 2901 #endif // defined TARGET_ARCH_IA32 |
| OLD | NEW |