| 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_X64. | 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_X64. |
| 6 #if defined(TARGET_ARCH_X64) | 6 #if defined(TARGET_ARCH_X64) |
| 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 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 142 } | 142 } |
| 143 | 143 |
| 144 | 144 |
| 145 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if | 145 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if |
| 146 // type test is conclusive, otherwise fallthrough if a type test could not | 146 // type test is conclusive, otherwise fallthrough if a type test could not |
| 147 // be completed. | 147 // be completed. |
| 148 // RAX: instance (must survive). | 148 // RAX: instance (must survive). |
| 149 // Clobbers R10. | 149 // Clobbers R10. |
| 150 RawSubtypeTestCache* | 150 RawSubtypeTestCache* |
| 151 FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest( | 151 FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest( |
| 152 intptr_t cid, | |
| 153 intptr_t token_pos, | 152 intptr_t token_pos, |
| 154 const AbstractType& type, | 153 const AbstractType& type, |
| 155 Label* is_instance_lbl, | 154 Label* is_instance_lbl, |
| 156 Label* is_not_instance_lbl) { | 155 Label* is_not_instance_lbl) { |
| 157 ASSERT(type.IsInstantiated()); | 156 ASSERT(type.IsInstantiated()); |
| 158 const Class& type_class = Class::ZoneHandle(type.type_class()); | 157 const Class& type_class = Class::ZoneHandle(type.type_class()); |
| 159 ASSERT(type_class.HasTypeArguments()); | 158 ASSERT(type_class.HasTypeArguments()); |
| 160 const Register kInstanceReg = RAX; | 159 const Register kInstanceReg = RAX; |
| 161 // A Smi object cannot be the instance of a parameterized class. | 160 // A Smi object cannot be the instance of a parameterized class. |
| 162 __ testq(kInstanceReg, Immediate(kSmiTagMask)); | 161 __ testq(kInstanceReg, Immediate(kSmiTagMask)); |
| 163 __ j(ZERO, is_not_instance_lbl); | 162 __ j(ZERO, is_not_instance_lbl); |
| 164 const AbstractTypeArguments& type_arguments = | 163 const AbstractTypeArguments& type_arguments = |
| 165 AbstractTypeArguments::ZoneHandle(type.arguments()); | 164 AbstractTypeArguments::ZoneHandle(type.arguments()); |
| 166 const bool is_raw_type = type_arguments.IsNull() || | 165 const bool is_raw_type = type_arguments.IsNull() || |
| 167 type_arguments.IsRaw(type_arguments.Length()); | 166 type_arguments.IsRaw(type_arguments.Length()); |
| 168 if (is_raw_type) { | 167 if (is_raw_type) { |
| 169 const Register kClassIdReg = R10; | 168 const Register kClassIdReg = R10; |
| 170 // Dynamic type argument, check only classes. | 169 // Dynamic type argument, check only classes. |
| 171 // List is a very common case. | 170 // List is a very common case. |
| 172 __ LoadClassId(kClassIdReg, kInstanceReg); | 171 __ LoadClassId(kClassIdReg, kInstanceReg); |
| 173 if (!type_class.is_interface()) { | 172 if (!type_class.is_interface()) { |
| 174 __ cmpl(kClassIdReg, Immediate(type_class.id())); | 173 __ cmpl(kClassIdReg, Immediate(type_class.id())); |
| 175 __ j(EQUAL, is_instance_lbl); | 174 __ j(EQUAL, is_instance_lbl); |
| 176 } | 175 } |
| 177 if (type.IsListInterface()) { | 176 if (type.IsListInterface()) { |
| 178 GenerateListTypeCheck(kClassIdReg, is_instance_lbl); | 177 GenerateListTypeCheck(kClassIdReg, is_instance_lbl); |
| 179 } | 178 } |
| 180 return GenerateSubtype1TestCacheLookup( | 179 return GenerateSubtype1TestCacheLookup( |
| 181 cid, token_pos, type_class, is_instance_lbl, is_not_instance_lbl); | 180 token_pos, type_class, is_instance_lbl, is_not_instance_lbl); |
| 182 } | 181 } |
| 183 // If one type argument only, check if type argument is Object or Dynamic. | 182 // If one type argument only, check if type argument is Object or Dynamic. |
| 184 if (type_arguments.Length() == 1) { | 183 if (type_arguments.Length() == 1) { |
| 185 const AbstractType& tp_argument = AbstractType::ZoneHandle( | 184 const AbstractType& tp_argument = AbstractType::ZoneHandle( |
| 186 type_arguments.TypeAt(0)); | 185 type_arguments.TypeAt(0)); |
| 187 ASSERT(!tp_argument.IsMalformed()); | 186 ASSERT(!tp_argument.IsMalformed()); |
| 188 if (tp_argument.IsType()) { | 187 if (tp_argument.IsType()) { |
| 189 ASSERT(tp_argument.HasResolvedTypeClass()); | 188 ASSERT(tp_argument.HasResolvedTypeClass()); |
| 190 // Check if type argument is dynamic or Object. | 189 // Check if type argument is dynamic or Object. |
| 191 const Type& object_type = Type::Handle(Type::ObjectType()); | 190 const Type& object_type = Type::Handle(Type::ObjectType()); |
| 192 if (object_type.IsSubtypeOf(tp_argument, NULL)) { | 191 if (object_type.IsSubtypeOf(tp_argument, NULL)) { |
| 193 // Instance class test only necessary. | 192 // Instance class test only necessary. |
| 194 return GenerateSubtype1TestCacheLookup( | 193 return GenerateSubtype1TestCacheLookup( |
| 195 cid, token_pos, type_class, is_instance_lbl, is_not_instance_lbl); | 194 token_pos, type_class, is_instance_lbl, is_not_instance_lbl); |
| 196 } | 195 } |
| 197 } | 196 } |
| 198 } | 197 } |
| 199 // Regular subtype test cache involving instance's type arguments. | 198 // Regular subtype test cache involving instance's type arguments. |
| 200 const Register kTypeArgumentsReg = kNoRegister; | 199 const Register kTypeArgumentsReg = kNoRegister; |
| 201 const Register kTempReg = R10; | 200 const Register kTempReg = R10; |
| 202 return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, | 201 return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, |
| 203 kInstanceReg, | 202 kInstanceReg, |
| 204 kTypeArgumentsReg, | 203 kTypeArgumentsReg, |
| 205 kTempReg, | 204 kTempReg, |
| (...skipping 13 matching lines...) Expand all Loading... |
| 219 __ jmp(is_not_equal_lbl); | 218 __ jmp(is_not_equal_lbl); |
| 220 } | 219 } |
| 221 | 220 |
| 222 | 221 |
| 223 // Testing against an instantiated type with no arguments, without | 222 // Testing against an instantiated type with no arguments, without |
| 224 // SubtypeTestCache. | 223 // SubtypeTestCache. |
| 225 // RAX: instance to test against (preserved). | 224 // RAX: instance to test against (preserved). |
| 226 // Clobbers R10, R13. | 225 // Clobbers R10, R13. |
| 227 // Returns true if there is a fallthrough. | 226 // Returns true if there is a fallthrough. |
| 228 bool FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest( | 227 bool FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest( |
| 229 intptr_t cid, | |
| 230 intptr_t token_pos, | 228 intptr_t token_pos, |
| 231 const AbstractType& type, | 229 const AbstractType& type, |
| 232 Label* is_instance_lbl, | 230 Label* is_instance_lbl, |
| 233 Label* is_not_instance_lbl) { | 231 Label* is_not_instance_lbl) { |
| 234 ASSERT(type.IsInstantiated()); | 232 ASSERT(type.IsInstantiated()); |
| 235 const Class& type_class = Class::Handle(type.type_class()); | 233 const Class& type_class = Class::Handle(type.type_class()); |
| 236 ASSERT(!type_class.HasTypeArguments()); | 234 ASSERT(!type_class.HasTypeArguments()); |
| 237 | 235 |
| 238 const Register kInstanceReg = RAX; | 236 const Register kInstanceReg = RAX; |
| 239 Label compare_classes; | 237 Label compare_classes; |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 294 | 292 |
| 295 | 293 |
| 296 // Uses SubtypeTestCache to store instance class and result. | 294 // Uses SubtypeTestCache to store instance class and result. |
| 297 // RAX: instance to test. | 295 // RAX: instance to test. |
| 298 // Clobbers R10, R13. | 296 // Clobbers R10, R13. |
| 299 // Immediate class test already done. | 297 // Immediate class test already done. |
| 300 // TODO(srdjan): Implement a quicker subtype check, as type test | 298 // TODO(srdjan): Implement a quicker subtype check, as type test |
| 301 // arrays can grow too high, but they may be useful when optimizing | 299 // arrays can grow too high, but they may be useful when optimizing |
| 302 // code (type-feedback). | 300 // code (type-feedback). |
| 303 RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup( | 301 RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup( |
| 304 intptr_t cid, | |
| 305 intptr_t token_pos, | 302 intptr_t token_pos, |
| 306 const Class& type_class, | 303 const Class& type_class, |
| 307 Label* is_instance_lbl, | 304 Label* is_instance_lbl, |
| 308 Label* is_not_instance_lbl) { | 305 Label* is_not_instance_lbl) { |
| 309 const Register kInstanceReg = RAX; | 306 const Register kInstanceReg = RAX; |
| 310 __ LoadClass(R10, kInstanceReg); | 307 __ LoadClass(R10, kInstanceReg); |
| 311 // R10: instance class. | 308 // R10: instance class. |
| 312 // Check immediate superclass equality. | 309 // Check immediate superclass equality. |
| 313 __ movq(R13, FieldAddress(R10, Class::super_type_offset())); | 310 __ movq(R13, FieldAddress(R10, Class::super_type_offset())); |
| 314 __ movq(R13, FieldAddress(R13, Type::type_class_offset())); | 311 __ movq(R13, FieldAddress(R13, Type::type_class_offset())); |
| 315 __ CompareObject(R13, type_class); | 312 __ CompareObject(R13, type_class); |
| 316 __ j(EQUAL, is_instance_lbl); | 313 __ j(EQUAL, is_instance_lbl); |
| 317 | 314 |
| 318 const Register kTypeArgumentsReg = kNoRegister; | 315 const Register kTypeArgumentsReg = kNoRegister; |
| 319 const Register kTempReg = R10; | 316 const Register kTempReg = R10; |
| 320 return GenerateCallSubtypeTestStub(kTestTypeOneArg, | 317 return GenerateCallSubtypeTestStub(kTestTypeOneArg, |
| 321 kInstanceReg, | 318 kInstanceReg, |
| 322 kTypeArgumentsReg, | 319 kTypeArgumentsReg, |
| 323 kTempReg, | 320 kTempReg, |
| 324 is_instance_lbl, | 321 is_instance_lbl, |
| 325 is_not_instance_lbl); | 322 is_not_instance_lbl); |
| 326 } | 323 } |
| 327 | 324 |
| 328 | 325 |
| 329 // Generates inlined check if 'type' is a type parameter or type itsef | 326 // Generates inlined check if 'type' is a type parameter or type itsef |
| 330 // RAX: instance (preserved). | 327 // RAX: instance (preserved). |
| 331 // Clobbers RDI, RDX, R10. | 328 // Clobbers RDI, RDX, R10. |
| 332 RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( | 329 RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( |
| 333 intptr_t cid, | |
| 334 intptr_t token_pos, | 330 intptr_t token_pos, |
| 335 const AbstractType& type, | 331 const AbstractType& type, |
| 336 Label* is_instance_lbl, | 332 Label* is_instance_lbl, |
| 337 Label* is_not_instance_lbl) { | 333 Label* is_not_instance_lbl) { |
| 338 ASSERT(!type.IsInstantiated()); | 334 ASSERT(!type.IsInstantiated()); |
| 339 // Skip check if destination is a dynamic type. | 335 // Skip check if destination is a dynamic type. |
| 340 const Immediate raw_null = | 336 const Immediate raw_null = |
| 341 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 337 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 342 if (type.IsTypeParameter()) { | 338 if (type.IsTypeParameter()) { |
| 343 const TypeParameter& type_param = TypeParameter::Cast(type); | 339 const TypeParameter& type_param = TypeParameter::Cast(type); |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 415 // Inputs: | 411 // Inputs: |
| 416 // - RAX: instance to test against (preserved). | 412 // - RAX: instance to test against (preserved). |
| 417 // - RDX: optional instantiator type arguments (preserved). | 413 // - RDX: optional instantiator type arguments (preserved). |
| 418 // Clobbers R10, R13. | 414 // Clobbers R10, R13. |
| 419 // Returns: | 415 // Returns: |
| 420 // - preserved instance in RAX and optional instantiator type arguments in RDX. | 416 // - preserved instance in RAX and optional instantiator type arguments in RDX. |
| 421 // Note that this inlined code must be followed by the runtime_call code, as it | 417 // Note that this inlined code must be followed by the runtime_call code, as it |
| 422 // may fall through to it. Otherwise, this inline code will jump to the label | 418 // may fall through to it. Otherwise, this inline code will jump to the label |
| 423 // is_instance or to the label is_not_instance. | 419 // is_instance or to the label is_not_instance. |
| 424 RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( | 420 RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( |
| 425 intptr_t cid, | |
| 426 intptr_t token_pos, | 421 intptr_t token_pos, |
| 427 const AbstractType& type, | 422 const AbstractType& type, |
| 428 Label* is_instance_lbl, | 423 Label* is_instance_lbl, |
| 429 Label* is_not_instance_lbl) { | 424 Label* is_not_instance_lbl) { |
| 430 if (type.IsVoidType()) { | 425 if (type.IsVoidType()) { |
| 431 // A non-null value is returned from a void function, which will result in a | 426 // A non-null value is returned from a void function, which will result in a |
| 432 // type error. A null value is handled prior to executing this inline code. | 427 // type error. A null value is handled prior to executing this inline code. |
| 433 return SubtypeTestCache::null(); | 428 return SubtypeTestCache::null(); |
| 434 } | 429 } |
| 435 if (type.IsInstantiated()) { | 430 if (type.IsInstantiated()) { |
| 436 const Class& type_class = Class::ZoneHandle(type.type_class()); | 431 const Class& type_class = Class::ZoneHandle(type.type_class()); |
| 437 // A Smi object cannot be the instance of a parameterized class. | 432 // A Smi object cannot be the instance of a parameterized class. |
| 438 // A class equality check is only applicable with a dst type of a | 433 // A class equality check is only applicable with a dst type of a |
| 439 // non-parameterized class or with a raw dst type of a parameterized class. | 434 // non-parameterized class or with a raw dst type of a parameterized class. |
| 440 if (type_class.HasTypeArguments()) { | 435 if (type_class.HasTypeArguments()) { |
| 441 return GenerateInstantiatedTypeWithArgumentsTest(cid, | 436 return GenerateInstantiatedTypeWithArgumentsTest(token_pos, |
| 442 token_pos, | |
| 443 type, | 437 type, |
| 444 is_instance_lbl, | 438 is_instance_lbl, |
| 445 is_not_instance_lbl); | 439 is_not_instance_lbl); |
| 446 // Fall through to runtime call. | 440 // Fall through to runtime call. |
| 447 } | 441 } |
| 448 const bool has_fall_through = | 442 const bool has_fall_through = |
| 449 GenerateInstantiatedTypeNoArgumentsTest(cid, | 443 GenerateInstantiatedTypeNoArgumentsTest(token_pos, |
| 450 token_pos, | |
| 451 type, | 444 type, |
| 452 is_instance_lbl, | 445 is_instance_lbl, |
| 453 is_not_instance_lbl); | 446 is_not_instance_lbl); |
| 454 if (has_fall_through) { | 447 if (has_fall_through) { |
| 455 // If test non-conclusive so far, try the inlined type-test cache. | 448 // If test non-conclusive so far, try the inlined type-test cache. |
| 456 // 'type' is known at compile time. | 449 // 'type' is known at compile time. |
| 457 return GenerateSubtype1TestCacheLookup( | 450 return GenerateSubtype1TestCacheLookup( |
| 458 cid, token_pos, type_class, | 451 token_pos, type_class, is_instance_lbl, is_not_instance_lbl); |
| 459 is_instance_lbl, is_not_instance_lbl); | |
| 460 } else { | 452 } else { |
| 461 return SubtypeTestCache::null(); | 453 return SubtypeTestCache::null(); |
| 462 } | 454 } |
| 463 } | 455 } |
| 464 return GenerateUninstantiatedTypeTest(cid, | 456 return GenerateUninstantiatedTypeTest(token_pos, |
| 465 token_pos, | |
| 466 type, | 457 type, |
| 467 is_instance_lbl, | 458 is_instance_lbl, |
| 468 is_not_instance_lbl); | 459 is_not_instance_lbl); |
| 469 } | 460 } |
| 470 | 461 |
| 471 | 462 |
| 472 // If instanceof type test cannot be performed successfully at compile time and | 463 // If instanceof type test cannot be performed successfully at compile time and |
| 473 // therefore eliminated, optimize it by adding inlined tests for: | 464 // therefore eliminated, optimize it by adding inlined tests for: |
| 474 // - NULL -> return false. | 465 // - NULL -> return false. |
| 475 // - Smi -> compile time subtype check (only if dst class is not parameterized). | 466 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
| 476 // - Class equality (only if class is not parameterized). | 467 // - Class equality (only if class is not parameterized). |
| 477 // Inputs: | 468 // Inputs: |
| 478 // - RAX: object. | 469 // - RAX: object. |
| 479 // - RDX: instantiator type arguments or raw_null. | 470 // - RDX: instantiator type arguments or raw_null. |
| 480 // - RCX: instantiator or raw_null. | 471 // - RCX: instantiator or raw_null. |
| 481 // Clobbers RCX and RDX. | 472 // Clobbers RCX and RDX. |
| 482 // Returns: | 473 // Returns: |
| 483 // - true or false in RAX. | 474 // - true or false in RAX. |
| 484 void FlowGraphCompiler::GenerateInstanceOf(intptr_t cid, | 475 void FlowGraphCompiler::GenerateInstanceOf(intptr_t deopt_id, |
| 485 intptr_t token_pos, | 476 intptr_t token_pos, |
| 486 intptr_t try_index, | 477 intptr_t try_index, |
| 487 const AbstractType& type, | 478 const AbstractType& type, |
| 488 bool negate_result) { | 479 bool negate_result) { |
| 489 ASSERT(type.IsFinalized() && !type.IsMalformed()); | 480 ASSERT(type.IsFinalized() && !type.IsMalformed()); |
| 490 | 481 |
| 491 const Immediate raw_null = | 482 const Immediate raw_null = |
| 492 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 483 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 493 Label is_instance, is_not_instance; | 484 Label is_instance, is_not_instance; |
| 494 __ pushq(RCX); // Store instantiator on stack. | 485 __ pushq(RCX); // Store instantiator on stack. |
| 495 __ pushq(RDX); // Store instantiator type arguments. | 486 __ pushq(RDX); // Store instantiator type arguments. |
| 496 // If type is instantiated and non-parameterized, we can inline code | 487 // If type is instantiated and non-parameterized, we can inline code |
| 497 // checking whether the tested instance is a Smi. | 488 // checking whether the tested instance is a Smi. |
| 498 if (type.IsInstantiated()) { | 489 if (type.IsInstantiated()) { |
| 499 // A null object is only an instance of Object and Dynamic, which has | 490 // A null object is only an instance of Object and Dynamic, which has |
| 500 // already been checked above (if the type is instantiated). So we can | 491 // already been checked above (if the type is instantiated). So we can |
| 501 // return false here if the instance is null (and if the type is | 492 // return false here if the instance is null (and if the type is |
| 502 // instantiated). | 493 // instantiated). |
| 503 // We can only inline this null check if the type is instantiated at compile | 494 // We can only inline this null check if the type is instantiated at compile |
| 504 // time, since an uninstantiated type at compile time could be Object or | 495 // time, since an uninstantiated type at compile time could be Object or |
| 505 // Dynamic at run time. | 496 // Dynamic at run time. |
| 506 __ cmpq(RAX, raw_null); | 497 __ cmpq(RAX, raw_null); |
| 507 __ j(EQUAL, &is_not_instance); | 498 __ j(EQUAL, &is_not_instance); |
| 508 } | 499 } |
| 509 | 500 |
| 510 // Generate inline instanceof test. | 501 // Generate inline instanceof test. |
| 511 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); | 502 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); |
| 512 test_cache = GenerateInlineInstanceof(cid, token_pos, type, | 503 test_cache = GenerateInlineInstanceof(token_pos, type, |
| 513 &is_instance, &is_not_instance); | 504 &is_instance, &is_not_instance); |
| 514 | 505 |
| 515 // test_cache is null if there is no fall-through. | 506 // test_cache is null if there is no fall-through. |
| 516 Label done; | 507 Label done; |
| 517 if (!test_cache.IsNull()) { | 508 if (!test_cache.IsNull()) { |
| 518 // Generate runtime call. | 509 // Generate runtime call. |
| 519 __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments. | 510 __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments. |
| 520 __ movq(RCX, Address(RSP, kWordSize)); // Get instantiator. | 511 __ movq(RCX, Address(RSP, kWordSize)); // Get instantiator. |
| 521 __ PushObject(Object::ZoneHandle()); // Make room for the result. | 512 __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| 522 __ pushq(Immediate(Smi::RawValue(cid))); // Computation id. | |
| 523 __ pushq(RAX); // Push the instance. | 513 __ pushq(RAX); // Push the instance. |
| 524 __ PushObject(type); // Push the type. | 514 __ PushObject(type); // Push the type. |
| 525 __ pushq(RCX); // TODO(srdjan): Pass instantiator instead of null. | 515 __ pushq(RCX); // TODO(srdjan): Pass instantiator instead of null. |
| 526 __ pushq(RDX); // Instantiator type arguments. | 516 __ pushq(RDX); // Instantiator type arguments. |
| 527 __ LoadObject(RAX, test_cache); | 517 __ LoadObject(RAX, test_cache); |
| 528 __ pushq(RAX); | 518 __ pushq(RAX); |
| 529 GenerateCallRuntime(cid, token_pos, try_index, kInstanceofRuntimeEntry); | 519 GenerateCallRuntime(deopt_id, token_pos, try_index, |
| 520 kInstanceofRuntimeEntry); |
| 530 // Pop the parameters supplied to the runtime entry. The result of the | 521 // Pop the parameters supplied to the runtime entry. The result of the |
| 531 // instanceof runtime call will be left as the result of the operation. | 522 // instanceof runtime call will be left as the result of the operation. |
| 532 __ Drop(6); | 523 __ Drop(5); |
| 533 if (negate_result) { | 524 if (negate_result) { |
| 534 __ popq(RDX); | 525 __ popq(RDX); |
| 535 __ LoadObject(RAX, bool_true()); | 526 __ LoadObject(RAX, bool_true()); |
| 536 __ cmpq(RDX, RAX); | 527 __ cmpq(RDX, RAX); |
| 537 __ j(NOT_EQUAL, &done, Assembler::kNearJump); | 528 __ j(NOT_EQUAL, &done, Assembler::kNearJump); |
| 538 __ LoadObject(RAX, bool_false()); | 529 __ LoadObject(RAX, bool_false()); |
| 539 } else { | 530 } else { |
| 540 __ popq(RAX); | 531 __ popq(RAX); |
| 541 } | 532 } |
| 542 __ jmp(&done, Assembler::kNearJump); | 533 __ jmp(&done, Assembler::kNearJump); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 558 // - Smi -> compile time subtype check (only if dst class is not parameterized). | 549 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
| 559 // - Class equality (only if class is not parameterized). | 550 // - Class equality (only if class is not parameterized). |
| 560 // Inputs: | 551 // Inputs: |
| 561 // - RAX: object. | 552 // - RAX: object. |
| 562 // - RDX: instantiator type arguments or raw_null. | 553 // - RDX: instantiator type arguments or raw_null. |
| 563 // - RCX: instantiator or raw_null. | 554 // - RCX: instantiator or raw_null. |
| 564 // Returns: | 555 // Returns: |
| 565 // - object in RAX for successful assignable check (or throws TypeError). | 556 // - object in RAX for successful assignable check (or throws TypeError). |
| 566 // Performance notes: positive checks must be quick, negative checks can be slow | 557 // Performance notes: positive checks must be quick, negative checks can be slow |
| 567 // as they throw an exception. | 558 // as they throw an exception. |
| 568 void FlowGraphCompiler::GenerateAssertAssignable(intptr_t cid, | 559 void FlowGraphCompiler::GenerateAssertAssignable(intptr_t deopt_id, |
| 569 intptr_t token_pos, | 560 intptr_t token_pos, |
| 570 intptr_t try_index, | 561 intptr_t try_index, |
| 571 const AbstractType& dst_type, | 562 const AbstractType& dst_type, |
| 572 const String& dst_name) { | 563 const String& dst_name) { |
| 573 ASSERT(token_pos >= 0); | 564 ASSERT(token_pos >= 0); |
| 574 ASSERT(!dst_type.IsNull()); | 565 ASSERT(!dst_type.IsNull()); |
| 575 ASSERT(dst_type.IsFinalized()); | 566 ASSERT(dst_type.IsFinalized()); |
| 576 // Assignable check is skipped in FlowGraphBuilder, not here. | 567 // Assignable check is skipped in FlowGraphBuilder, not here. |
| 577 ASSERT(dst_type.IsMalformed() || | 568 ASSERT(dst_type.IsMalformed() || |
| 578 (!dst_type.IsDynamicType() && !dst_type.IsObjectType())); | 569 (!dst_type.IsDynamicType() && !dst_type.IsObjectType())); |
| 579 __ pushq(RCX); // Store instantiator. | 570 __ pushq(RCX); // Store instantiator. |
| 580 __ pushq(RDX); // Store instantiator type arguments. | 571 __ pushq(RDX); // Store instantiator type arguments. |
| 581 // A null object is always assignable and is returned as result. | 572 // A null object is always assignable and is returned as result. |
| 582 const Immediate raw_null = | 573 const Immediate raw_null = |
| 583 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 574 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 584 Label is_assignable, runtime_call; | 575 Label is_assignable, runtime_call; |
| 585 __ cmpq(RAX, raw_null); | 576 __ cmpq(RAX, raw_null); |
| 586 __ j(EQUAL, &is_assignable); | 577 __ j(EQUAL, &is_assignable); |
| 587 | 578 |
| 588 // Generate throw new TypeError() if the type is malformed. | 579 // Generate throw new TypeError() if the type is malformed. |
| 589 if (dst_type.IsMalformed()) { | 580 if (dst_type.IsMalformed()) { |
| 590 const Error& error = Error::Handle(dst_type.malformed_error()); | 581 const Error& error = Error::Handle(dst_type.malformed_error()); |
| 591 const String& error_message = String::ZoneHandle( | 582 const String& error_message = String::ZoneHandle( |
| 592 Symbols::New(error.ToErrorCString())); | 583 Symbols::New(error.ToErrorCString())); |
| 593 __ PushObject(Object::ZoneHandle()); // Make room for the result. | 584 __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| 594 __ pushq(RAX); // Push the source object. | 585 __ pushq(RAX); // Push the source object. |
| 595 __ PushObject(dst_name); // Push the name of the destination. | 586 __ PushObject(dst_name); // Push the name of the destination. |
| 596 __ PushObject(error_message); | 587 __ PushObject(error_message); |
| 597 GenerateCallRuntime(cid, | 588 GenerateCallRuntime(deopt_id, |
| 598 token_pos, | 589 token_pos, |
| 599 try_index, | 590 try_index, |
| 600 kMalformedTypeErrorRuntimeEntry); | 591 kMalformedTypeErrorRuntimeEntry); |
| 601 // We should never return here. | 592 // We should never return here. |
| 602 __ int3(); | 593 __ int3(); |
| 603 | 594 |
| 604 __ Bind(&is_assignable); // For a null object. | 595 __ Bind(&is_assignable); // For a null object. |
| 605 return; | 596 return; |
| 606 } | 597 } |
| 607 | 598 |
| 608 // Generate inline type check, linking to runtime call if not assignable. | 599 // Generate inline type check, linking to runtime call if not assignable. |
| 609 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); | 600 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); |
| 610 test_cache = GenerateInlineInstanceof(cid, token_pos, dst_type, | 601 test_cache = GenerateInlineInstanceof(token_pos, dst_type, |
| 611 &is_assignable, &runtime_call); | 602 &is_assignable, &runtime_call); |
| 612 | 603 |
| 613 __ Bind(&runtime_call); | 604 __ Bind(&runtime_call); |
| 614 __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments. | 605 __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments. |
| 615 __ movq(RCX, Address(RSP, kWordSize)); // Get instantiator. | 606 __ movq(RCX, Address(RSP, kWordSize)); // Get instantiator. |
| 616 __ PushObject(Object::ZoneHandle()); // Make room for the result. | 607 __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| 617 __ pushq(Immediate(Smi::RawValue(cid))); // Computation id. | |
| 618 __ pushq(RAX); // Push the source object. | 608 __ pushq(RAX); // Push the source object. |
| 619 __ PushObject(dst_type); // Push the type of the destination. | 609 __ PushObject(dst_type); // Push the type of the destination. |
| 620 __ pushq(RCX); // Instantiator. | 610 __ pushq(RCX); // Instantiator. |
| 621 __ pushq(RDX); // Instantiator type arguments. | 611 __ pushq(RDX); // Instantiator type arguments. |
| 622 __ PushObject(dst_name); // Push the name of the destination. | 612 __ PushObject(dst_name); // Push the name of the destination. |
| 623 __ LoadObject(RAX, test_cache); | 613 __ LoadObject(RAX, test_cache); |
| 624 __ pushq(RAX); | 614 __ pushq(RAX); |
| 625 GenerateCallRuntime(cid, | 615 GenerateCallRuntime(deopt_id, |
| 626 token_pos, | 616 token_pos, |
| 627 try_index, | 617 try_index, |
| 628 kTypeCheckRuntimeEntry); | 618 kTypeCheckRuntimeEntry); |
| 629 // Pop the parameters supplied to the runtime entry. The result of the | 619 // Pop the parameters supplied to the runtime entry. The result of the |
| 630 // type check runtime call is the checked value. | 620 // type check runtime call is the checked value. |
| 631 __ Drop(7); | 621 __ Drop(6); |
| 632 __ popq(RAX); | 622 __ popq(RAX); |
| 633 | 623 |
| 634 __ Bind(&is_assignable); | 624 __ Bind(&is_assignable); |
| 635 __ popq(RDX); // Remove pushed instantiator type arguments.. | 625 __ popq(RDX); // Remove pushed instantiator type arguments.. |
| 636 __ popq(RCX); // Remove pushed instantiator. | 626 __ popq(RCX); // Remove pushed instantiator. |
| 637 } | 627 } |
| 638 | 628 |
| 639 | 629 |
| 640 void FlowGraphCompiler::EmitInstructionPrologue(Instruction* instr) { | 630 void FlowGraphCompiler::EmitInstructionPrologue(Instruction* instr) { |
| 641 LocationSummary* locs = instr->locs(); | 631 LocationSummary* locs = instr->locs(); |
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 796 } | 786 } |
| 797 | 787 |
| 798 __ Bind(&wrong_num_arguments); | 788 __ Bind(&wrong_num_arguments); |
| 799 if (StackSize() != 0) { | 789 if (StackSize() != 0) { |
| 800 // We need to unwind the space we reserved for locals and copied parameters. | 790 // We need to unwind the space we reserved for locals and copied parameters. |
| 801 // The NoSuchMethodFunction stub does not expect to see that area on the | 791 // The NoSuchMethodFunction stub does not expect to see that area on the |
| 802 // stack. | 792 // stack. |
| 803 __ addq(RSP, Immediate(StackSize() * kWordSize)); | 793 __ addq(RSP, Immediate(StackSize() * kWordSize)); |
| 804 } | 794 } |
| 805 if (function.IsClosureFunction()) { | 795 if (function.IsClosureFunction()) { |
| 806 GenerateCallRuntime(AstNode::kNoId, | 796 GenerateCallRuntime(Isolate::kNoDeoptId, |
| 807 0, | 797 0, |
| 808 CatchClauseNode::kInvalidTryIndex, | 798 CatchClauseNode::kInvalidTryIndex, |
| 809 kClosureArgumentMismatchRuntimeEntry); | 799 kClosureArgumentMismatchRuntimeEntry); |
| 810 } else { | 800 } else { |
| 811 ASSERT(!IsLeaf()); | 801 ASSERT(!IsLeaf()); |
| 812 // Invoke noSuchMethod function. | 802 // Invoke noSuchMethod function. |
| 813 const int kNumArgsChecked = 1; | 803 const int kNumArgsChecked = 1; |
| 814 ICData& ic_data = ICData::ZoneHandle(); | 804 ICData& ic_data = ICData::ZoneHandle(); |
| 815 ic_data = ICData::New(function, | 805 ic_data = ICData::New(function, |
| 816 String::Handle(function.name()), | 806 String::Handle(function.name()), |
| 817 AstNode::kNoId, | 807 Isolate::kNoDeoptId, |
| 818 kNumArgsChecked); | 808 kNumArgsChecked); |
| 819 __ LoadObject(RBX, ic_data); | 809 __ LoadObject(RBX, ic_data); |
| 820 // RBP - 8 : PC marker, allows easy identification of RawInstruction obj. | 810 // RBP - 8 : PC marker, allows easy identification of RawInstruction obj. |
| 821 // RBP : points to previous frame pointer. | 811 // RBP : points to previous frame pointer. |
| 822 // RBP + 8 : points to return address. | 812 // RBP + 8 : points to return address. |
| 823 // RBP + 16 : address of last argument (arg n-1). | 813 // RBP + 16 : address of last argument (arg n-1). |
| 824 // RSP + 16 + 8*(n-1) : address of first argument (arg 0). | 814 // RSP + 16 + 8*(n-1) : address of first argument (arg 0). |
| 825 // RBX : ic-data. | 815 // RBX : ic-data. |
| 826 // R10 : arguments descriptor array. | 816 // R10 : arguments descriptor array. |
| 827 __ call(&StubCode::CallNoSuchMethodFunctionLabel()); | 817 __ call(&StubCode::CallNoSuchMethodFunctionLabel()); |
| 828 } | 818 } |
| 829 | 819 |
| 830 if (FLAG_trace_functions) { | 820 if (FLAG_trace_functions) { |
| 831 __ pushq(RAX); // Preserve result. | 821 __ pushq(RAX); // Preserve result. |
| 832 __ PushObject(Function::ZoneHandle(function.raw())); | 822 __ PushObject(Function::ZoneHandle(function.raw())); |
| 833 GenerateCallRuntime(AstNode::kNoId, | 823 GenerateCallRuntime(Isolate::kNoDeoptId, |
| 834 0, | 824 0, |
| 835 CatchClauseNode::kInvalidTryIndex, | 825 CatchClauseNode::kInvalidTryIndex, |
| 836 kTraceFunctionExitRuntimeEntry); | 826 kTraceFunctionExitRuntimeEntry); |
| 837 __ popq(RAX); // Remove argument. | 827 __ popq(RAX); // Remove argument. |
| 838 __ popq(RAX); // Restore result. | 828 __ popq(RAX); // Restore result. |
| 839 } | 829 } |
| 840 __ LeaveFrame(); | 830 __ LeaveFrame(); |
| 841 __ ret(); | 831 __ ret(); |
| 842 | 832 |
| 843 __ Bind(&all_arguments_processed); | 833 __ Bind(&all_arguments_processed); |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 945 const bool check_arguments = function.IsClosureFunction(); | 935 const bool check_arguments = function.IsClosureFunction(); |
| 946 #endif | 936 #endif |
| 947 if (check_arguments) { | 937 if (check_arguments) { |
| 948 // Check that num_fixed <= argc <= num_params. | 938 // Check that num_fixed <= argc <= num_params. |
| 949 Label argc_in_range; | 939 Label argc_in_range; |
| 950 // Total number of args is the first Smi in args descriptor array (R10). | 940 // Total number of args is the first Smi in args descriptor array (R10). |
| 951 __ movq(RAX, FieldAddress(R10, Array::data_offset())); | 941 __ movq(RAX, FieldAddress(R10, Array::data_offset())); |
| 952 __ cmpq(RAX, Immediate(Smi::RawValue(parameter_count))); | 942 __ cmpq(RAX, Immediate(Smi::RawValue(parameter_count))); |
| 953 __ j(EQUAL, &argc_in_range, Assembler::kNearJump); | 943 __ j(EQUAL, &argc_in_range, Assembler::kNearJump); |
| 954 if (function.IsClosureFunction()) { | 944 if (function.IsClosureFunction()) { |
| 955 GenerateCallRuntime(AstNode::kNoId, | 945 GenerateCallRuntime(Isolate::kNoDeoptId, |
| 956 function.token_pos(), | 946 function.token_pos(), |
| 957 CatchClauseNode::kInvalidTryIndex, | 947 CatchClauseNode::kInvalidTryIndex, |
| 958 kClosureArgumentMismatchRuntimeEntry); | 948 kClosureArgumentMismatchRuntimeEntry); |
| 959 } else { | 949 } else { |
| 960 __ Stop("Wrong number of arguments"); | 950 __ Stop("Wrong number of arguments"); |
| 961 } | 951 } |
| 962 __ Bind(&argc_in_range); | 952 __ Bind(&argc_in_range); |
| 963 } | 953 } |
| 964 } else { | 954 } else { |
| 965 CopyParameters(); | 955 CopyParameters(); |
| (...skipping 17 matching lines...) Expand all Loading... |
| 983 __ movq(Address(RBP, (slot_base - i) * kWordSize), RAX); | 973 __ movq(Address(RBP, (slot_base - i) * kWordSize), RAX); |
| 984 } | 974 } |
| 985 } | 975 } |
| 986 | 976 |
| 987 if (!IsLeaf()) { | 977 if (!IsLeaf()) { |
| 988 // Generate stack overflow check. | 978 // Generate stack overflow check. |
| 989 __ movq(RDI, Immediate(Isolate::Current()->stack_limit_address())); | 979 __ movq(RDI, Immediate(Isolate::Current()->stack_limit_address())); |
| 990 __ cmpq(RSP, Address(RDI, 0)); | 980 __ cmpq(RSP, Address(RDI, 0)); |
| 991 Label no_stack_overflow; | 981 Label no_stack_overflow; |
| 992 __ j(ABOVE, &no_stack_overflow, Assembler::kNearJump); | 982 __ j(ABOVE, &no_stack_overflow, Assembler::kNearJump); |
| 993 GenerateCallRuntime(AstNode::kNoId, | 983 GenerateCallRuntime(Isolate::kNoDeoptId, |
| 994 function.token_pos(), | 984 function.token_pos(), |
| 995 CatchClauseNode::kInvalidTryIndex, | 985 CatchClauseNode::kInvalidTryIndex, |
| 996 kStackOverflowRuntimeEntry); | 986 kStackOverflowRuntimeEntry); |
| 997 __ Bind(&no_stack_overflow); | 987 __ Bind(&no_stack_overflow); |
| 998 } | 988 } |
| 999 if (FLAG_print_scopes) { | 989 if (FLAG_print_scopes) { |
| 1000 // Print the function scope (again) after generating the prologue in order | 990 // Print the function scope (again) after generating the prologue in order |
| 1001 // to see annotations such as allocation indices of locals. | 991 // to see annotations such as allocation indices of locals. |
| 1002 if (FLAG_print_ast) { | 992 if (FLAG_print_ast) { |
| 1003 // Second printing. | 993 // Second printing. |
| 1004 OS::Print("Annotated "); | 994 OS::Print("Annotated "); |
| 1005 } | 995 } |
| 1006 AstPrinter::PrintFunctionScope(parsed_function()); | 996 AstPrinter::PrintFunctionScope(parsed_function()); |
| 1007 } | 997 } |
| 1008 | 998 |
| 1009 VisitBlocks(); | 999 VisitBlocks(); |
| 1010 | 1000 |
| 1011 __ int3(); | 1001 __ int3(); |
| 1012 GenerateDeferredCode(); | 1002 GenerateDeferredCode(); |
| 1013 // Emit function patching code. This will be swapped with the first 13 bytes | 1003 // Emit function patching code. This will be swapped with the first 13 bytes |
| 1014 // at entry point. | 1004 // at entry point. |
| 1015 pc_descriptors_list()->AddDescriptor(PcDescriptors::kPatchCode, | 1005 pc_descriptors_list()->AddDescriptor(PcDescriptors::kPatchCode, |
| 1016 assembler()->CodeSize(), | 1006 assembler()->CodeSize(), |
| 1017 AstNode::kNoId, | 1007 Isolate::kNoDeoptId, |
| 1018 0, | 1008 0, |
| 1019 -1); | 1009 -1); |
| 1020 __ jmp(&StubCode::FixCallersTargetLabel()); | 1010 __ jmp(&StubCode::FixCallersTargetLabel()); |
| 1021 } | 1011 } |
| 1022 | 1012 |
| 1023 | 1013 |
| 1024 void FlowGraphCompiler::GenerateCall(intptr_t token_pos, | 1014 void FlowGraphCompiler::GenerateCall(intptr_t token_pos, |
| 1025 intptr_t try_index, | 1015 intptr_t try_index, |
| 1026 const ExternalLabel* label, | 1016 const ExternalLabel* label, |
| 1027 PcDescriptors::Kind kind) { | 1017 PcDescriptors::Kind kind) { |
| 1028 ASSERT(!IsLeaf()); | 1018 ASSERT(!IsLeaf()); |
| 1029 ASSERT(frame_register_allocator()->IsSpilled()); | 1019 ASSERT(frame_register_allocator()->IsSpilled()); |
| 1030 __ call(label); | 1020 __ call(label); |
| 1031 AddCurrentDescriptor(kind, AstNode::kNoId, token_pos, try_index); | 1021 AddCurrentDescriptor(kind, Isolate::kNoDeoptId, token_pos, try_index); |
| 1032 } | 1022 } |
| 1033 | 1023 |
| 1034 | 1024 |
| 1035 void FlowGraphCompiler::GenerateCallRuntime(intptr_t cid, | 1025 void FlowGraphCompiler::GenerateCallRuntime(intptr_t deopt_id, |
| 1036 intptr_t token_pos, | 1026 intptr_t token_pos, |
| 1037 intptr_t try_index, | 1027 intptr_t try_index, |
| 1038 const RuntimeEntry& entry) { | 1028 const RuntimeEntry& entry) { |
| 1039 ASSERT(!IsLeaf()); | 1029 ASSERT(!IsLeaf()); |
| 1040 ASSERT(frame_register_allocator()->IsSpilled()); | 1030 ASSERT(frame_register_allocator()->IsSpilled()); |
| 1041 __ CallRuntime(entry); | 1031 __ CallRuntime(entry); |
| 1042 AddCurrentDescriptor(PcDescriptors::kOther, cid, token_pos, try_index); | 1032 AddCurrentDescriptor(PcDescriptors::kOther, deopt_id, token_pos, try_index); |
| 1043 } | 1033 } |
| 1044 | 1034 |
| 1045 | 1035 |
| 1046 intptr_t FlowGraphCompiler::EmitInstanceCall(ExternalLabel* target_label, | 1036 intptr_t FlowGraphCompiler::EmitInstanceCall(ExternalLabel* target_label, |
| 1047 const ICData& ic_data, | 1037 const ICData& ic_data, |
| 1048 const Array& arguments_descriptor, | 1038 const Array& arguments_descriptor, |
| 1049 intptr_t argument_count) { | 1039 intptr_t argument_count) { |
| 1050 ASSERT(!IsLeaf()); | 1040 ASSERT(!IsLeaf()); |
| 1051 __ LoadObject(RBX, ic_data); | 1041 __ LoadObject(RBX, ic_data); |
| 1052 __ LoadObject(R10, arguments_descriptor); | 1042 __ LoadObject(R10, arguments_descriptor); |
| (...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1226 void ParallelMoveResolver::Exchange(const Address& mem1, const Address& mem2) { | 1216 void ParallelMoveResolver::Exchange(const Address& mem1, const Address& mem2) { |
| 1227 __ Exchange(mem1, mem2); | 1217 __ Exchange(mem1, mem2); |
| 1228 } | 1218 } |
| 1229 | 1219 |
| 1230 | 1220 |
| 1231 #undef __ | 1221 #undef __ |
| 1232 | 1222 |
| 1233 } // namespace dart | 1223 } // namespace dart |
| 1234 | 1224 |
| 1235 #endif // defined TARGET_ARCH_X64 | 1225 #endif // defined TARGET_ARCH_X64 |
| OLD | NEW |