| 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/flow_graph_compiler.h" | 8 #include "vm/flow_graph_compiler.h" |
| 9 | 9 |
| 10 #include "lib/error.h" | 10 #include "lib/error.h" |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 163 } | 163 } |
| 164 | 164 |
| 165 | 165 |
| 166 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if | 166 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if |
| 167 // type test is conclusive, otherwise fallthrough if a type test could not | 167 // type test is conclusive, otherwise fallthrough if a type test could not |
| 168 // be completed. | 168 // be completed. |
| 169 // EAX: instance (must survive). | 169 // EAX: instance (must survive). |
| 170 // Clobbers ECX, EDI. | 170 // Clobbers ECX, EDI. |
| 171 RawSubtypeTestCache* | 171 RawSubtypeTestCache* |
| 172 FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest( | 172 FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest( |
| 173 intptr_t cid, | |
| 174 intptr_t token_pos, | 173 intptr_t token_pos, |
| 175 const AbstractType& type, | 174 const AbstractType& type, |
| 176 Label* is_instance_lbl, | 175 Label* is_instance_lbl, |
| 177 Label* is_not_instance_lbl) { | 176 Label* is_not_instance_lbl) { |
| 178 ASSERT(type.IsInstantiated()); | 177 ASSERT(type.IsInstantiated()); |
| 179 const Class& type_class = Class::ZoneHandle(type.type_class()); | 178 const Class& type_class = Class::ZoneHandle(type.type_class()); |
| 180 ASSERT(type_class.HasTypeArguments()); | 179 ASSERT(type_class.HasTypeArguments()); |
| 181 const Register kInstanceReg = EAX; | 180 const Register kInstanceReg = EAX; |
| 182 // A Smi object cannot be the instance of a parameterized class. | 181 // A Smi object cannot be the instance of a parameterized class. |
| 183 __ testl(kInstanceReg, Immediate(kSmiTagMask)); | 182 __ testl(kInstanceReg, Immediate(kSmiTagMask)); |
| 184 __ j(ZERO, is_not_instance_lbl); | 183 __ j(ZERO, is_not_instance_lbl); |
| 185 const AbstractTypeArguments& type_arguments = | 184 const AbstractTypeArguments& type_arguments = |
| 186 AbstractTypeArguments::ZoneHandle(type.arguments()); | 185 AbstractTypeArguments::ZoneHandle(type.arguments()); |
| 187 const bool is_raw_type = type_arguments.IsNull() || | 186 const bool is_raw_type = type_arguments.IsNull() || |
| 188 type_arguments.IsRaw(type_arguments.Length()); | 187 type_arguments.IsRaw(type_arguments.Length()); |
| 189 if (is_raw_type) { | 188 if (is_raw_type) { |
| 190 const Register kClassIdReg = ECX; | 189 const Register kClassIdReg = ECX; |
| 191 // Dynamic type argument, check only classes. | 190 // Dynamic type argument, check only classes. |
| 192 // List is a very common case. | 191 // List is a very common case. |
| 193 __ LoadClassId(kClassIdReg, kInstanceReg); | 192 __ LoadClassId(kClassIdReg, kInstanceReg); |
| 194 if (!type_class.is_interface()) { | 193 if (!type_class.is_interface()) { |
| 195 __ cmpl(kClassIdReg, Immediate(type_class.id())); | 194 __ cmpl(kClassIdReg, Immediate(type_class.id())); |
| 196 __ j(EQUAL, is_instance_lbl); | 195 __ j(EQUAL, is_instance_lbl); |
| 197 } | 196 } |
| 198 if (type.IsListInterface()) { | 197 if (type.IsListInterface()) { |
| 199 GenerateListTypeCheck(kClassIdReg, is_instance_lbl); | 198 GenerateListTypeCheck(kClassIdReg, is_instance_lbl); |
| 200 } | 199 } |
| 201 return GenerateSubtype1TestCacheLookup( | 200 return GenerateSubtype1TestCacheLookup( |
| 202 cid, token_pos, type_class, is_instance_lbl, is_not_instance_lbl); | 201 token_pos, type_class, is_instance_lbl, is_not_instance_lbl); |
| 203 } | 202 } |
| 204 // If one type argument only, check if type argument is Object or Dynamic. | 203 // If one type argument only, check if type argument is Object or Dynamic. |
| 205 if (type_arguments.Length() == 1) { | 204 if (type_arguments.Length() == 1) { |
| 206 const AbstractType& tp_argument = AbstractType::ZoneHandle( | 205 const AbstractType& tp_argument = AbstractType::ZoneHandle( |
| 207 type_arguments.TypeAt(0)); | 206 type_arguments.TypeAt(0)); |
| 208 ASSERT(!tp_argument.IsMalformed()); | 207 ASSERT(!tp_argument.IsMalformed()); |
| 209 if (tp_argument.IsType()) { | 208 if (tp_argument.IsType()) { |
| 210 ASSERT(tp_argument.HasResolvedTypeClass()); | 209 ASSERT(tp_argument.HasResolvedTypeClass()); |
| 211 // Check if type argument is dynamic or Object. | 210 // Check if type argument is dynamic or Object. |
| 212 const Type& object_type = Type::Handle(Type::ObjectType()); | 211 const Type& object_type = Type::Handle(Type::ObjectType()); |
| 213 if (object_type.IsSubtypeOf(tp_argument, NULL)) { | 212 if (object_type.IsSubtypeOf(tp_argument, NULL)) { |
| 214 // Instance class test only necessary. | 213 // Instance class test only necessary. |
| 215 return GenerateSubtype1TestCacheLookup( | 214 return GenerateSubtype1TestCacheLookup( |
| 216 cid, token_pos, type_class, is_instance_lbl, is_not_instance_lbl); | 215 token_pos, type_class, is_instance_lbl, is_not_instance_lbl); |
| 217 } | 216 } |
| 218 } | 217 } |
| 219 } | 218 } |
| 220 // Regular subtype test cache involving instance's type arguments. | 219 // Regular subtype test cache involving instance's type arguments. |
| 221 const Register kTypeArgumentsReg = kNoRegister; | 220 const Register kTypeArgumentsReg = kNoRegister; |
| 222 const Register kTempReg = EDI; | 221 const Register kTempReg = EDI; |
| 223 return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, | 222 return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, |
| 224 kInstanceReg, | 223 kInstanceReg, |
| 225 kTypeArgumentsReg, | 224 kTypeArgumentsReg, |
| 226 kTempReg, | 225 kTempReg, |
| (...skipping 13 matching lines...) Expand all Loading... |
| 240 __ jmp(is_not_equal_lbl); | 239 __ jmp(is_not_equal_lbl); |
| 241 } | 240 } |
| 242 | 241 |
| 243 | 242 |
| 244 // Testing against an instantiated type with no arguments, without | 243 // Testing against an instantiated type with no arguments, without |
| 245 // SubtypeTestCache. | 244 // SubtypeTestCache. |
| 246 // EAX: instance to test against (preserved). | 245 // EAX: instance to test against (preserved). |
| 247 // Clobbers ECX, EDI. | 246 // Clobbers ECX, EDI. |
| 248 // Returns true if there is a fallthrough. | 247 // Returns true if there is a fallthrough. |
| 249 bool FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest( | 248 bool FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest( |
| 250 intptr_t cid, | |
| 251 intptr_t token_pos, | 249 intptr_t token_pos, |
| 252 const AbstractType& type, | 250 const AbstractType& type, |
| 253 Label* is_instance_lbl, | 251 Label* is_instance_lbl, |
| 254 Label* is_not_instance_lbl) { | 252 Label* is_not_instance_lbl) { |
| 255 ASSERT(type.IsInstantiated()); | 253 ASSERT(type.IsInstantiated()); |
| 256 const Class& type_class = Class::Handle(type.type_class()); | 254 const Class& type_class = Class::Handle(type.type_class()); |
| 257 ASSERT(!type_class.HasTypeArguments()); | 255 ASSERT(!type_class.HasTypeArguments()); |
| 258 | 256 |
| 259 const Register kInstanceReg = EAX; | 257 const Register kInstanceReg = EAX; |
| 260 Label compare_classes; | 258 Label compare_classes; |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 315 | 313 |
| 316 | 314 |
| 317 // Uses SubtypeTestCache to store instance class and result. | 315 // Uses SubtypeTestCache to store instance class and result. |
| 318 // EAX: instance to test. | 316 // EAX: instance to test. |
| 319 // Clobbers EDI, ECX. | 317 // Clobbers EDI, ECX. |
| 320 // Immediate class test already done. | 318 // Immediate class test already done. |
| 321 // TODO(srdjan): Implement a quicker subtype check, as type test | 319 // TODO(srdjan): Implement a quicker subtype check, as type test |
| 322 // arrays can grow too high, but they may be useful when optimizing | 320 // arrays can grow too high, but they may be useful when optimizing |
| 323 // code (type-feedback). | 321 // code (type-feedback). |
| 324 RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup( | 322 RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup( |
| 325 intptr_t cid, | |
| 326 intptr_t token_pos, | 323 intptr_t token_pos, |
| 327 const Class& type_class, | 324 const Class& type_class, |
| 328 Label* is_instance_lbl, | 325 Label* is_instance_lbl, |
| 329 Label* is_not_instance_lbl) { | 326 Label* is_not_instance_lbl) { |
| 330 const Register kInstanceReg = EAX; | 327 const Register kInstanceReg = EAX; |
| 331 __ LoadClass(ECX, kInstanceReg, EDI); | 328 __ LoadClass(ECX, kInstanceReg, EDI); |
| 332 // ECX: instance class. | 329 // ECX: instance class. |
| 333 // Check immediate superclass equality. | 330 // Check immediate superclass equality. |
| 334 __ movl(EDI, FieldAddress(ECX, Class::super_type_offset())); | 331 __ movl(EDI, FieldAddress(ECX, Class::super_type_offset())); |
| 335 __ movl(EDI, FieldAddress(EDI, Type::type_class_offset())); | 332 __ movl(EDI, FieldAddress(EDI, Type::type_class_offset())); |
| 336 __ CompareObject(EDI, type_class); | 333 __ CompareObject(EDI, type_class); |
| 337 __ j(EQUAL, is_instance_lbl); | 334 __ j(EQUAL, is_instance_lbl); |
| 338 | 335 |
| 339 const Register kTypeArgumentsReg = kNoRegister; | 336 const Register kTypeArgumentsReg = kNoRegister; |
| 340 const Register kTempReg = EDI; | 337 const Register kTempReg = EDI; |
| 341 return GenerateCallSubtypeTestStub(kTestTypeOneArg, | 338 return GenerateCallSubtypeTestStub(kTestTypeOneArg, |
| 342 kInstanceReg, | 339 kInstanceReg, |
| 343 kTypeArgumentsReg, | 340 kTypeArgumentsReg, |
| 344 kTempReg, | 341 kTempReg, |
| 345 is_instance_lbl, | 342 is_instance_lbl, |
| 346 is_not_instance_lbl); | 343 is_not_instance_lbl); |
| 347 } | 344 } |
| 348 | 345 |
| 349 | 346 |
| 350 // Generates inlined check if 'type' is a type parameter or type itsef | 347 // Generates inlined check if 'type' is a type parameter or type itsef |
| 351 // EAX: instance (preserved). | 348 // EAX: instance (preserved). |
| 352 // Clobbers EDX, EDI, ECX. | 349 // Clobbers EDX, EDI, ECX. |
| 353 RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( | 350 RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( |
| 354 intptr_t cid, | |
| 355 intptr_t token_pos, | 351 intptr_t token_pos, |
| 356 const AbstractType& type, | 352 const AbstractType& type, |
| 357 Label* is_instance_lbl, | 353 Label* is_instance_lbl, |
| 358 Label* is_not_instance_lbl) { | 354 Label* is_not_instance_lbl) { |
| 359 ASSERT(!type.IsInstantiated()); | 355 ASSERT(!type.IsInstantiated()); |
| 360 // Skip check if destination is a dynamic type. | 356 // Skip check if destination is a dynamic type. |
| 361 const Immediate raw_null = | 357 const Immediate raw_null = |
| 362 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 358 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 363 if (type.IsTypeParameter()) { | 359 if (type.IsTypeParameter()) { |
| 364 const TypeParameter& type_param = TypeParameter::Cast(type); | 360 const TypeParameter& type_param = TypeParameter::Cast(type); |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 436 // Inputs: | 432 // Inputs: |
| 437 // - EAX: instance to test against (preserved). | 433 // - EAX: instance to test against (preserved). |
| 438 // - EDX: optional instantiator type arguments (preserved). | 434 // - EDX: optional instantiator type arguments (preserved). |
| 439 // Clobbers ECX, EDI. | 435 // Clobbers ECX, EDI. |
| 440 // Returns: | 436 // Returns: |
| 441 // - preserved instance in EAX and optional instantiator type arguments in EDX. | 437 // - preserved instance in EAX and optional instantiator type arguments in EDX. |
| 442 // Note that this inlined code must be followed by the runtime_call code, as it | 438 // Note that this inlined code must be followed by the runtime_call code, as it |
| 443 // may fall through to it. Otherwise, this inline code will jump to the label | 439 // may fall through to it. Otherwise, this inline code will jump to the label |
| 444 // is_instance or to the label is_not_instance. | 440 // is_instance or to the label is_not_instance. |
| 445 RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( | 441 RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( |
| 446 intptr_t cid, | |
| 447 intptr_t token_pos, | 442 intptr_t token_pos, |
| 448 const AbstractType& type, | 443 const AbstractType& type, |
| 449 Label* is_instance_lbl, | 444 Label* is_instance_lbl, |
| 450 Label* is_not_instance_lbl) { | 445 Label* is_not_instance_lbl) { |
| 451 if (type.IsVoidType()) { | 446 if (type.IsVoidType()) { |
| 452 // A non-null value is returned from a void function, which will result in a | 447 // A non-null value is returned from a void function, which will result in a |
| 453 // type error. A null value is handled prior to executing this inline code. | 448 // type error. A null value is handled prior to executing this inline code. |
| 454 return SubtypeTestCache::null(); | 449 return SubtypeTestCache::null(); |
| 455 } | 450 } |
| 456 if (type.IsInstantiated()) { | 451 if (type.IsInstantiated()) { |
| 457 const Class& type_class = Class::ZoneHandle(type.type_class()); | 452 const Class& type_class = Class::ZoneHandle(type.type_class()); |
| 458 // A Smi object cannot be the instance of a parameterized class. | 453 // A Smi object cannot be the instance of a parameterized class. |
| 459 // A class equality check is only applicable with a dst type of a | 454 // A class equality check is only applicable with a dst type of a |
| 460 // non-parameterized class or with a raw dst type of a parameterized class. | 455 // non-parameterized class or with a raw dst type of a parameterized class. |
| 461 if (type_class.HasTypeArguments()) { | 456 if (type_class.HasTypeArguments()) { |
| 462 return GenerateInstantiatedTypeWithArgumentsTest(cid, | 457 return GenerateInstantiatedTypeWithArgumentsTest(token_pos, |
| 463 token_pos, | |
| 464 type, | 458 type, |
| 465 is_instance_lbl, | 459 is_instance_lbl, |
| 466 is_not_instance_lbl); | 460 is_not_instance_lbl); |
| 467 // Fall through to runtime call. | 461 // Fall through to runtime call. |
| 468 } | 462 } |
| 469 const bool has_fall_through = | 463 const bool has_fall_through = |
| 470 GenerateInstantiatedTypeNoArgumentsTest(cid, | 464 GenerateInstantiatedTypeNoArgumentsTest(token_pos, |
| 471 token_pos, | |
| 472 type, | 465 type, |
| 473 is_instance_lbl, | 466 is_instance_lbl, |
| 474 is_not_instance_lbl); | 467 is_not_instance_lbl); |
| 475 if (has_fall_through) { | 468 if (has_fall_through) { |
| 476 // If test non-conclusive so far, try the inlined type-test cache. | 469 // If test non-conclusive so far, try the inlined type-test cache. |
| 477 // 'type' is known at compile time. | 470 // 'type' is known at compile time. |
| 478 return GenerateSubtype1TestCacheLookup( | 471 return GenerateSubtype1TestCacheLookup( |
| 479 cid, token_pos, type_class, | 472 token_pos, type_class, is_instance_lbl, is_not_instance_lbl); |
| 480 is_instance_lbl, is_not_instance_lbl); | |
| 481 } else { | 473 } else { |
| 482 return SubtypeTestCache::null(); | 474 return SubtypeTestCache::null(); |
| 483 } | 475 } |
| 484 } | 476 } |
| 485 return GenerateUninstantiatedTypeTest(cid, | 477 return GenerateUninstantiatedTypeTest(token_pos, |
| 486 token_pos, | |
| 487 type, | 478 type, |
| 488 is_instance_lbl, | 479 is_instance_lbl, |
| 489 is_not_instance_lbl); | 480 is_not_instance_lbl); |
| 490 } | 481 } |
| 491 | 482 |
| 492 | 483 |
| 493 // If instanceof type test cannot be performed successfully at compile time and | 484 // If instanceof type test cannot be performed successfully at compile time and |
| 494 // therefore eliminated, optimize it by adding inlined tests for: | 485 // therefore eliminated, optimize it by adding inlined tests for: |
| 495 // - NULL -> return false. | 486 // - NULL -> return false. |
| 496 // - Smi -> compile time subtype check (only if dst class is not parameterized). | 487 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
| 497 // - Class equality (only if class is not parameterized). | 488 // - Class equality (only if class is not parameterized). |
| 498 // Inputs: | 489 // Inputs: |
| 499 // - EAX: object. | 490 // - EAX: object. |
| 500 // - EDX: instantiator type arguments or raw_null. | 491 // - EDX: instantiator type arguments or raw_null. |
| 501 // - ECX: instantiator or raw_null. | 492 // - ECX: instantiator or raw_null. |
| 502 // Clobbers ECX and EDX. | 493 // Clobbers ECX and EDX. |
| 503 // Returns: | 494 // Returns: |
| 504 // - true or false in EAX. | 495 // - true or false in EAX. |
| 505 void FlowGraphCompiler::GenerateInstanceOf(intptr_t cid, | 496 void FlowGraphCompiler::GenerateInstanceOf(intptr_t deopt_id, |
| 506 intptr_t token_pos, | 497 intptr_t token_pos, |
| 507 intptr_t try_index, | 498 intptr_t try_index, |
| 508 const AbstractType& type, | 499 const AbstractType& type, |
| 509 bool negate_result) { | 500 bool negate_result) { |
| 510 ASSERT(type.IsFinalized() && !type.IsMalformed()); | 501 ASSERT(type.IsFinalized() && !type.IsMalformed()); |
| 511 | 502 |
| 512 const Immediate raw_null = | 503 const Immediate raw_null = |
| 513 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 504 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 514 Label is_instance, is_not_instance; | 505 Label is_instance, is_not_instance; |
| 515 __ pushl(ECX); // Store instantiator on stack. | 506 __ pushl(ECX); // Store instantiator on stack. |
| 516 __ pushl(EDX); // Store instantiator type arguments. | 507 __ pushl(EDX); // Store instantiator type arguments. |
| 517 // If type is instantiated and non-parameterized, we can inline code | 508 // If type is instantiated and non-parameterized, we can inline code |
| 518 // checking whether the tested instance is a Smi. | 509 // checking whether the tested instance is a Smi. |
| 519 if (type.IsInstantiated()) { | 510 if (type.IsInstantiated()) { |
| 520 // A null object is only an instance of Object and Dynamic, which has | 511 // A null object is only an instance of Object and Dynamic, which has |
| 521 // already been checked above (if the type is instantiated). So we can | 512 // already been checked above (if the type is instantiated). So we can |
| 522 // return false here if the instance is null (and if the type is | 513 // return false here if the instance is null (and if the type is |
| 523 // instantiated). | 514 // instantiated). |
| 524 // We can only inline this null check if the type is instantiated at compile | 515 // We can only inline this null check if the type is instantiated at compile |
| 525 // time, since an uninstantiated type at compile time could be Object or | 516 // time, since an uninstantiated type at compile time could be Object or |
| 526 // Dynamic at run time. | 517 // Dynamic at run time. |
| 527 __ cmpl(EAX, raw_null); | 518 __ cmpl(EAX, raw_null); |
| 528 __ j(EQUAL, &is_not_instance); | 519 __ j(EQUAL, &is_not_instance); |
| 529 } | 520 } |
| 530 | 521 |
| 531 // Generate inline instanceof test. | 522 // Generate inline instanceof test. |
| 532 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); | 523 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); |
| 533 test_cache = GenerateInlineInstanceof(cid, token_pos, type, | 524 test_cache = GenerateInlineInstanceof(token_pos, type, |
| 534 &is_instance, &is_not_instance); | 525 &is_instance, &is_not_instance); |
| 535 | 526 |
| 536 // test_cache is null if there is no fall-through. | 527 // test_cache is null if there is no fall-through. |
| 537 Label done; | 528 Label done; |
| 538 if (!test_cache.IsNull()) { | 529 if (!test_cache.IsNull()) { |
| 539 // Generate runtime call. | 530 // Generate runtime call. |
| 540 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. | 531 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. |
| 541 __ movl(ECX, Address(ESP, kWordSize)); // Get instantiator. | 532 __ movl(ECX, Address(ESP, kWordSize)); // Get instantiator. |
| 542 __ PushObject(Object::ZoneHandle()); // Make room for the result. | 533 __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| 543 __ pushl(Immediate(Smi::RawValue(cid))); // Computation id. | |
| 544 __ pushl(EAX); // Push the instance. | 534 __ pushl(EAX); // Push the instance. |
| 545 __ PushObject(type); // Push the type. | 535 __ PushObject(type); // Push the type. |
| 546 __ pushl(ECX); // Instantiator. | 536 __ pushl(ECX); // Instantiator. |
| 547 __ pushl(EDX); // Instantiator type arguments. | 537 __ pushl(EDX); // Instantiator type arguments. |
| 548 __ LoadObject(EAX, test_cache); | 538 __ LoadObject(EAX, test_cache); |
| 549 __ pushl(EAX); | 539 __ pushl(EAX); |
| 550 GenerateCallRuntime(cid, token_pos, try_index, kInstanceofRuntimeEntry); | 540 GenerateCallRuntime(deopt_id, token_pos, try_index, |
| 541 kInstanceofRuntimeEntry); |
| 551 // Pop the parameters supplied to the runtime entry. The result of the | 542 // Pop the parameters supplied to the runtime entry. The result of the |
| 552 // instanceof runtime call will be left as the result of the operation. | 543 // instanceof runtime call will be left as the result of the operation. |
| 553 __ Drop(6); | 544 __ Drop(5); |
| 554 if (negate_result) { | 545 if (negate_result) { |
| 555 __ popl(EDX); | 546 __ popl(EDX); |
| 556 __ LoadObject(EAX, bool_true()); | 547 __ LoadObject(EAX, bool_true()); |
| 557 __ cmpl(EDX, EAX); | 548 __ cmpl(EDX, EAX); |
| 558 __ j(NOT_EQUAL, &done, Assembler::kNearJump); | 549 __ j(NOT_EQUAL, &done, Assembler::kNearJump); |
| 559 __ LoadObject(EAX, bool_false()); | 550 __ LoadObject(EAX, bool_false()); |
| 560 } else { | 551 } else { |
| 561 __ popl(EAX); | 552 __ popl(EAX); |
| 562 } | 553 } |
| 563 __ jmp(&done, Assembler::kNearJump); | 554 __ jmp(&done, Assembler::kNearJump); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 579 // - Smi -> compile time subtype check (only if dst class is not parameterized). | 570 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
| 580 // - Class equality (only if class is not parameterized). | 571 // - Class equality (only if class is not parameterized). |
| 581 // Inputs: | 572 // Inputs: |
| 582 // - EAX: object. | 573 // - EAX: object. |
| 583 // - EDX: instantiator type arguments or raw_null. | 574 // - EDX: instantiator type arguments or raw_null. |
| 584 // - ECX: instantiator or raw_null. | 575 // - ECX: instantiator or raw_null. |
| 585 // Returns: | 576 // Returns: |
| 586 // - object in EAX for successful assignable check (or throws TypeError). | 577 // - object in EAX for successful assignable check (or throws TypeError). |
| 587 // Performance notes: positive checks must be quick, negative checks can be slow | 578 // Performance notes: positive checks must be quick, negative checks can be slow |
| 588 // as they throw an exception. | 579 // as they throw an exception. |
| 589 void FlowGraphCompiler::GenerateAssertAssignable(intptr_t cid, | 580 void FlowGraphCompiler::GenerateAssertAssignable(intptr_t deopt_id, |
| 590 intptr_t token_pos, | 581 intptr_t token_pos, |
| 591 intptr_t try_index, | 582 intptr_t try_index, |
| 592 const AbstractType& dst_type, | 583 const AbstractType& dst_type, |
| 593 const String& dst_name) { | 584 const String& dst_name) { |
| 594 ASSERT(token_pos >= 0); | 585 ASSERT(token_pos >= 0); |
| 595 ASSERT(!dst_type.IsNull()); | 586 ASSERT(!dst_type.IsNull()); |
| 596 ASSERT(dst_type.IsFinalized()); | 587 ASSERT(dst_type.IsFinalized()); |
| 597 // Assignable check is skipped in FlowGraphBuilder, not here. | 588 // Assignable check is skipped in FlowGraphBuilder, not here. |
| 598 ASSERT(dst_type.IsMalformed() || | 589 ASSERT(dst_type.IsMalformed() || |
| 599 (!dst_type.IsDynamicType() && !dst_type.IsObjectType())); | 590 (!dst_type.IsDynamicType() && !dst_type.IsObjectType())); |
| 600 __ pushl(ECX); // Store instantiator. | 591 __ pushl(ECX); // Store instantiator. |
| 601 __ pushl(EDX); // Store instantiator type arguments. | 592 __ pushl(EDX); // Store instantiator type arguments. |
| 602 // A null object is always assignable and is returned as result. | 593 // A null object is always assignable and is returned as result. |
| 603 const Immediate raw_null = | 594 const Immediate raw_null = |
| 604 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 595 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 605 Label is_assignable, runtime_call; | 596 Label is_assignable, runtime_call; |
| 606 __ cmpl(EAX, raw_null); | 597 __ cmpl(EAX, raw_null); |
| 607 __ j(EQUAL, &is_assignable); | 598 __ j(EQUAL, &is_assignable); |
| 608 | 599 |
| 609 // Generate throw new TypeError() if the type is malformed. | 600 // Generate throw new TypeError() if the type is malformed. |
| 610 if (dst_type.IsMalformed()) { | 601 if (dst_type.IsMalformed()) { |
| 611 const Error& error = Error::Handle(dst_type.malformed_error()); | 602 const Error& error = Error::Handle(dst_type.malformed_error()); |
| 612 const String& error_message = String::ZoneHandle( | 603 const String& error_message = String::ZoneHandle( |
| 613 Symbols::New(error.ToErrorCString())); | 604 Symbols::New(error.ToErrorCString())); |
| 614 __ PushObject(Object::ZoneHandle()); // Make room for the result. | 605 __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| 615 __ pushl(EAX); // Push the source object. | 606 __ pushl(EAX); // Push the source object. |
| 616 __ PushObject(dst_name); // Push the name of the destination. | 607 __ PushObject(dst_name); // Push the name of the destination. |
| 617 __ PushObject(error_message); | 608 __ PushObject(error_message); |
| 618 GenerateCallRuntime(cid, | 609 GenerateCallRuntime(deopt_id, |
| 619 token_pos, | 610 token_pos, |
| 620 try_index, | 611 try_index, |
| 621 kMalformedTypeErrorRuntimeEntry); | 612 kMalformedTypeErrorRuntimeEntry); |
| 622 // We should never return here. | 613 // We should never return here. |
| 623 __ int3(); | 614 __ int3(); |
| 624 | 615 |
| 625 __ Bind(&is_assignable); // For a null object. | 616 __ Bind(&is_assignable); // For a null object. |
| 626 return; | 617 return; |
| 627 } | 618 } |
| 628 | 619 |
| 629 // Generate inline type check, linking to runtime call if not assignable. | 620 // Generate inline type check, linking to runtime call if not assignable. |
| 630 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); | 621 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); |
| 631 test_cache = GenerateInlineInstanceof(cid, token_pos, dst_type, | 622 test_cache = GenerateInlineInstanceof(token_pos, dst_type, |
| 632 &is_assignable, &runtime_call); | 623 &is_assignable, &runtime_call); |
| 633 | 624 |
| 634 __ Bind(&runtime_call); | 625 __ Bind(&runtime_call); |
| 635 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. | 626 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. |
| 636 __ movl(ECX, Address(ESP, kWordSize)); // Get instantiator. | 627 __ movl(ECX, Address(ESP, kWordSize)); // Get instantiator. |
| 637 __ PushObject(Object::ZoneHandle()); // Make room for the result. | 628 __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| 638 __ pushl(Immediate(Smi::RawValue(cid))); // Computation id. | |
| 639 __ pushl(EAX); // Push the source object. | 629 __ pushl(EAX); // Push the source object. |
| 640 __ PushObject(dst_type); // Push the type of the destination. | 630 __ PushObject(dst_type); // Push the type of the destination. |
| 641 __ pushl(ECX); // Instantiator. | 631 __ pushl(ECX); // Instantiator. |
| 642 __ pushl(EDX); // Instantiator type arguments. | 632 __ pushl(EDX); // Instantiator type arguments. |
| 643 __ PushObject(dst_name); // Push the name of the destination. | 633 __ PushObject(dst_name); // Push the name of the destination. |
| 644 __ LoadObject(EAX, test_cache); | 634 __ LoadObject(EAX, test_cache); |
| 645 __ pushl(EAX); | 635 __ pushl(EAX); |
| 646 GenerateCallRuntime(cid, | 636 GenerateCallRuntime(deopt_id, |
| 647 token_pos, | 637 token_pos, |
| 648 try_index, | 638 try_index, |
| 649 kTypeCheckRuntimeEntry); | 639 kTypeCheckRuntimeEntry); |
| 650 // Pop the parameters supplied to the runtime entry. The result of the | 640 // Pop the parameters supplied to the runtime entry. The result of the |
| 651 // type check runtime call is the checked value. | 641 // type check runtime call is the checked value. |
| 652 __ Drop(7); | 642 __ Drop(6); |
| 653 __ popl(EAX); | 643 __ popl(EAX); |
| 654 | 644 |
| 655 __ Bind(&is_assignable); | 645 __ Bind(&is_assignable); |
| 656 __ popl(EDX); // Remove pushed instantiator type arguments.. | 646 __ popl(EDX); // Remove pushed instantiator type arguments.. |
| 657 __ popl(ECX); // Remove pushed instantiator. | 647 __ popl(ECX); // Remove pushed instantiator. |
| 658 } | 648 } |
| 659 | 649 |
| 660 | 650 |
| 661 void FlowGraphCompiler::EmitInstructionPrologue(Instruction* instr) { | 651 void FlowGraphCompiler::EmitInstructionPrologue(Instruction* instr) { |
| 662 LocationSummary* locs = instr->locs(); | 652 LocationSummary* locs = instr->locs(); |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 815 } | 805 } |
| 816 | 806 |
| 817 __ Bind(&wrong_num_arguments); | 807 __ Bind(&wrong_num_arguments); |
| 818 if (StackSize() != 0) { | 808 if (StackSize() != 0) { |
| 819 // We need to unwind the space we reserved for locals and copied parameters. | 809 // We need to unwind the space we reserved for locals and copied parameters. |
| 820 // The NoSuchMethodFunction stub does not expect to see that area on the | 810 // The NoSuchMethodFunction stub does not expect to see that area on the |
| 821 // stack. | 811 // stack. |
| 822 __ addl(ESP, Immediate(StackSize() * kWordSize)); | 812 __ addl(ESP, Immediate(StackSize() * kWordSize)); |
| 823 } | 813 } |
| 824 if (function.IsClosureFunction()) { | 814 if (function.IsClosureFunction()) { |
| 825 GenerateCallRuntime(AstNode::kNoId, | 815 GenerateCallRuntime(Isolate::kNoDeoptId, |
| 826 0, | 816 0, |
| 827 CatchClauseNode::kInvalidTryIndex, | 817 CatchClauseNode::kInvalidTryIndex, |
| 828 kClosureArgumentMismatchRuntimeEntry); | 818 kClosureArgumentMismatchRuntimeEntry); |
| 829 } else { | 819 } else { |
| 830 ASSERT(!IsLeaf()); | 820 ASSERT(!IsLeaf()); |
| 831 // Invoke noSuchMethod function. | 821 // Invoke noSuchMethod function. |
| 832 const int kNumArgsChecked = 1; | 822 const int kNumArgsChecked = 1; |
| 833 ICData& ic_data = ICData::ZoneHandle(); | 823 ICData& ic_data = ICData::ZoneHandle(); |
| 834 ic_data = ICData::New(function, | 824 ic_data = ICData::New(function, |
| 835 String::Handle(function.name()), | 825 String::Handle(function.name()), |
| 836 AstNode::kNoId, | 826 Isolate::kNoDeoptId, |
| 837 kNumArgsChecked); | 827 kNumArgsChecked); |
| 838 __ LoadObject(ECX, ic_data); | 828 __ LoadObject(ECX, ic_data); |
| 839 // EBP - 4 : PC marker, allows easy identification of RawInstruction obj. | 829 // EBP - 4 : PC marker, allows easy identification of RawInstruction obj. |
| 840 // EBP : points to previous frame pointer. | 830 // EBP : points to previous frame pointer. |
| 841 // EBP + 4 : points to return address. | 831 // EBP + 4 : points to return address. |
| 842 // EBP + 8 : address of last argument (arg n-1). | 832 // EBP + 8 : address of last argument (arg n-1). |
| 843 // ESP + 8 + 4*(n-1) : address of first argument (arg 0). | 833 // ESP + 8 + 4*(n-1) : address of first argument (arg 0). |
| 844 // ECX : ic-data. | 834 // ECX : ic-data. |
| 845 // EDX : arguments descriptor array. | 835 // EDX : arguments descriptor array. |
| 846 __ call(&StubCode::CallNoSuchMethodFunctionLabel()); | 836 __ call(&StubCode::CallNoSuchMethodFunctionLabel()); |
| 847 } | 837 } |
| 848 | 838 |
| 849 if (FLAG_trace_functions) { | 839 if (FLAG_trace_functions) { |
| 850 __ pushl(EAX); // Preserve result. | 840 __ pushl(EAX); // Preserve result. |
| 851 __ PushObject(Function::ZoneHandle(function.raw())); | 841 __ PushObject(Function::ZoneHandle(function.raw())); |
| 852 GenerateCallRuntime(AstNode::kNoId, | 842 GenerateCallRuntime(Isolate::kNoDeoptId, |
| 853 0, | 843 0, |
| 854 CatchClauseNode::kInvalidTryIndex, | 844 CatchClauseNode::kInvalidTryIndex, |
| 855 kTraceFunctionExitRuntimeEntry); | 845 kTraceFunctionExitRuntimeEntry); |
| 856 __ popl(EAX); // Remove argument. | 846 __ popl(EAX); // Remove argument. |
| 857 __ popl(EAX); // Restore result. | 847 __ popl(EAX); // Restore result. |
| 858 } | 848 } |
| 859 __ LeaveFrame(); | 849 __ LeaveFrame(); |
| 860 __ ret(); | 850 __ ret(); |
| 861 | 851 |
| 862 __ Bind(&all_arguments_processed); | 852 __ Bind(&all_arguments_processed); |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 963 const bool check_arguments = function.IsClosureFunction(); | 953 const bool check_arguments = function.IsClosureFunction(); |
| 964 #endif | 954 #endif |
| 965 if (check_arguments) { | 955 if (check_arguments) { |
| 966 // Check that num_fixed <= argc <= num_params. | 956 // Check that num_fixed <= argc <= num_params. |
| 967 Label argc_in_range; | 957 Label argc_in_range; |
| 968 // Total number of args is the first Smi in args descriptor array (EDX). | 958 // Total number of args is the first Smi in args descriptor array (EDX). |
| 969 __ movl(EAX, FieldAddress(EDX, Array::data_offset())); | 959 __ movl(EAX, FieldAddress(EDX, Array::data_offset())); |
| 970 __ cmpl(EAX, Immediate(Smi::RawValue(parameter_count))); | 960 __ cmpl(EAX, Immediate(Smi::RawValue(parameter_count))); |
| 971 __ j(EQUAL, &argc_in_range, Assembler::kNearJump); | 961 __ j(EQUAL, &argc_in_range, Assembler::kNearJump); |
| 972 if (function.IsClosureFunction()) { | 962 if (function.IsClosureFunction()) { |
| 973 GenerateCallRuntime(AstNode::kNoId, | 963 GenerateCallRuntime(Isolate::kNoDeoptId, |
| 974 function.token_pos(), | 964 function.token_pos(), |
| 975 CatchClauseNode::kInvalidTryIndex, | 965 CatchClauseNode::kInvalidTryIndex, |
| 976 kClosureArgumentMismatchRuntimeEntry); | 966 kClosureArgumentMismatchRuntimeEntry); |
| 977 } else { | 967 } else { |
| 978 __ Stop("Wrong number of arguments"); | 968 __ Stop("Wrong number of arguments"); |
| 979 } | 969 } |
| 980 __ Bind(&argc_in_range); | 970 __ Bind(&argc_in_range); |
| 981 } | 971 } |
| 982 } else { | 972 } else { |
| 983 CopyParameters(); | 973 CopyParameters(); |
| (...skipping 17 matching lines...) Expand all Loading... |
| 1001 __ movl(Address(EBP, (slot_base - i) * kWordSize), EAX); | 991 __ movl(Address(EBP, (slot_base - i) * kWordSize), EAX); |
| 1002 } | 992 } |
| 1003 } | 993 } |
| 1004 | 994 |
| 1005 if (!IsLeaf()) { | 995 if (!IsLeaf()) { |
| 1006 // Generate stack overflow check. | 996 // Generate stack overflow check. |
| 1007 __ cmpl(ESP, | 997 __ cmpl(ESP, |
| 1008 Address::Absolute(Isolate::Current()->stack_limit_address())); | 998 Address::Absolute(Isolate::Current()->stack_limit_address())); |
| 1009 Label no_stack_overflow; | 999 Label no_stack_overflow; |
| 1010 __ j(ABOVE, &no_stack_overflow, Assembler::kNearJump); | 1000 __ j(ABOVE, &no_stack_overflow, Assembler::kNearJump); |
| 1011 GenerateCallRuntime(AstNode::kNoId, | 1001 GenerateCallRuntime(Isolate::kNoDeoptId, |
| 1012 function.token_pos(), | 1002 function.token_pos(), |
| 1013 CatchClauseNode::kInvalidTryIndex, | 1003 CatchClauseNode::kInvalidTryIndex, |
| 1014 kStackOverflowRuntimeEntry); | 1004 kStackOverflowRuntimeEntry); |
| 1015 __ Bind(&no_stack_overflow); | 1005 __ Bind(&no_stack_overflow); |
| 1016 } | 1006 } |
| 1017 if (FLAG_print_scopes) { | 1007 if (FLAG_print_scopes) { |
| 1018 // Print the function scope (again) after generating the prologue in order | 1008 // Print the function scope (again) after generating the prologue in order |
| 1019 // to see annotations such as allocation indices of locals. | 1009 // to see annotations such as allocation indices of locals. |
| 1020 if (FLAG_print_ast) { | 1010 if (FLAG_print_ast) { |
| 1021 // Second printing. | 1011 // Second printing. |
| 1022 OS::Print("Annotated "); | 1012 OS::Print("Annotated "); |
| 1023 } | 1013 } |
| 1024 AstPrinter::PrintFunctionScope(parsed_function()); | 1014 AstPrinter::PrintFunctionScope(parsed_function()); |
| 1025 } | 1015 } |
| 1026 | 1016 |
| 1027 VisitBlocks(); | 1017 VisitBlocks(); |
| 1028 | 1018 |
| 1029 __ int3(); | 1019 __ int3(); |
| 1030 GenerateDeferredCode(); | 1020 GenerateDeferredCode(); |
| 1031 // Emit function patching code. This will be swapped with the first 5 bytes | 1021 // Emit function patching code. This will be swapped with the first 5 bytes |
| 1032 // at entry point. | 1022 // at entry point. |
| 1033 pc_descriptors_list()->AddDescriptor(PcDescriptors::kPatchCode, | 1023 pc_descriptors_list()->AddDescriptor(PcDescriptors::kPatchCode, |
| 1034 assembler()->CodeSize(), | 1024 assembler()->CodeSize(), |
| 1035 AstNode::kNoId, | 1025 Isolate::kNoDeoptId, |
| 1036 0, | 1026 0, |
| 1037 -1); | 1027 -1); |
| 1038 __ jmp(&StubCode::FixCallersTargetLabel()); | 1028 __ jmp(&StubCode::FixCallersTargetLabel()); |
| 1039 } | 1029 } |
| 1040 | 1030 |
| 1041 | 1031 |
| 1042 void FlowGraphCompiler::GenerateCall(intptr_t token_pos, | 1032 void FlowGraphCompiler::GenerateCall(intptr_t token_pos, |
| 1043 intptr_t try_index, | 1033 intptr_t try_index, |
| 1044 const ExternalLabel* label, | 1034 const ExternalLabel* label, |
| 1045 PcDescriptors::Kind kind) { | 1035 PcDescriptors::Kind kind) { |
| 1046 ASSERT(!IsLeaf()); | 1036 ASSERT(!IsLeaf()); |
| 1047 ASSERT(frame_register_allocator()->IsSpilled()); | 1037 ASSERT(frame_register_allocator()->IsSpilled()); |
| 1048 __ call(label); | 1038 __ call(label); |
| 1049 AddCurrentDescriptor(kind, AstNode::kNoId, token_pos, try_index); | 1039 AddCurrentDescriptor(kind, Isolate::kNoDeoptId, token_pos, try_index); |
| 1050 } | 1040 } |
| 1051 | 1041 |
| 1052 | 1042 |
| 1053 void FlowGraphCompiler::GenerateCallRuntime(intptr_t cid, | 1043 void FlowGraphCompiler::GenerateCallRuntime(intptr_t deopt_id, |
| 1054 intptr_t token_pos, | 1044 intptr_t token_pos, |
| 1055 intptr_t try_index, | 1045 intptr_t try_index, |
| 1056 const RuntimeEntry& entry) { | 1046 const RuntimeEntry& entry) { |
| 1057 ASSERT(!IsLeaf()); | 1047 ASSERT(!IsLeaf()); |
| 1058 ASSERT(frame_register_allocator()->IsSpilled()); | 1048 ASSERT(frame_register_allocator()->IsSpilled()); |
| 1059 __ CallRuntime(entry); | 1049 __ CallRuntime(entry); |
| 1060 AddCurrentDescriptor(PcDescriptors::kOther, cid, token_pos, try_index); | 1050 AddCurrentDescriptor(PcDescriptors::kOther, deopt_id, token_pos, try_index); |
| 1061 } | 1051 } |
| 1062 | 1052 |
| 1063 | 1053 |
| 1064 intptr_t FlowGraphCompiler::EmitInstanceCall(ExternalLabel* target_label, | 1054 intptr_t FlowGraphCompiler::EmitInstanceCall(ExternalLabel* target_label, |
| 1065 const ICData& ic_data, | 1055 const ICData& ic_data, |
| 1066 const Array& arguments_descriptor, | 1056 const Array& arguments_descriptor, |
| 1067 intptr_t argument_count) { | 1057 intptr_t argument_count) { |
| 1068 ASSERT(!IsLeaf()); | 1058 ASSERT(!IsLeaf()); |
| 1069 __ LoadObject(ECX, ic_data); | 1059 __ LoadObject(ECX, ic_data); |
| 1070 __ LoadObject(EDX, arguments_descriptor); | 1060 __ LoadObject(EDX, arguments_descriptor); |
| (...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1266 __ popl(ECX); | 1256 __ popl(ECX); |
| 1267 __ popl(EAX); | 1257 __ popl(EAX); |
| 1268 } | 1258 } |
| 1269 | 1259 |
| 1270 | 1260 |
| 1271 #undef __ | 1261 #undef __ |
| 1272 | 1262 |
| 1273 } // namespace dart | 1263 } // namespace dart |
| 1274 | 1264 |
| 1275 #endif // defined TARGET_ARCH_IA32 | 1265 #endif // defined TARGET_ARCH_IA32 |
| OLD | NEW |