| 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 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 93 const Class& cls = Class::ZoneHandle(Library::Handle( | 93 const Class& cls = Class::ZoneHandle(Library::Handle( |
| 94 Library::CoreImplLibrary()).LookupClass(class_name)); | 94 Library::CoreImplLibrary()).LookupClass(class_name)); |
| 95 ASSERT(!cls.IsNull()); | 95 ASSERT(!cls.IsNull()); |
| 96 return &cls; | 96 return &cls; |
| 97 } | 97 } |
| 98 | 98 |
| 99 | 99 |
| 100 #define __ assembler_-> | 100 #define __ assembler_-> |
| 101 | 101 |
| 102 | 102 |
| 103 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if |
| 104 // type test is conclusive, otherwise fallthrough if a type test could not |
| 105 // be completed. |
| 106 // RAX: instance (must survive), |
| 107 RawSubtypeTestCache* |
| 108 FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest( |
| 109 intptr_t cid, |
| 110 intptr_t token_index, |
| 111 const AbstractType& type, |
| 112 Label* is_instance_lbl, |
| 113 Label* is_not_instance_lbl) { |
| 114 ASSERT(type.IsInstantiated()); |
| 115 const Class& type_class = Class::ZoneHandle(type.type_class()); |
| 116 ASSERT(type_class.HasTypeArguments()); |
| 117 // A Smi object cannot be the instance of a parameterized class. |
| 118 __ testq(RAX, Immediate(kSmiTagMask)); |
| 119 __ j(ZERO, is_not_instance_lbl); |
| 120 const AbstractTypeArguments& type_arguments = |
| 121 AbstractTypeArguments::ZoneHandle(type.arguments()); |
| 122 const bool is_raw_type = type_arguments.IsNull() || |
| 123 type_arguments.IsRaw(type_arguments.Length()); |
| 124 if (is_raw_type) { |
| 125 // Dynamic type argument, check only classes. |
| 126 // List is a very common case. |
| 127 __ movq(R10, FieldAddress(RAX, Object::class_offset())); |
| 128 if (!type_class.is_interface()) { |
| 129 __ CompareObject(R10, type_class); |
| 130 __ j(EQUAL, is_instance_lbl); |
| 131 } |
| 132 if (type.IsListInterface()) { |
| 133 Label unknown; |
| 134 GrowableArray<const Class*> args; |
| 135 args.Add(CoreClass("ObjectArray")); |
| 136 args.Add(CoreClass("GrowableObjectArray")); |
| 137 args.Add(CoreClass("ImmutableArray")); |
| 138 CheckClasses(args, is_instance_lbl, &unknown); |
| 139 __ Bind(&unknown); |
| 140 } |
| 141 return GenerateSubtype1TestCacheLookup( |
| 142 cid, token_index, type_class, is_instance_lbl, is_not_instance_lbl); |
| 143 } |
| 144 return SubtypeTestCache::null(); |
| 145 } |
| 146 |
| 147 |
| 148 // R10: instance class to check. |
| 149 void FlowGraphCompiler::CheckClasses(const GrowableArray<const Class*>& classes, |
| 150 Label* is_instance_lbl, |
| 151 Label* is_not_instance_lbl) { |
| 152 for (intptr_t i = 0; i < classes.length(); i++) { |
| 153 __ CompareObject(R10, *classes[i]); |
| 154 __ j(EQUAL, is_instance_lbl); |
| 155 } |
| 156 __ jmp(is_not_instance_lbl); |
| 157 } |
| 158 |
| 159 |
| 160 |
| 161 // Testing against an instantiated type with no arguments, without |
| 162 // SubtypeTestCache. |
| 163 // RAX: instance to test against (preserved). |
| 164 void FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest( |
| 165 intptr_t cid, |
| 166 intptr_t token_index, |
| 167 const AbstractType& type, |
| 168 Label* is_instance_lbl, |
| 169 Label* is_not_instance_lbl) { |
| 170 ASSERT(type.IsInstantiated()); |
| 171 const Class& type_class = Class::ZoneHandle(type.type_class()); |
| 172 ASSERT(!type_class.HasTypeArguments()); |
| 173 |
| 174 Label compare_classes; |
| 175 __ testq(RAX, Immediate(kSmiTagMask)); |
| 176 __ j(NOT_ZERO, &compare_classes); |
| 177 // Instance is Smi, check directly. |
| 178 const Class& smi_class = Class::Handle(Smi::Class()); |
| 179 // TODO(regis): We should introduce a SmiType. |
| 180 Error& malformed_error = Error::Handle(); |
| 181 if (smi_class.IsSubtypeOf(TypeArguments::Handle(), |
| 182 type_class, |
| 183 TypeArguments::Handle(), |
| 184 &malformed_error)) { |
| 185 __ jmp(is_instance_lbl); |
| 186 } else { |
| 187 __ jmp(is_not_instance_lbl); |
| 188 } |
| 189 |
| 190 ObjectStore* object_store = Isolate::Current()->object_store(); |
| 191 // Compare if the classes are equal. Instance is not Smi. |
| 192 __ Bind(&compare_classes); |
| 193 __ movq(R10, FieldAddress(RAX, Object::class_offset())); |
| 194 // If type is an interface, we can skip the class equality check. |
| 195 if (!type_class.is_interface()) { |
| 196 __ CompareObject(R10, type_class); |
| 197 __ j(EQUAL, is_instance_lbl); |
| 198 } |
| 199 // Check for interfaces that cannot be implemented by user. |
| 200 // (see ClassFinalizer::ResolveInterfaces for list of restricted interfaces). |
| 201 // Bool interface can be implemented only by core class Bool. |
| 202 if (type.IsBoolInterface()) { |
| 203 const Class& bool_class = Class::ZoneHandle(object_store->bool_class()); |
| 204 __ CompareObject(R10, bool_class); |
| 205 __ j(EQUAL, is_instance_lbl); |
| 206 __ jmp(is_not_instance_lbl); |
| 207 return; |
| 208 } |
| 209 if (type.IsFunctionInterface()) { |
| 210 // Check if instance is a closure. |
| 211 const Immediate raw_null = |
| 212 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 213 __ movq(R10, FieldAddress(R10, Class::signature_function_offset())); |
| 214 __ cmpq(R10, raw_null); |
| 215 __ j(NOT_EQUAL, is_instance_lbl); |
| 216 __ jmp(is_not_instance_lbl); |
| 217 return; |
| 218 } |
| 219 // Custom checking for numbers (Smi, Mint, Bigint and Double). |
| 220 // Note that instance is not Smi(checked above). |
| 221 if (type.IsSubtypeOf( |
| 222 Type::Handle(Type::NumberInterface()), &malformed_error)) { |
| 223 const Class& mint_class = Class::ZoneHandle(object_store->mint_class()); |
| 224 const Class& bigint_class = Class::ZoneHandle(object_store->bigint_class()); |
| 225 const Class& double_class = Class::ZoneHandle(object_store->double_class()); |
| 226 GrowableArray<const Class*> args; |
| 227 if (type.IsNumberInterface()) { |
| 228 args.Add(&double_class); |
| 229 args.Add(&mint_class); |
| 230 args.Add(&bigint_class); |
| 231 } else if (type.IsIntInterface()) { |
| 232 args.Add(&mint_class); |
| 233 args.Add(&bigint_class); |
| 234 } else if (type.IsDoubleInterface()) { |
| 235 args.Add(&double_class); |
| 236 } |
| 237 CheckClasses(args, is_instance_lbl, is_not_instance_lbl); |
| 238 return; |
| 239 } |
| 240 if (type.IsStringInterface()) { |
| 241 const Class& one_byte_string_class = |
| 242 Class::ZoneHandle(object_store->one_byte_string_class()); |
| 243 const Class& two_byte_string_class = |
| 244 Class::ZoneHandle(object_store->two_byte_string_class()); |
| 245 const Class& four_byte_string_class = |
| 246 Class::ZoneHandle(object_store->four_byte_string_class()); |
| 247 const Class& external_one_byte_string_class = |
| 248 Class::ZoneHandle(object_store->external_one_byte_string_class()); |
| 249 const Class& external_two_byte_string_class = |
| 250 Class::ZoneHandle(object_store->external_two_byte_string_class()); |
| 251 const Class& external_four_byte_string_class = |
| 252 Class::ZoneHandle(object_store->external_four_byte_string_class()); |
| 253 GrowableArray<const Class*> args; |
| 254 args.Add(&one_byte_string_class); |
| 255 args.Add(&two_byte_string_class); |
| 256 args.Add(&four_byte_string_class); |
| 257 args.Add(&external_one_byte_string_class); |
| 258 args.Add(&external_two_byte_string_class); |
| 259 args.Add(&external_four_byte_string_class); |
| 260 CheckClasses(args, is_instance_lbl, is_not_instance_lbl); |
| 261 return; |
| 262 } |
| 263 // Otherwise fallthrough. |
| 264 } |
| 265 |
| 266 |
| 267 // Uses SubtypeTestCache to store instance class and result. |
| 268 // RAX: instance to test. |
| 269 // Immediate class test already done. |
| 270 RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup( |
| 271 intptr_t cid, |
| 272 intptr_t token_index, |
| 273 const Class& type_class, |
| 274 Label* is_instance_lbl, |
| 275 Label* is_not_instance_lbl) { |
| 276 const SubtypeTestCache& type_test_cache = |
| 277 SubtypeTestCache::ZoneHandle(SubtypeTestCache::New()); |
| 278 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); |
| 279 const Immediate raw_null = |
| 280 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 281 __ movq(R10, FieldAddress(RAX, Object::class_offset())); |
| 282 // Check immediate superclass equality. |
| 283 __ movq(R13, FieldAddress(R10, Class::super_type_offset())); |
| 284 __ movq(R13, FieldAddress(R13, Type::type_class_offset())); |
| 285 __ CompareObject(R13, type_class); |
| 286 __ j(EQUAL, is_instance_lbl); |
| 287 |
| 288 __ LoadObject(R10, type_test_cache); |
| 289 __ pushq(R10); // Cache array. |
| 290 __ pushq(RAX); // Instance. |
| 291 __ pushq(raw_null); // Unused |
| 292 __ call(&StubCode::Subtype1TestCacheLabel()); |
| 293 __ popq(RAX); // Discard. |
| 294 __ popq(RAX); // Restore receiver. |
| 295 __ popq(RDX); // Discard. |
| 296 // Result is in RCX: null -> not found, otherwise Bool::True or Bool::False. |
| 297 |
| 298 Label runtime_call; |
| 299 __ cmpq(RCX, raw_null); |
| 300 __ j(EQUAL, &runtime_call, Assembler::kNearJump); |
| 301 __ CompareObject(RCX, bool_true); |
| 302 __ j(EQUAL, is_instance_lbl); |
| 303 __ jmp(is_not_instance_lbl); |
| 304 __ Bind(&runtime_call); |
| 305 return type_test_cache.raw(); |
| 306 } |
| 307 |
| 308 |
| 309 RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( |
| 310 const AbstractType& type, |
| 311 intptr_t cid, |
| 312 intptr_t token_index, |
| 313 Label* is_instance_lbl, |
| 314 Label* is_not_instance_lbl) { |
| 315 return SubtypeTestCache::null(); |
| 316 } |
| 317 |
| 318 |
| 103 // Inputs: | 319 // Inputs: |
| 104 // - RAX: object (preserved). | 320 // - RAX: instance to test against (preserved). |
| 105 // - RDX: optional instantiator type arguments (preserved). | 321 // - RDX: optional instantiator type arguments (preserved). |
| 106 // Destroys RCX. | 322 // Destroys RCX. |
| 107 // Returns: | 323 // Returns: |
| 108 // - unchanged object in RAX and optional instantiator type arguments in RDX. | 324 // - unchanged object in RAX and optional instantiator type arguments in RDX. |
| 109 // Note that this inlined code must be followed by the runtime_call code, as it | 325 // Note that this inlined code must be followed by the runtime_call code, as it |
| 110 // may fall through to it. Otherwise, this inline code will jump to the label | 326 // may fall through to it. Otherwise, this inline code will jump to the label |
| 111 // is_instance or to the label is_not_instance. | 327 // is_instance or to the label is_not_instance. |
| 112 RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( | 328 RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( |
| 113 intptr_t cid, | 329 intptr_t cid, |
| 114 intptr_t token_index, | 330 intptr_t token_index, |
| 115 const AbstractType& type, | 331 const AbstractType& type, |
| 116 Label* is_instance, | 332 Label* is_instance_lbl, |
| 117 Label* is_not_instance) { | 333 Label* is_not_instance_lbl) { |
| 118 Label runtime_call; | |
| 119 if (type.IsInstantiated()) { | 334 if (type.IsInstantiated()) { |
| 120 const Class& type_class = Class::ZoneHandle(type.type_class()); | 335 const Class& type_class = Class::ZoneHandle(type.type_class()); |
| 121 const bool requires_type_arguments = type_class.HasTypeArguments(); | |
| 122 // A Smi object cannot be the instance of a parameterized class. | 336 // A Smi object cannot be the instance of a parameterized class. |
| 123 // A class equality check is only applicable with a dst type of a | 337 // A class equality check is only applicable with a dst type of a |
| 124 // non-parameterized class or with a raw dst type of a parameterized class. | 338 // non-parameterized class or with a raw dst type of a parameterized class. |
| 125 if (requires_type_arguments) { | 339 if (type_class.HasTypeArguments()) { |
| 126 const AbstractTypeArguments& type_arguments = | 340 return GenerateInstantiatedTypeWithArgumentsTest(cid, |
| 127 AbstractTypeArguments::Handle(type.arguments()); | 341 token_index, |
| 128 const bool is_raw_type = type_arguments.IsNull() || | 342 type, |
| 129 type_arguments.IsRaw(type_arguments.Length()); | 343 is_instance_lbl, |
| 130 __ testq(RAX, Immediate(kSmiTagMask)); | 344 is_not_instance_lbl); |
| 131 __ j(ZERO, &runtime_call); | 345 // Fall through to runtime call. |
| 132 // Object not Smi. | 346 } else { |
| 133 if (is_raw_type) { | 347 GenerateInstantiatedTypeNoArgumentsTest(cid, |
| 134 // Dynamic type argument, check only classes. | 348 token_index, |
| 135 if (type.IsListInterface()) { | 349 type, |
| 136 // TODO(srdjan) also accept List<Object>. | 350 is_instance_lbl, |
| 137 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); | 351 is_not_instance_lbl); |
| 138 __ CompareObject(RCX, *CoreClass("ObjectArray")); | 352 // If test non-conclusive so far, try the inlined type-test cache. |
| 139 __ j(EQUAL, is_instance); | 353 // 'type' is known at compile time. |
| 140 __ CompareObject(RCX, *CoreClass("GrowableObjectArray")); | 354 return GenerateSubtype1TestCacheLookup( |
| 141 __ j(EQUAL, is_instance); | 355 cid, token_index, type_class, |
| 142 } else if (!type_class.is_interface()) { | 356 is_instance_lbl, is_not_instance_lbl); |
| 143 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); | |
| 144 __ CompareObject(RCX, type_class); | |
| 145 __ j(EQUAL, is_instance); | |
| 146 } | |
| 147 // Fall through to runtime call. | |
| 148 } | |
| 149 } else { // type has NO type arguments. | |
| 150 Label compare_classes; | |
| 151 __ testq(RAX, Immediate(kSmiTagMask)); | |
| 152 __ j(NOT_ZERO, &compare_classes); | |
| 153 // Object is Smi. | |
| 154 const Class& smi_class = Class::Handle(Smi::Class()); | |
| 155 // TODO(regis): We should introduce a SmiType. | |
| 156 Error& malformed_error = Error::Handle(); | |
| 157 if (smi_class.IsSubtypeOf(TypeArguments::Handle(), | |
| 158 type_class, | |
| 159 TypeArguments::Handle(), | |
| 160 &malformed_error)) { | |
| 161 // Successful assignable type check: return object in RAX. | |
| 162 __ jmp(is_instance); | |
| 163 } else { | |
| 164 // Failed assignable type check: call runtime to throw TypeError. | |
| 165 __ jmp(&runtime_call); | |
| 166 } | |
| 167 // Compare if the classes are equal. | |
| 168 __ Bind(&compare_classes); | |
| 169 // If type is an interface, we can skip the class equality check, | |
| 170 // because instances cannot be of an interface type. | |
| 171 if (!type_class.is_interface()) { | |
| 172 __ LoadObject(RCX, type_class); | |
| 173 __ movq(R10, FieldAddress(RAX, Object::class_offset())); | |
| 174 __ cmpq(R10, RCX); | |
| 175 __ j(EQUAL, is_instance); | |
| 176 // TODO(srdjan): Finish implementation. | |
| 177 // Otherwise fall through to runtime call. | |
| 178 } else { | |
| 179 // However, for specific core library interfaces, we can check for | |
| 180 // specific core library classes. | |
| 181 Error& malformed_error = Error::Handle(); | |
| 182 if (type.IsBoolInterface()) { | |
| 183 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); | |
| 184 const Class& bool_class = Class::ZoneHandle( | |
| 185 Isolate::Current()->object_store()->bool_class()); | |
| 186 __ CompareObject(RCX, bool_class); | |
| 187 __ j(EQUAL, is_instance); | |
| 188 } else if (type.IsSubtypeOf( | |
| 189 Type::Handle(Type::NumberInterface()), &malformed_error)) { | |
| 190 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); | |
| 191 if (type.IsIntInterface() || type.IsNumberInterface()) { | |
| 192 // We already checked for Smi above. | |
| 193 const Class& mint_class = Class::ZoneHandle( | |
| 194 Isolate::Current()->object_store()->mint_class()); | |
| 195 __ CompareObject(RCX, mint_class); | |
| 196 __ j(EQUAL, is_instance); | |
| 197 const Class& bigint_class = Class::ZoneHandle( | |
| 198 Isolate::Current()->object_store()->bigint_class()); | |
| 199 __ CompareObject(RCX, bigint_class); | |
| 200 __ j(EQUAL, is_instance); | |
| 201 } | |
| 202 if (type.IsDoubleInterface() || type.IsNumberInterface()) { | |
| 203 const Class& double_class = Class::ZoneHandle( | |
| 204 Isolate::Current()->object_store()->double_class()); | |
| 205 __ CompareObject(RCX, double_class); | |
| 206 __ j(EQUAL, is_instance); | |
| 207 } | |
| 208 } else if (type.IsStringInterface()) { | |
| 209 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); | |
| 210 const Class& one_byte_string_class = Class::ZoneHandle( | |
| 211 Isolate::Current()->object_store()->one_byte_string_class()); | |
| 212 __ CompareObject(RCX, one_byte_string_class); | |
| 213 __ j(EQUAL, is_instance); | |
| 214 const Class& two_byte_string_class = Class::ZoneHandle( | |
| 215 Isolate::Current()->object_store()->two_byte_string_class()); | |
| 216 __ CompareObject(RCX, two_byte_string_class); | |
| 217 __ j(EQUAL, is_instance); | |
| 218 const Class& four_byte_string_class = Class::ZoneHandle( | |
| 219 Isolate::Current()->object_store()->four_byte_string_class()); | |
| 220 __ CompareObject(RCX, four_byte_string_class); | |
| 221 __ j(EQUAL, is_instance); | |
| 222 } else if (type.IsFunctionInterface()) { | |
| 223 const Immediate raw_null = | |
| 224 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 225 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); | |
| 226 __ movq(RCX, FieldAddress(RCX, Class::signature_function_offset())); | |
| 227 __ cmpq(RCX, raw_null); | |
| 228 __ j(NOT_EQUAL, is_instance); | |
| 229 } else { | |
| 230 // TODO(srdjan): Finish implementation. | |
| 231 } | |
| 232 } | |
| 233 } | 357 } |
| 234 } else { | 358 } else { |
| 235 ASSERT(!type.IsInstantiated()); | 359 return GenerateUninstantiatedTypeTest(type, |
| 236 // Skip check if destination is a dynamic type. | 360 cid, |
| 237 if (type.IsTypeParameter()) { | 361 token_index, |
| 238 // Check if dynamic. | 362 is_instance_lbl, |
| 239 const Immediate raw_null = | 363 is_not_instance_lbl); |
| 240 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 241 // Instantiator type arguments are in RDX. | |
| 242 __ cmpq(RDX, raw_null); | |
| 243 __ j(EQUAL, is_instance); | |
| 244 | |
| 245 // For now handle only TypeArguments and bail out if InstantiatedTypeArgs. | |
| 246 __ movq(RCX, FieldAddress(RDX, Object::class_offset())); | |
| 247 __ CompareObject(RCX, Object::ZoneHandle(Object::type_arguments_class())); | |
| 248 __ j(NOT_EQUAL, &runtime_call); | |
| 249 __ movq(RCX, | |
| 250 FieldAddress(RDX, TypeArguments::type_at_offset(type.Index()))); | |
| 251 // RCX: instantiated type parameter. | |
| 252 __ CompareObject(RCX, Type::ZoneHandle(Type::DynamicType())); | |
| 253 __ j(EQUAL, is_instance); | |
| 254 // Check if the type has type parameters, if not, do the class comparison. | |
| 255 Label not_smi; | |
| 256 __ testq(RAX, Immediate(kSmiTagMask)); // Value is Smi? | |
| 257 __ j(NOT_ZERO, ¬_smi, Assembler::kNearJump); | |
| 258 __ CompareObject(RCX, Type::ZoneHandle(Type::IntInterface())); | |
| 259 __ j(EQUAL, is_instance); | |
| 260 __ CompareObject(RCX, Type::ZoneHandle(Type::NumberInterface())); | |
| 261 __ j(EQUAL, is_instance); | |
| 262 __ Bind(¬_smi); | |
| 263 // The instantiated type parameter RCX may not be a Type, but could be an | |
| 264 // InstantiatedType. It is therefore necessary to check its class. | |
| 265 __ movq(R10, FieldAddress(RCX, Object::class_offset())); | |
| 266 __ CompareObject(R10, Object::ZoneHandle(Object::type_class())); | |
| 267 __ j(NOT_EQUAL, &runtime_call, Assembler::kNearJump); | |
| 268 __ movq(RCX, FieldAddress(RCX, Type::type_class_offset())); | |
| 269 __ movq(R10, FieldAddress(RCX, Class::type_parameters_offset())); | |
| 270 // Check that class of type has no type parameters. | |
| 271 __ cmpq(R10, raw_null); | |
| 272 __ j(NOT_EQUAL, &runtime_call, Assembler::kNearJump); | |
| 273 // TODO(srdjan): Implement subtype test cache. | |
| 274 // Fall through to runtime call. | |
| 275 } | |
| 276 } | 364 } |
| 277 __ Bind(&runtime_call); | |
| 278 return SubtypeTestCache::null(); | 365 return SubtypeTestCache::null(); |
| 279 } | 366 } |
| 280 | 367 |
| 281 | 368 |
| 282 // Optimize assignable type check by adding inlined tests for: | 369 // Optimize assignable type check by adding inlined tests for: |
| 283 // - NULL -> return NULL. | 370 // - NULL -> return NULL. |
| 284 // - Smi -> compile time subtype check (only if dst class is not parameterized). | 371 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
| 285 // - Class equality (only if class is not parameterized). | 372 // - Class equality (only if class is not parameterized). |
| 286 // Inputs: | 373 // Inputs: |
| 287 // - RAX: object. | 374 // - RAX: object. |
| (...skipping 10 matching lines...) Expand all Loading... |
| 298 const AbstractType& dst_type, | 385 const AbstractType& dst_type, |
| 299 const String& dst_name) { | 386 const String& dst_name) { |
| 300 ASSERT(FLAG_enable_type_checks); | 387 ASSERT(FLAG_enable_type_checks); |
| 301 ASSERT(token_index >= 0); | 388 ASSERT(token_index >= 0); |
| 302 ASSERT(!dst_type.IsNull()); | 389 ASSERT(!dst_type.IsNull()); |
| 303 ASSERT(dst_type.IsFinalized()); | 390 ASSERT(dst_type.IsFinalized()); |
| 304 // Assignable check is skipped in FlowGraphBuilder, not here. | 391 // Assignable check is skipped in FlowGraphBuilder, not here. |
| 305 ASSERT(dst_type.IsMalformed() || | 392 ASSERT(dst_type.IsMalformed() || |
| 306 (!dst_type.IsDynamicType() && !dst_type.IsObjectType())); | 393 (!dst_type.IsDynamicType() && !dst_type.IsObjectType())); |
| 307 ASSERT(!dst_type.IsVoidType()); | 394 ASSERT(!dst_type.IsVoidType()); |
| 308 __ pushq(RCX); // Temporary store instantiator on stack. | 395 __ pushq(RCX); // Store instantiator. |
| 396 __ pushq(RDX); // Store instantiator type arguments. |
| 309 // A null object is always assignable and is returned as result. | 397 // A null object is always assignable and is returned as result. |
| 310 const Immediate raw_null = | 398 const Immediate raw_null = |
| 311 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 399 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 312 Label is_assignable, runtime_call; | 400 Label is_assignable, runtime_call; |
| 313 __ cmpq(RAX, raw_null); | 401 __ cmpq(RAX, raw_null); |
| 314 __ j(EQUAL, &is_assignable); | 402 __ j(EQUAL, &is_assignable); |
| 315 | 403 |
| 316 // Generate throw new TypeError() if the type is malformed. | 404 // Generate throw new TypeError() if the type is malformed. |
| 317 if (dst_type.IsMalformed()) { | 405 if (dst_type.IsMalformed()) { |
| 318 const Error& error = Error::Handle(dst_type.malformed_error()); | 406 const Error& error = Error::Handle(dst_type.malformed_error()); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 332 | 420 |
| 333 __ Bind(&is_assignable); // For a null object. | 421 __ Bind(&is_assignable); // For a null object. |
| 334 return; | 422 return; |
| 335 } | 423 } |
| 336 | 424 |
| 337 // Generate inline type check, linking to runtime call if not assignable. | 425 // Generate inline type check, linking to runtime call if not assignable. |
| 338 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); | 426 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); |
| 339 test_cache = GenerateInlineInstanceof(cid, token_index, dst_type, | 427 test_cache = GenerateInlineInstanceof(cid, token_index, dst_type, |
| 340 &is_assignable, &runtime_call); | 428 &is_assignable, &runtime_call); |
| 341 __ Bind(&runtime_call); | 429 __ Bind(&runtime_call); |
| 342 __ movq(RCX, Address(RSP, 0)); // Get instantiator. | 430 __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments. |
| 431 __ movq(RCX, Address(RSP, kWordSize)); // Get instantiator. |
| 343 __ PushObject(Object::ZoneHandle()); // Make room for the result. | 432 __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| 344 __ pushq(Immediate(Smi::RawValue(token_index))); // Source location. | 433 __ pushq(Immediate(Smi::RawValue(token_index))); // Source location. |
| 345 __ pushq(Immediate(Smi::RawValue(cid))); // Computation id. | 434 __ pushq(Immediate(Smi::RawValue(cid))); // Computation id. |
| 346 __ pushq(RAX); // Push the source object. | 435 __ pushq(RAX); // Push the source object. |
| 347 __ PushObject(dst_type); // Push the type of the destination. | 436 __ PushObject(dst_type); // Push the type of the destination. |
| 348 __ pushq(RCX); // Instantiator. | 437 __ pushq(RCX); // Instantiator. |
| 349 __ pushq(RDX); // Instantiator type arguments. | 438 __ pushq(RDX); // Instantiator type arguments. |
| 350 __ PushObject(dst_name); // Push the name of the destination. | 439 __ PushObject(dst_name); // Push the name of the destination. |
| 351 __ LoadObject(RAX, test_cache); | 440 __ LoadObject(RAX, test_cache); |
| 352 __ pushq(RAX); | 441 __ pushq(RAX); |
| 353 GenerateCallRuntime(cid, | 442 GenerateCallRuntime(cid, |
| 354 token_index, | 443 token_index, |
| 355 try_index, | 444 try_index, |
| 356 kTypeCheckRuntimeEntry); | 445 kTypeCheckRuntimeEntry); |
| 357 // Pop the parameters supplied to the runtime entry. The result of the | 446 // Pop the parameters supplied to the runtime entry. The result of the |
| 358 // type check runtime call is the checked value. | 447 // type check runtime call is the checked value. |
| 359 __ Drop(8); | 448 __ Drop(8); |
| 360 __ popq(RAX); | 449 __ popq(RAX); |
| 361 | 450 |
| 362 __ Bind(&is_assignable); | 451 __ Bind(&is_assignable); |
| 452 __ popq(RDX); // Remove pushed instantiator type arguments.. |
| 363 __ popq(RCX); // Remove pushed instantiator. | 453 __ popq(RCX); // Remove pushed instantiator. |
| 364 } | 454 } |
| 365 | 455 |
| 366 | 456 |
| 367 void FlowGraphCompiler::LoadValue(Register dst, Value* value) { | 457 void FlowGraphCompiler::LoadValue(Register dst, Value* value) { |
| 368 if (value->IsConstant()) { | 458 if (value->IsConstant()) { |
| 369 ConstantVal* constant = value->AsConstant(); | 459 ConstantVal* constant = value->AsConstant(); |
| 370 if (constant->value().IsSmi()) { | 460 if (constant->value().IsSmi()) { |
| 371 int64_t imm = reinterpret_cast<int64_t>(constant->value().raw()); | 461 int64_t imm = reinterpret_cast<int64_t>(constant->value().raw()); |
| 372 __ movq(dst, Immediate(imm)); | 462 __ movq(dst, Immediate(imm)); |
| (...skipping 427 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 800 __ pushq(Immediate(Smi::RawValue(token_index))); // Source location. | 890 __ pushq(Immediate(Smi::RawValue(token_index))); // Source location. |
| 801 __ pushq(Immediate(Smi::RawValue(cid))); // Computation id. | 891 __ pushq(Immediate(Smi::RawValue(cid))); // Computation id. |
| 802 __ pushq(RAX); // Push the instance. | 892 __ pushq(RAX); // Push the instance. |
| 803 __ PushObject(type); // Push the type. | 893 __ PushObject(type); // Push the type. |
| 804 __ pushq(raw_null); // TODO(srdjan): Pass instantiator instead of null. | 894 __ pushq(raw_null); // TODO(srdjan): Pass instantiator instead of null. |
| 805 if (type.IsInstantiated()) { | 895 if (type.IsInstantiated()) { |
| 806 __ pushq(raw_null); // Null instantiator type arguments. | 896 __ pushq(raw_null); // Null instantiator type arguments. |
| 807 } else { | 897 } else { |
| 808 __ pushq(RDX); // Instantiator type arguments. | 898 __ pushq(RDX); // Instantiator type arguments. |
| 809 } | 899 } |
| 810 __ pushq(raw_null); // SubtypeTestCache not yet supported. | 900 __ LoadObject(RAX, test_cache); |
| 901 __ pushq(RAX); |
| 811 GenerateCallRuntime(cid, token_index, try_index, kInstanceofRuntimeEntry); | 902 GenerateCallRuntime(cid, token_index, try_index, kInstanceofRuntimeEntry); |
| 812 // Pop the two parameters supplied to the runtime entry. The result of the | 903 // Pop the two parameters supplied to the runtime entry. The result of the |
| 813 // instanceof runtime call will be left as the result of the operation. | 904 // instanceof runtime call will be left as the result of the operation. |
| 814 __ Drop(7); | 905 __ Drop(7); |
| 815 Label done; | 906 Label done; |
| 816 if (negate_result) { | 907 if (negate_result) { |
| 817 __ popq(RDX); | 908 __ popq(RDX); |
| 818 __ LoadObject(RAX, bool_true); | 909 __ LoadObject(RAX, bool_true); |
| 819 __ cmpq(RDX, RAX); | 910 __ cmpq(RDX, RAX); |
| 820 __ j(NOT_EQUAL, &done, Assembler::kNearJump); | 911 __ j(NOT_EQUAL, &done, Assembler::kNearJump); |
| (...skipping 934 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1755 | 1846 |
| 1756 | 1847 |
| 1757 void FlowGraphCompiler::FinalizeComments(const Code& code) { | 1848 void FlowGraphCompiler::FinalizeComments(const Code& code) { |
| 1758 code.set_comments(assembler_->GetCodeComments()); | 1849 code.set_comments(assembler_->GetCodeComments()); |
| 1759 } | 1850 } |
| 1760 | 1851 |
| 1761 | 1852 |
| 1762 } // namespace dart | 1853 } // namespace dart |
| 1763 | 1854 |
| 1764 #endif // defined TARGET_ARCH_X64 | 1855 #endif // defined TARGET_ARCH_X64 |
| OLD | NEW |