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 |