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 |