| 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 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 53 const GrowableArray<BlockEntryInstr*>& block_order, | 53 const GrowableArray<BlockEntryInstr*>& block_order, |
| 54 bool is_optimizing) | 54 bool is_optimizing) |
| 55 : FlowGraphCompilerShared(assembler, | 55 : FlowGraphCompilerShared(assembler, |
| 56 parsed_function, | 56 parsed_function, |
| 57 block_order, | 57 block_order, |
| 58 is_optimizing) {} | 58 is_optimizing) {} |
| 59 | 59 |
| 60 #define __ assembler()-> | 60 #define __ assembler()-> |
| 61 | 61 |
| 62 | 62 |
| 63 |
| 64 // Fall through if bool_register contains null. |
| 65 void FlowGraphCompiler::GenerateBoolToJump(Register bool_register, |
| 66 Label* is_true, |
| 67 Label* is_false) { |
| 68 const Immediate raw_null = |
| 69 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 70 Label fall_through; |
| 71 __ cmpq(bool_register, raw_null); |
| 72 __ j(EQUAL, &fall_through, Assembler::kNearJump); |
| 73 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); |
| 74 __ CompareObject(bool_register, bool_true); |
| 75 __ j(EQUAL, is_true); |
| 76 __ jmp(is_false); |
| 77 __ Bind(&fall_through); |
| 78 } |
| 79 |
| 80 |
| 81 RawSubtypeTestCache* FlowGraphCompiler::GenerateCallSubtypeTestStub( |
| 82 TypeTestStubKind test_kind, |
| 83 Register instance_reg, |
| 84 Register type_arguments_reg, |
| 85 Register temp_reg, |
| 86 Label* is_instance_lbl, |
| 87 Label* is_not_instance_lbl) { |
| 88 const SubtypeTestCache& type_test_cache = |
| 89 SubtypeTestCache::ZoneHandle(SubtypeTestCache::New()); |
| 90 const Immediate raw_null = |
| 91 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 92 __ LoadObject(temp_reg, type_test_cache); |
| 93 __ pushq(temp_reg); // Subtype test cache. |
| 94 __ pushq(instance_reg); // Instance. |
| 95 if (test_kind == kTestTypeOneArg) { |
| 96 ASSERT(type_arguments_reg == kNoRegister); |
| 97 __ pushq(raw_null); |
| 98 __ call(&StubCode::Subtype1TestCacheLabel()); |
| 99 } else if (test_kind == kTestTypeTwoArgs) { |
| 100 ASSERT(type_arguments_reg == kNoRegister); |
| 101 __ pushq(raw_null); |
| 102 __ call(&StubCode::Subtype2TestCacheLabel()); |
| 103 } else if (test_kind == kTestTypeThreeArgs) { |
| 104 __ pushq(type_arguments_reg); |
| 105 __ call(&StubCode::Subtype3TestCacheLabel()); |
| 106 } else { |
| 107 UNREACHABLE(); |
| 108 } |
| 109 // Result is in ECX: null -> not found, otherwise Bool::True or Bool::False. |
| 110 __ popq(instance_reg); // Discard. |
| 111 __ popq(instance_reg); // Restore receiver. |
| 112 __ popq(temp_reg); // Discard. |
| 113 GenerateBoolToJump(RCX, is_instance_lbl, is_not_instance_lbl); |
| 114 return type_test_cache.raw(); |
| 115 } |
| 116 |
| 63 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if | 117 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if |
| 64 // type test is conclusive, otherwise fallthrough if a type test could not | 118 // type test is conclusive, otherwise fallthrough if a type test could not |
| 65 // be completed. | 119 // be completed. |
| 66 // RAX: instance (must survive), | 120 // RAX: instance (must survive), |
| 67 RawSubtypeTestCache* | 121 RawSubtypeTestCache* |
| 68 FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest( | 122 FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest( |
| 69 intptr_t cid, | 123 intptr_t cid, |
| 70 intptr_t token_index, | 124 intptr_t token_index, |
| 71 const AbstractType& type, | 125 const AbstractType& type, |
| 72 Label* is_instance_lbl, | 126 Label* is_instance_lbl, |
| 73 Label* is_not_instance_lbl) { | 127 Label* is_not_instance_lbl) { |
| 74 ASSERT(type.IsInstantiated()); | 128 ASSERT(type.IsInstantiated()); |
| 75 const Class& type_class = Class::ZoneHandle(type.type_class()); | 129 const Class& type_class = Class::ZoneHandle(type.type_class()); |
| 76 ASSERT(type_class.HasTypeArguments()); | 130 ASSERT(type_class.HasTypeArguments()); |
| 131 const Register kInstanceReg = RAX; |
| 77 // A Smi object cannot be the instance of a parameterized class. | 132 // A Smi object cannot be the instance of a parameterized class. |
| 78 __ testq(RAX, Immediate(kSmiTagMask)); | 133 __ testq(kInstanceReg, Immediate(kSmiTagMask)); |
| 79 __ j(ZERO, is_not_instance_lbl); | 134 __ j(ZERO, is_not_instance_lbl); |
| 80 const AbstractTypeArguments& type_arguments = | 135 const AbstractTypeArguments& type_arguments = |
| 81 AbstractTypeArguments::ZoneHandle(type.arguments()); | 136 AbstractTypeArguments::ZoneHandle(type.arguments()); |
| 82 const bool is_raw_type = type_arguments.IsNull() || | 137 const bool is_raw_type = type_arguments.IsNull() || |
| 83 type_arguments.IsRaw(type_arguments.Length()); | 138 type_arguments.IsRaw(type_arguments.Length()); |
| 84 if (is_raw_type) { | 139 if (is_raw_type) { |
| 140 const Register kClassIdReg = R10; |
| 85 // Dynamic type argument, check only classes. | 141 // Dynamic type argument, check only classes. |
| 86 // List is a very common case. | 142 // List is a very common case. |
| 87 __ LoadClassId(R10, RAX); | 143 __ LoadClassId(kClassIdReg, kInstanceReg); |
| 88 if (!type_class.is_interface()) { | 144 if (!type_class.is_interface()) { |
| 89 __ cmpl(R10, Immediate(type_class.id())); | 145 __ cmpl(kClassIdReg, Immediate(type_class.id())); |
| 90 __ j(EQUAL, is_instance_lbl); | 146 __ j(EQUAL, is_instance_lbl); |
| 91 } | 147 } |
| 92 if (type.IsListInterface()) { | 148 if (type.IsListInterface()) { |
| 93 Label unknown; | 149 GenerateListTypeCheck(kClassIdReg, is_instance_lbl); |
| 94 GrowableArray<intptr_t> args; | |
| 95 args.Add(kArray); | |
| 96 args.Add(kGrowableObjectArray); | |
| 97 args.Add(kImmutableArray); | |
| 98 CheckClassIds(args, is_instance_lbl, &unknown); | |
| 99 __ Bind(&unknown); | |
| 100 } | 150 } |
| 101 return GenerateSubtype1TestCacheLookup( | 151 return GenerateSubtype1TestCacheLookup( |
| 102 cid, token_index, type_class, is_instance_lbl, is_not_instance_lbl); | 152 cid, token_index, type_class, is_instance_lbl, is_not_instance_lbl); |
| 103 } | 153 } |
| 104 // If one type argument only, check if type argument is Object or Dynamic. | 154 // If one type argument only, check if type argument is Object or Dynamic. |
| 105 if (type_arguments.Length() == 1) { | 155 if (type_arguments.Length() == 1) { |
| 106 const AbstractType& tp_argument = AbstractType::ZoneHandle( | 156 const AbstractType& tp_argument = AbstractType::ZoneHandle( |
| 107 type_arguments.TypeAt(0)); | 157 type_arguments.TypeAt(0)); |
| 108 ASSERT(!tp_argument.IsMalformed()); | 158 ASSERT(!tp_argument.IsMalformed()); |
| 109 if (tp_argument.IsType()) { | 159 if (tp_argument.IsType()) { |
| 110 ASSERT(tp_argument.HasResolvedTypeClass()); | 160 ASSERT(tp_argument.HasResolvedTypeClass()); |
| 111 // Check if type argument is dynamic or Object. | 161 // Check if type argument is dynamic or Object. |
| 112 const Type& object_type = | 162 const Type& object_type = |
| 113 Type::Handle(Isolate::Current()->object_store()->object_type()); | 163 Type::Handle(Isolate::Current()->object_store()->object_type()); |
| 114 Error& malformed_error = Error::Handle(); | 164 Error& malformed_error = Error::Handle(); |
| 115 if (object_type.IsSubtypeOf(tp_argument, &malformed_error)) { | 165 if (object_type.IsSubtypeOf(tp_argument, &malformed_error)) { |
| 116 // Instance class test only necessary. | 166 // Instance class test only necessary. |
| 117 return GenerateSubtype1TestCacheLookup( | 167 return GenerateSubtype1TestCacheLookup( |
| 118 cid, token_index, type_class, is_instance_lbl, is_not_instance_lbl); | 168 cid, token_index, type_class, is_instance_lbl, is_not_instance_lbl); |
| 119 } | 169 } |
| 120 } | 170 } |
| 121 } | 171 } |
| 122 | 172 |
| 123 // Regular subtype test cache involving instance's type arguments. | 173 // Regular subtype test cache involving instance's type arguments. |
| 124 const SubtypeTestCache& type_test_cache = | 174 const Register kTypeArgumentsReg = kNoRegister; |
| 125 SubtypeTestCache::ZoneHandle(SubtypeTestCache::New()); | 175 const Register kTempReg = R10; |
| 126 Label runtime_call; | 176 return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, |
| 127 const Immediate raw_null = | 177 kInstanceReg, |
| 128 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 178 kTypeArgumentsReg, |
| 129 __ LoadObject(R10, type_test_cache); | 179 kTempReg, |
| 130 __ pushq(R10); // Subtype test cache. | 180 is_instance_lbl, |
| 131 __ pushq(RAX); // Instance. | 181 is_not_instance_lbl); |
| 132 __ pushq(raw_null); // Unused. | |
| 133 __ call(&StubCode::Subtype2TestCacheLabel()); | |
| 134 __ popq(RAX); // Discard. | |
| 135 __ popq(RAX); // Restore receiver. | |
| 136 __ popq(RDX); // Discard. | |
| 137 // Result is in RCX: null -> not found, otherwise Bool::True or Bool::False. | |
| 138 | |
| 139 __ cmpq(RCX, raw_null); | |
| 140 __ j(EQUAL, &runtime_call, Assembler::kNearJump); | |
| 141 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | |
| 142 __ CompareObject(RCX, bool_true); | |
| 143 __ j(EQUAL, is_instance_lbl); | |
| 144 __ jmp(is_not_instance_lbl); | |
| 145 __ Bind(&runtime_call); | |
| 146 return type_test_cache.raw(); | |
| 147 } | 182 } |
| 148 | 183 |
| 149 | 184 |
| 150 // R10: instance class id to check. | 185 void FlowGraphCompiler::CheckClassIds(Register class_id_reg, |
| 151 void FlowGraphCompiler::CheckClassIds(const GrowableArray<intptr_t>& class_ids, | 186 const GrowableArray<intptr_t>& class_ids, |
| 152 Label* is_instance_lbl, | 187 Label* is_equal_lbl, |
| 153 Label* is_not_instance_lbl) { | 188 Label* is_not_equal_lbl) { |
| 154 for (intptr_t i = 0; i < class_ids.length(); i++) { | 189 for (intptr_t i = 0; i < class_ids.length(); i++) { |
| 155 __ cmpl(R10, Immediate(class_ids[i])); | 190 __ cmpl(class_id_reg, Immediate(class_ids[i])); |
| 156 __ j(EQUAL, is_instance_lbl); | 191 __ j(EQUAL, is_equal_lbl); |
| 157 } | 192 } |
| 158 __ jmp(is_not_instance_lbl); | 193 __ jmp(is_not_equal_lbl); |
| 159 } | 194 } |
| 160 | 195 |
| 161 | 196 |
| 162 | 197 |
| 163 // Testing against an instantiated type with no arguments, without | 198 // Testing against an instantiated type with no arguments, without |
| 164 // SubtypeTestCache. | 199 // SubtypeTestCache. |
| 165 // RAX: instance to test against (preserved). | 200 // RAX: instance to test against (preserved). |
| 166 void FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest( | 201 void FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest( |
| 167 intptr_t cid, | 202 intptr_t cid, |
| 168 intptr_t token_index, | 203 intptr_t token_index, |
| (...skipping 15 matching lines...) Expand all Loading... |
| 184 type_class, | 219 type_class, |
| 185 TypeArguments::Handle(), | 220 TypeArguments::Handle(), |
| 186 &malformed_error)) { | 221 &malformed_error)) { |
| 187 __ jmp(is_instance_lbl); | 222 __ jmp(is_instance_lbl); |
| 188 } else { | 223 } else { |
| 189 __ jmp(is_not_instance_lbl); | 224 __ jmp(is_not_instance_lbl); |
| 190 } | 225 } |
| 191 | 226 |
| 192 // Compare if the classes are equal. Instance is not Smi. | 227 // Compare if the classes are equal. Instance is not Smi. |
| 193 __ Bind(&compare_classes); | 228 __ Bind(&compare_classes); |
| 194 __ LoadClassId(R10, RAX); | 229 const Register kClassIdReg = R10; |
| 230 __ LoadClassId(kClassIdReg, RAX); |
| 195 // If type is an interface, we can skip the class equality check. | 231 // If type is an interface, we can skip the class equality check. |
| 196 if (!type_class.is_interface()) { | 232 if (!type_class.is_interface()) { |
| 197 __ cmpl(R10, Immediate(type_class.id())); | 233 __ cmpl(kClassIdReg, Immediate(type_class.id())); |
| 198 __ j(EQUAL, is_instance_lbl); | 234 __ j(EQUAL, is_instance_lbl); |
| 199 } | 235 } |
| 200 // Check for interfaces that cannot be implemented by user. | 236 // Check for interfaces that cannot be implemented by user. |
| 201 // (see ClassFinalizer::ResolveInterfaces for list of restricted interfaces). | 237 // (see ClassFinalizer::ResolveInterfaces for list of restricted interfaces). |
| 202 // Bool interface can be implemented only by core class Bool. | 238 // Bool interface can be implemented only by core class Bool. |
| 203 if (type.IsBoolInterface()) { | 239 if (type.IsBoolInterface()) { |
| 204 __ cmpl(R10, Immediate(kBool)); | 240 __ cmpl(kClassIdReg, Immediate(kBool)); |
| 205 __ j(EQUAL, is_instance_lbl); | 241 __ j(EQUAL, is_instance_lbl); |
| 206 __ jmp(is_not_instance_lbl); | 242 __ jmp(is_not_instance_lbl); |
| 207 return; | 243 return; |
| 208 } | 244 } |
| 209 if (type.IsFunctionInterface()) { | 245 if (type.IsFunctionInterface()) { |
| 210 // Check if instance is a closure. | 246 // Check if instance is a closure. |
| 211 const Immediate raw_null = | 247 const Immediate raw_null = |
| 212 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 248 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 213 __ LoadClassById(R13, R10); | 249 __ LoadClassById(R13, kClassIdReg); |
| 214 __ movq(R13, FieldAddress(R13, Class::signature_function_offset())); | 250 __ movq(R13, FieldAddress(R13, Class::signature_function_offset())); |
| 215 __ cmpq(R13, raw_null); | 251 __ cmpq(R13, raw_null); |
| 216 __ j(NOT_EQUAL, is_instance_lbl); | 252 __ j(NOT_EQUAL, is_instance_lbl); |
| 217 __ jmp(is_not_instance_lbl); | 253 __ jmp(is_not_instance_lbl); |
| 218 return; | 254 return; |
| 219 } | 255 } |
| 220 // Custom checking for numbers (Smi, Mint, Bigint and Double). | 256 // Custom checking for numbers (Smi, Mint, Bigint and Double). |
| 221 // Note that instance is not Smi(checked above). | 257 // Note that instance is not Smi(checked above). |
| 222 if (type.IsSubtypeOf( | 258 if (type.IsSubtypeOf( |
| 223 Type::Handle(Type::NumberInterface()), &malformed_error)) { | 259 Type::Handle(Type::NumberInterface()), &malformed_error)) { |
| 224 GrowableArray<intptr_t> args; | 260 GenerateNumberTypeCheck( |
| 225 if (type.IsNumberInterface()) { | 261 kClassIdReg, type, is_instance_lbl, is_not_instance_lbl); |
| 226 args.Add(kDouble); | |
| 227 args.Add(kMint); | |
| 228 args.Add(kBigint); | |
| 229 } else if (type.IsIntInterface()) { | |
| 230 args.Add(kMint); | |
| 231 args.Add(kBigint); | |
| 232 } else if (type.IsDoubleInterface()) { | |
| 233 args.Add(kDouble); | |
| 234 } | |
| 235 CheckClassIds(args, is_instance_lbl, is_not_instance_lbl); | |
| 236 return; | 262 return; |
| 237 } | 263 } |
| 238 if (type.IsStringInterface()) { | 264 if (type.IsStringInterface()) { |
| 239 GrowableArray<intptr_t> args; | 265 GenerateStringTypeCheck(kClassIdReg, is_instance_lbl, is_not_instance_lbl); |
| 240 args.Add(kOneByteString); | |
| 241 args.Add(kTwoByteString); | |
| 242 args.Add(kFourByteString); | |
| 243 args.Add(kExternalOneByteString); | |
| 244 args.Add(kExternalTwoByteString); | |
| 245 args.Add(kExternalFourByteString); | |
| 246 CheckClassIds(args, is_instance_lbl, is_not_instance_lbl); | |
| 247 return; | 266 return; |
| 248 } | 267 } |
| 249 // Otherwise fallthrough. | 268 // Otherwise fallthrough. |
| 250 } | 269 } |
| 251 | 270 |
| 252 | 271 |
| 253 // Uses SubtypeTestCache to store instance class and result. | 272 // Uses SubtypeTestCache to store instance class and result. |
| 254 // RAX: instance to test. | 273 // RAX: instance to test. |
| 255 // Immediate class test already done. | 274 // Immediate class test already done. |
| 256 RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup( | 275 RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup( |
| 257 intptr_t cid, | 276 intptr_t cid, |
| 258 intptr_t token_index, | 277 intptr_t token_index, |
| 259 const Class& type_class, | 278 const Class& type_class, |
| 260 Label* is_instance_lbl, | 279 Label* is_instance_lbl, |
| 261 Label* is_not_instance_lbl) { | 280 Label* is_not_instance_lbl) { |
| 262 const SubtypeTestCache& type_test_cache = | 281 const Register kInstanceReg = RAX; |
| 263 SubtypeTestCache::ZoneHandle(SubtypeTestCache::New()); | 282 __ LoadClass(R10, kInstanceReg); |
| 264 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | |
| 265 const Immediate raw_null = | |
| 266 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 267 __ LoadClass(R10, RAX); | |
| 268 // Check immediate superclass equality. | 283 // Check immediate superclass equality. |
| 269 __ movq(R13, FieldAddress(R10, Class::super_type_offset())); | 284 __ movq(R13, FieldAddress(R10, Class::super_type_offset())); |
| 270 __ movq(R13, FieldAddress(R13, Type::type_class_offset())); | 285 __ movq(R13, FieldAddress(R13, Type::type_class_offset())); |
| 271 __ CompareObject(R13, type_class); | 286 __ CompareObject(R13, type_class); |
| 272 __ j(EQUAL, is_instance_lbl); | 287 __ j(EQUAL, is_instance_lbl); |
| 273 | 288 |
| 274 __ LoadObject(R10, type_test_cache); | 289 const Register kTypeArgumentsReg = kNoRegister; |
| 275 __ pushq(R10); // Cache array. | 290 const Register kTempReg = R10; |
| 276 __ pushq(RAX); // Instance. | 291 return GenerateCallSubtypeTestStub(kTestTypeOneArg, |
| 277 __ pushq(raw_null); // Unused | 292 kInstanceReg, |
| 278 __ call(&StubCode::Subtype1TestCacheLabel()); | 293 kTypeArgumentsReg, |
| 279 __ popq(RAX); // Discard. | 294 kTempReg, |
| 280 __ popq(RAX); // Restore receiver. | 295 is_instance_lbl, |
| 281 __ popq(RDX); // Discard. | 296 is_not_instance_lbl); |
| 282 // Result is in RCX: null -> not found, otherwise Bool::True or Bool::False. | |
| 283 | |
| 284 Label runtime_call; | |
| 285 __ cmpq(RCX, raw_null); | |
| 286 __ j(EQUAL, &runtime_call, Assembler::kNearJump); | |
| 287 __ CompareObject(RCX, bool_true); | |
| 288 __ j(EQUAL, is_instance_lbl); | |
| 289 __ jmp(is_not_instance_lbl); | |
| 290 __ Bind(&runtime_call); | |
| 291 return type_test_cache.raw(); | |
| 292 } | 297 } |
| 293 | 298 |
| 294 | 299 |
| 295 // Generates inlined check if 'type' is a type parameter or type itsef | 300 // Generates inlined check if 'type' is a type parameter or type itsef |
| 296 // RAX: instance (preserved). | 301 // RAX: instance (preserved). |
| 297 RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( | 302 RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( |
| 298 const AbstractType& type, | |
| 299 intptr_t cid, | 303 intptr_t cid, |
| 300 intptr_t token_index, | 304 intptr_t token_index, |
| 305 const AbstractType& type, |
| 301 Label* is_instance_lbl, | 306 Label* is_instance_lbl, |
| 302 Label* is_not_instance_lbl) { | 307 Label* is_not_instance_lbl) { |
| 303 ASSERT(!type.IsInstantiated()); | 308 ASSERT(!type.IsInstantiated()); |
| 304 const Immediate raw_null = | 309 const Immediate raw_null = |
| 305 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 310 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 306 if (type.IsTypeParameter()) { | 311 if (type.IsTypeParameter()) { |
| 307 // Load instantiator (or null) and instantiator type arguments on stack. | 312 // Load instantiator (or null) and instantiator type arguments on stack. |
| 308 __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments. | 313 __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments. |
| 309 // RDX: instantiator type arguments. | 314 // RDX: instantiator type arguments. |
| 310 // Check if type argument is Dynamic. | 315 // Check if type argument is Dynamic. |
| 311 __ cmpq(RDX, raw_null); | 316 __ cmpq(RDX, raw_null); |
| 312 __ j(EQUAL, is_instance_lbl); | 317 __ j(EQUAL, is_instance_lbl); |
| 313 // Can handle only type arguments that are instances of TypeArguments. | 318 // Can handle only type arguments that are instances of TypeArguments. |
| 314 // (runtime checks canonicalize type arguments). | 319 // (runtime checks canonicalize type arguments). |
| 315 Label fall_through; | 320 Label fall_through; |
| 316 __ CompareClassId(RDX, kTypeArguments); | 321 __ CompareClassId(RDX, kTypeArguments); |
| 317 __ j(NOT_EQUAL, &fall_through); | 322 __ j(NOT_EQUAL, &fall_through); |
| 318 __ movq(RDI, | 323 __ movq(RDI, |
| 319 FieldAddress(RDX, TypeArguments::type_at_offset(type.Index()))); | 324 FieldAddress(RDX, TypeArguments::type_at_offset(type.Index()))); |
| 320 // RDI: Concrete type. | 325 // RDI: Concrete type. |
| 321 // Check if it is Dynamic, | 326 // Check if it is Dynamic, |
| 322 __ CompareObject(RDI, Type::ZoneHandle(Type::DynamicType())); | 327 __ CompareObject(RDI, Type::ZoneHandle(Type::DynamicType())); |
| 323 __ j(EQUAL, is_instance_lbl); | 328 __ j(EQUAL, is_instance_lbl); |
| 324 __ cmpq(RDI, raw_null); | 329 __ cmpq(RDI, raw_null); |
| 325 __ j(EQUAL, is_instance_lbl); | 330 __ j(EQUAL, is_instance_lbl); |
| 331 const Type& object_type = |
| 332 Type::ZoneHandle(Isolate::Current()->object_store()->object_type()); |
| 333 __ CompareObject(RDI, object_type); |
| 334 __ j(EQUAL, is_instance_lbl); |
| 335 |
| 326 // For Smi check quickly against int and num interface types. | 336 // For Smi check quickly against int and num interface types. |
| 327 Label not_smi; | 337 Label not_smi; |
| 328 __ testq(RAX, Immediate(kSmiTagMask)); // Value is Smi? | 338 __ testq(RAX, Immediate(kSmiTagMask)); // Value is Smi? |
| 329 __ j(NOT_ZERO, ¬_smi, Assembler::kNearJump); | 339 __ j(NOT_ZERO, ¬_smi, Assembler::kNearJump); |
| 330 __ CompareObject(RDI, Type::ZoneHandle(Type::IntInterface())); | 340 __ CompareObject(RDI, Type::ZoneHandle(Type::IntInterface())); |
| 331 __ j(EQUAL, is_instance_lbl); | 341 __ j(EQUAL, is_instance_lbl); |
| 332 __ CompareObject(RDI, Type::ZoneHandle(Type::NumberInterface())); | 342 __ CompareObject(RDI, Type::ZoneHandle(Type::NumberInterface())); |
| 333 __ j(EQUAL, is_instance_lbl); | 343 __ j(EQUAL, is_instance_lbl); |
| 344 // Smi must be handled in runtime. |
| 334 __ jmp(&fall_through); | 345 __ jmp(&fall_through); |
| 346 |
| 335 __ Bind(¬_smi); | 347 __ Bind(¬_smi); |
| 336 // RDX: instantiator type arguments. | 348 // RDX: instantiator type arguments. |
| 337 // RAX: instance. | 349 // RAX: instance. |
| 350 const Register kInstanceReg = RAX; |
| 351 const Register kTypeArgumentsReg = RDX; |
| 352 const Register kTempReg = R10; |
| 338 const SubtypeTestCache& type_test_cache = | 353 const SubtypeTestCache& type_test_cache = |
| 339 SubtypeTestCache::ZoneHandle(SubtypeTestCache::New()); | 354 SubtypeTestCache::ZoneHandle( |
| 340 __ LoadObject(R10, type_test_cache); | 355 GenerateCallSubtypeTestStub(kTestTypeThreeArgs, |
| 341 __ pushq(R10); // Subtype test cache. | 356 kInstanceReg, |
| 342 __ pushq(RAX); // Instance | 357 kTypeArgumentsReg, |
| 343 __ pushq(RDX); // Instantiator type arguments. | 358 kTempReg, |
| 344 __ call(&StubCode::Subtype3TestCacheLabel()); | 359 is_instance_lbl, |
| 345 __ popq(RDX); // Discard type arguments. | 360 is_not_instance_lbl)); |
| 346 __ popq(RAX); // Restore receiver. | 361 |
| 347 __ popq(RDX); // Discard subtype test cache. | |
| 348 // Result is in RCX: null -> not found, otherwise Bool::True or Bool::False. | |
| 349 __ cmpq(RCX, raw_null); | |
| 350 __ j(EQUAL, &fall_through, Assembler::kNearJump); | |
| 351 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | |
| 352 __ CompareObject(RCX, bool_true); | |
| 353 __ j(EQUAL, is_instance_lbl); | |
| 354 __ jmp(is_not_instance_lbl); | |
| 355 __ Bind(&fall_through); | 362 __ Bind(&fall_through); |
| 356 return type_test_cache.raw(); | 363 return type_test_cache.raw(); |
| 357 } | 364 } |
| 358 if (type.IsType()) { | 365 if (type.IsType()) { |
| 359 Label fall_through; | 366 const Register kInstanceReg = RAX; |
| 360 __ testq(RAX, Immediate(kSmiTagMask)); // Is instance Smi? | 367 const Register kTypeArgumentsReg = RDX; |
| 368 __ testq(kInstanceReg, Immediate(kSmiTagMask)); // Is instance Smi? |
| 361 __ j(ZERO, is_not_instance_lbl); | 369 __ j(ZERO, is_not_instance_lbl); |
| 362 __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments. | 370 __ movq(kTypeArgumentsReg, Address(RSP, 0)); // Instantiator type args. |
| 363 // Uninstantiated type class is known at compile time, but the type | 371 // Uninstantiated type class is known at compile time, but the type |
| 364 // arguments are determined at runtime by the instantiator. | 372 // arguments are determined at runtime by the instantiator. |
| 365 const SubtypeTestCache& type_test_cache = | 373 const Register kTempReg = R10; |
| 366 SubtypeTestCache::ZoneHandle(SubtypeTestCache::New()); | 374 return GenerateCallSubtypeTestStub(kTestTypeThreeArgs, |
| 367 __ LoadObject(R10, type_test_cache); | 375 kInstanceReg, |
| 368 __ pushq(R10); // Subtype test cache. | 376 kTypeArgumentsReg, |
| 369 __ pushq(RAX); // Instance. | 377 kTempReg, |
| 370 __ pushq(RDX); // Instantiator type arguments. | 378 is_instance_lbl, |
| 371 __ call(&StubCode::Subtype3TestCacheLabel()); | 379 is_not_instance_lbl); |
| 372 __ popq(RDX); // Discard type arguments. | |
| 373 __ popq(RAX); // Restore receiver. | |
| 374 __ popq(RDX); // Discard subtype test cache. | |
| 375 // Result is in RCX: null -> not found, otherwise Bool::True or Bool::False. | |
| 376 __ cmpq(RCX, raw_null); | |
| 377 __ j(EQUAL, &fall_through, Assembler::kNearJump); | |
| 378 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | |
| 379 __ CompareObject(RCX, bool_true); | |
| 380 __ j(EQUAL, is_instance_lbl); | |
| 381 __ jmp(is_not_instance_lbl); | |
| 382 __ Bind(&fall_through); | |
| 383 return type_test_cache.raw(); | |
| 384 } | 380 } |
| 385 return SubtypeTestCache::null(); | 381 return SubtypeTestCache::null(); |
| 386 } | 382 } |
| 387 | 383 |
| 388 | 384 |
| 389 // Inputs: | 385 // Inputs: |
| 390 // - RAX: instance to test against (preserved). | 386 // - RAX: instance to test against (preserved). |
| 391 // - RDX: optional instantiator type arguments (preserved). | 387 // - RDX: optional instantiator type arguments (preserved). |
| 392 // Destroys RCX. | 388 // Destroys RCX. |
| 393 // Returns: | 389 // Returns: |
| (...skipping 25 matching lines...) Expand all Loading... |
| 419 type, | 415 type, |
| 420 is_instance_lbl, | 416 is_instance_lbl, |
| 421 is_not_instance_lbl); | 417 is_not_instance_lbl); |
| 422 // If test non-conclusive so far, try the inlined type-test cache. | 418 // If test non-conclusive so far, try the inlined type-test cache. |
| 423 // 'type' is known at compile time. | 419 // 'type' is known at compile time. |
| 424 return GenerateSubtype1TestCacheLookup( | 420 return GenerateSubtype1TestCacheLookup( |
| 425 cid, token_index, type_class, | 421 cid, token_index, type_class, |
| 426 is_instance_lbl, is_not_instance_lbl); | 422 is_instance_lbl, is_not_instance_lbl); |
| 427 } | 423 } |
| 428 } else { | 424 } else { |
| 429 return GenerateUninstantiatedTypeTest(type, | 425 return GenerateUninstantiatedTypeTest(cid, |
| 430 cid, | |
| 431 token_index, | 426 token_index, |
| 427 type, |
| 432 is_instance_lbl, | 428 is_instance_lbl, |
| 433 is_not_instance_lbl); | 429 is_not_instance_lbl); |
| 434 } | 430 } |
| 435 return SubtypeTestCache::null(); | 431 return SubtypeTestCache::null(); |
| 436 } | 432 } |
| 437 | 433 |
| 438 | 434 |
| 439 // Optimize assignable type check by adding inlined tests for: | 435 // Optimize assignable type check by adding inlined tests for: |
| 440 // - NULL -> return NULL. | 436 // - NULL -> return NULL. |
| 441 // - Smi -> compile time subtype check (only if dst class is not parameterized). | 437 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
| (...skipping 591 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1033 __ CallRuntime(entry); | 1029 __ CallRuntime(entry); |
| 1034 AddCurrentDescriptor(PcDescriptors::kOther, cid, token_index, try_index); | 1030 AddCurrentDescriptor(PcDescriptors::kOther, cid, token_index, try_index); |
| 1035 } | 1031 } |
| 1036 | 1032 |
| 1037 | 1033 |
| 1038 #undef __ | 1034 #undef __ |
| 1039 | 1035 |
| 1040 } // namespace dart | 1036 } // namespace dart |
| 1041 | 1037 |
| 1042 #endif // defined TARGET_ARCH_X64 | 1038 #endif // defined TARGET_ARCH_X64 |
| OLD | NEW |