| 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" |
| 11 #include "vm/ast_printer.h" | 11 #include "vm/ast_printer.h" |
| 12 #include "vm/code_descriptors.h" | |
| 13 #include "vm/code_generator.h" | |
| 14 #include "vm/disassembler.h" | |
| 15 #include "vm/il_printer.h" | 12 #include "vm/il_printer.h" |
| 16 #include "vm/locations.h" | 13 #include "vm/locations.h" |
| 17 #include "vm/object_store.h" | 14 #include "vm/object_store.h" |
| 18 #include "vm/parser.h" | 15 #include "vm/parser.h" |
| 19 #include "vm/stub_code.h" | 16 #include "vm/stub_code.h" |
| 20 | 17 |
| 21 namespace dart { | 18 namespace dart { |
| 22 | 19 |
| 23 DEFINE_FLAG(bool, print_scopes, false, "Print scopes of local variables."); | |
| 24 DEFINE_FLAG(bool, trace_functions, false, "Trace entry of each function."); | |
| 25 DECLARE_FLAG(bool, enable_type_checks); | 20 DECLARE_FLAG(bool, enable_type_checks); |
| 26 DECLARE_FLAG(bool, print_ast); | 21 DECLARE_FLAG(bool, print_ast); |
| 22 DECLARE_FLAG(bool, print_scopes); |
| 23 DECLARE_FLAG(bool, trace_functions); |
| 27 | 24 |
| 28 | 25 |
| 29 void DeoptimizationStub::GenerateCode(FlowGraphCompiler* compiler) { | 26 void DeoptimizationStub::GenerateCode(FlowGraphCompiler* compiler) { |
| 30 Assembler* assem = compiler->assembler(); | 27 Assembler* assem = compiler->assembler(); |
| 31 #define __ assem-> | 28 #define __ assem-> |
| 32 __ Comment("Deopt stub for id %d", deopt_id_); | 29 __ Comment("Deopt stub for id %d", deopt_id_); |
| 33 __ Bind(entry_label()); | 30 __ Bind(entry_label()); |
| 34 for (intptr_t i = 0; i < registers_.length(); i++) { | 31 for (intptr_t i = 0; i < registers_.length(); i++) { |
| 35 if (registers_[i] != kNoRegister) { | 32 if (registers_[i] != kNoRegister) { |
| 36 __ pushq(registers_[i]); | 33 __ pushq(registers_[i]); |
| 37 } | 34 } |
| 38 } | 35 } |
| 39 __ movq(RAX, Immediate(Smi::RawValue(reason_))); | 36 __ movq(RAX, Immediate(Smi::RawValue(reason_))); |
| 40 __ call(&StubCode::DeoptimizeLabel()); | 37 __ call(&StubCode::DeoptimizeLabel()); |
| 41 compiler->AddCurrentDescriptor(PcDescriptors::kOther, | 38 compiler->AddCurrentDescriptor(PcDescriptors::kOther, |
| 42 deopt_id_, | 39 deopt_id_, |
| 43 deopt_token_pos_, | 40 deopt_token_pos_, |
| 44 try_index_); | 41 try_index_); |
| 45 #undef __ | 42 #undef __ |
| 46 } | 43 } |
| 47 | 44 |
| 48 | 45 |
| 49 #define __ assembler()-> | 46 #define __ assembler()-> |
| 50 | 47 |
| 51 | 48 |
| 52 | |
| 53 // Fall through if bool_register contains null. | 49 // Fall through if bool_register contains null. |
| 54 void FlowGraphCompiler::GenerateBoolToJump(Register bool_register, | 50 void FlowGraphCompiler::GenerateBoolToJump(Register bool_register, |
| 55 Label* is_true, | 51 Label* is_true, |
| 56 Label* is_false) { | 52 Label* is_false) { |
| 57 const Immediate raw_null = | 53 const Immediate raw_null = |
| 58 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 54 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 59 Label fall_through; | 55 Label fall_through; |
| 60 __ cmpq(bool_register, raw_null); | 56 __ cmpq(bool_register, raw_null); |
| 61 __ j(EQUAL, &fall_through, Assembler::kNearJump); | 57 __ j(EQUAL, &fall_through, Assembler::kNearJump); |
| 62 __ CompareObject(bool_register, bool_true()); | 58 __ CompareObject(bool_register, bool_true()); |
| 63 __ j(EQUAL, is_true); | 59 __ j(EQUAL, is_true); |
| 64 __ jmp(is_false); | 60 __ jmp(is_false); |
| 65 __ Bind(&fall_through); | 61 __ Bind(&fall_through); |
| 66 } | 62 } |
| 67 | 63 |
| 68 | 64 |
| 65 // Clobbers RCX. |
| 69 RawSubtypeTestCache* FlowGraphCompiler::GenerateCallSubtypeTestStub( | 66 RawSubtypeTestCache* FlowGraphCompiler::GenerateCallSubtypeTestStub( |
| 70 TypeTestStubKind test_kind, | 67 TypeTestStubKind test_kind, |
| 71 Register instance_reg, | 68 Register instance_reg, |
| 72 Register type_arguments_reg, | 69 Register type_arguments_reg, |
| 73 Register temp_reg, | 70 Register temp_reg, |
| 74 Label* is_instance_lbl, | 71 Label* is_instance_lbl, |
| 75 Label* is_not_instance_lbl) { | 72 Label* is_not_instance_lbl) { |
| 76 const SubtypeTestCache& type_test_cache = | 73 const SubtypeTestCache& type_test_cache = |
| 77 SubtypeTestCache::ZoneHandle(SubtypeTestCache::New()); | 74 SubtypeTestCache::ZoneHandle(SubtypeTestCache::New()); |
| 78 const Immediate raw_null = | 75 const Immediate raw_null = |
| 79 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 76 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 80 __ LoadObject(temp_reg, type_test_cache); | 77 __ LoadObject(temp_reg, type_test_cache); |
| 81 __ pushq(temp_reg); // Subtype test cache. | 78 __ pushq(temp_reg); // Subtype test cache. |
| 82 __ pushq(instance_reg); // Instance. | 79 __ pushq(instance_reg); // Instance. |
| 83 if (test_kind == kTestTypeOneArg) { | 80 if (test_kind == kTestTypeOneArg) { |
| 84 ASSERT(type_arguments_reg == kNoRegister); | 81 ASSERT(type_arguments_reg == kNoRegister); |
| 85 __ pushq(raw_null); | 82 __ pushq(raw_null); |
| 86 __ call(&StubCode::Subtype1TestCacheLabel()); | 83 __ call(&StubCode::Subtype1TestCacheLabel()); |
| 87 } else if (test_kind == kTestTypeTwoArgs) { | 84 } else if (test_kind == kTestTypeTwoArgs) { |
| 88 ASSERT(type_arguments_reg == kNoRegister); | 85 ASSERT(type_arguments_reg == kNoRegister); |
| 89 __ pushq(raw_null); | 86 __ pushq(raw_null); |
| 90 __ call(&StubCode::Subtype2TestCacheLabel()); | 87 __ call(&StubCode::Subtype2TestCacheLabel()); |
| 91 } else if (test_kind == kTestTypeThreeArgs) { | 88 } else if (test_kind == kTestTypeThreeArgs) { |
| 92 __ pushq(type_arguments_reg); | 89 __ pushq(type_arguments_reg); |
| 93 __ call(&StubCode::Subtype3TestCacheLabel()); | 90 __ call(&StubCode::Subtype3TestCacheLabel()); |
| 94 } else { | 91 } else { |
| 95 UNREACHABLE(); | 92 UNREACHABLE(); |
| 96 } | 93 } |
| 97 // Result is in ECX: null -> not found, otherwise Bool::True or Bool::False. | 94 // Result is in RCX: null -> not found, otherwise Bool::True or Bool::False. |
| 95 ASSERT(instance_reg != RCX); |
| 96 ASSERT(temp_reg != RCX); |
| 98 __ popq(instance_reg); // Discard. | 97 __ popq(instance_reg); // Discard. |
| 99 __ popq(instance_reg); // Restore receiver. | 98 __ popq(instance_reg); // Restore receiver. |
| 100 __ popq(temp_reg); // Discard. | 99 __ popq(temp_reg); // Discard. |
| 101 GenerateBoolToJump(RCX, is_instance_lbl, is_not_instance_lbl); | 100 GenerateBoolToJump(RCX, is_instance_lbl, is_not_instance_lbl); |
| 102 return type_test_cache.raw(); | 101 return type_test_cache.raw(); |
| 103 } | 102 } |
| 104 | 103 |
| 104 |
| 105 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if | 105 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if |
| 106 // type test is conclusive, otherwise fallthrough if a type test could not | 106 // type test is conclusive, otherwise fallthrough if a type test could not |
| 107 // be completed. | 107 // be completed. |
| 108 // RAX: instance (must survive), | 108 // RAX: instance (must survive). |
| 109 // Clobbers R10. |
| 109 RawSubtypeTestCache* | 110 RawSubtypeTestCache* |
| 110 FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest( | 111 FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest( |
| 111 intptr_t cid, | 112 intptr_t cid, |
| 112 intptr_t token_pos, | 113 intptr_t token_pos, |
| 113 const AbstractType& type, | 114 const AbstractType& type, |
| 114 Label* is_instance_lbl, | 115 Label* is_instance_lbl, |
| 115 Label* is_not_instance_lbl) { | 116 Label* is_not_instance_lbl) { |
| 116 ASSERT(type.IsInstantiated()); | 117 ASSERT(type.IsInstantiated()); |
| 117 const Class& type_class = Class::ZoneHandle(type.type_class()); | 118 const Class& type_class = Class::ZoneHandle(type.type_class()); |
| 118 ASSERT(type_class.HasTypeArguments()); | 119 ASSERT(type_class.HasTypeArguments()); |
| (...skipping 30 matching lines...) Expand all Loading... |
| 149 // Check if type argument is dynamic or Object. | 150 // Check if type argument is dynamic or Object. |
| 150 const Type& object_type = Type::Handle(Type::ObjectType()); | 151 const Type& object_type = Type::Handle(Type::ObjectType()); |
| 151 Error& malformed_error = Error::Handle(); | 152 Error& malformed_error = Error::Handle(); |
| 152 if (object_type.IsSubtypeOf(tp_argument, &malformed_error)) { | 153 if (object_type.IsSubtypeOf(tp_argument, &malformed_error)) { |
| 153 // Instance class test only necessary. | 154 // Instance class test only necessary. |
| 154 return GenerateSubtype1TestCacheLookup( | 155 return GenerateSubtype1TestCacheLookup( |
| 155 cid, token_pos, type_class, is_instance_lbl, is_not_instance_lbl); | 156 cid, token_pos, type_class, is_instance_lbl, is_not_instance_lbl); |
| 156 } | 157 } |
| 157 } | 158 } |
| 158 } | 159 } |
| 159 | |
| 160 // Regular subtype test cache involving instance's type arguments. | 160 // Regular subtype test cache involving instance's type arguments. |
| 161 const Register kTypeArgumentsReg = kNoRegister; | 161 const Register kTypeArgumentsReg = kNoRegister; |
| 162 const Register kTempReg = R10; | 162 const Register kTempReg = R10; |
| 163 return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, | 163 return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, |
| 164 kInstanceReg, | 164 kInstanceReg, |
| 165 kTypeArgumentsReg, | 165 kTypeArgumentsReg, |
| 166 kTempReg, | 166 kTempReg, |
| 167 is_instance_lbl, | 167 is_instance_lbl, |
| 168 is_not_instance_lbl); | 168 is_not_instance_lbl); |
| 169 } | 169 } |
| 170 | 170 |
| 171 | 171 |
| 172 void FlowGraphCompiler::CheckClassIds(Register class_id_reg, | 172 void FlowGraphCompiler::CheckClassIds(Register class_id_reg, |
| 173 const GrowableArray<intptr_t>& class_ids, | 173 const GrowableArray<intptr_t>& class_ids, |
| 174 Label* is_equal_lbl, | 174 Label* is_equal_lbl, |
| 175 Label* is_not_equal_lbl) { | 175 Label* is_not_equal_lbl) { |
| 176 for (intptr_t i = 0; i < class_ids.length(); i++) { | 176 for (intptr_t i = 0; i < class_ids.length(); i++) { |
| 177 __ cmpl(class_id_reg, Immediate(class_ids[i])); | 177 __ cmpl(class_id_reg, Immediate(class_ids[i])); |
| 178 __ j(EQUAL, is_equal_lbl); | 178 __ j(EQUAL, is_equal_lbl); |
| 179 } | 179 } |
| 180 __ jmp(is_not_equal_lbl); | 180 __ jmp(is_not_equal_lbl); |
| 181 } | 181 } |
| 182 | 182 |
| 183 | 183 |
| 184 | |
| 185 // Testing against an instantiated type with no arguments, without | 184 // Testing against an instantiated type with no arguments, without |
| 186 // SubtypeTestCache. | 185 // SubtypeTestCache. |
| 187 // RAX: instance to test against (preserved). | 186 // RAX: instance to test against (preserved). |
| 187 // Clobbers R10, R13. |
| 188 void FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest( | 188 void FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest( |
| 189 intptr_t cid, | 189 intptr_t cid, |
| 190 intptr_t token_pos, | 190 intptr_t token_pos, |
| 191 const AbstractType& type, | 191 const AbstractType& type, |
| 192 Label* is_instance_lbl, | 192 Label* is_instance_lbl, |
| 193 Label* is_not_instance_lbl) { | 193 Label* is_not_instance_lbl) { |
| 194 ASSERT(type.IsInstantiated()); | 194 ASSERT(type.IsInstantiated()); |
| 195 const Class& type_class = Class::ZoneHandle(type.type_class()); | 195 const Class& type_class = Class::Handle(type.type_class()); |
| 196 ASSERT(!type_class.HasTypeArguments()); | 196 ASSERT(!type_class.HasTypeArguments()); |
| 197 | 197 |
| 198 const Register kInstanceReg = RAX; |
| 198 Label compare_classes; | 199 Label compare_classes; |
| 199 __ testq(RAX, Immediate(kSmiTagMask)); | 200 __ testq(kInstanceReg, Immediate(kSmiTagMask)); |
| 200 __ j(NOT_ZERO, &compare_classes); | 201 __ j(NOT_ZERO, &compare_classes, Assembler::kNearJump); |
| 201 // Instance is Smi, check directly. | 202 // Instance is Smi, check directly. |
| 202 const Class& smi_class = Class::Handle(Smi::Class()); | 203 const Class& smi_class = Class::Handle(Smi::Class()); |
| 203 // TODO(regis): We should introduce a SmiType. | 204 // TODO(regis): We should introduce a SmiType. |
| 204 Error& malformed_error = Error::Handle(); | 205 Error& malformed_error = Error::Handle(); |
| 205 if (smi_class.IsSubtypeOf(TypeArguments::Handle(), | 206 if (smi_class.IsSubtypeOf(TypeArguments::Handle(), |
| 206 type_class, | 207 type_class, |
| 207 TypeArguments::Handle(), | 208 TypeArguments::Handle(), |
| 208 &malformed_error)) { | 209 &malformed_error)) { |
| 209 __ jmp(is_instance_lbl); | 210 __ jmp(is_instance_lbl); |
| 210 } else { | 211 } else { |
| 211 __ jmp(is_not_instance_lbl); | 212 __ jmp(is_not_instance_lbl); |
| 212 } | 213 } |
| 213 | 214 // Compare if the classes are equal. |
| 214 // Compare if the classes are equal. Instance is not Smi. | |
| 215 __ Bind(&compare_classes); | 215 __ Bind(&compare_classes); |
| 216 const Register kClassIdReg = R10; | 216 const Register kClassIdReg = R10; |
| 217 __ LoadClassId(kClassIdReg, RAX); | 217 __ LoadClassId(kClassIdReg, kInstanceReg); |
| 218 // If type is an interface, we can skip the class equality check. | 218 // If type is an interface, we can skip the class equality check. |
| 219 if (!type_class.is_interface()) { | 219 if (!type_class.is_interface()) { |
| 220 __ cmpl(kClassIdReg, Immediate(type_class.id())); | 220 __ cmpl(kClassIdReg, Immediate(type_class.id())); |
| 221 __ j(EQUAL, is_instance_lbl); | 221 __ j(EQUAL, is_instance_lbl); |
| 222 } | 222 } |
| 223 // Check for interfaces that cannot be implemented by user. | 223 // Bool interface can be implemented only by core class Bool. |
| 224 // (see ClassFinalizer::ResolveInterfaces for list of restricted interfaces). | 224 // (see ClassFinalizer::ResolveInterfaces for list of restricted interfaces). |
| 225 // Bool interface can be implemented only by core class Bool. | |
| 226 if (type.IsBoolInterface()) { | 225 if (type.IsBoolInterface()) { |
| 227 __ cmpl(kClassIdReg, Immediate(kBool)); | 226 __ cmpl(kClassIdReg, Immediate(kBool)); |
| 228 __ j(EQUAL, is_instance_lbl); | 227 __ j(EQUAL, is_instance_lbl); |
| 229 __ jmp(is_not_instance_lbl); | 228 __ jmp(is_not_instance_lbl); |
| 230 return; | 229 return; |
| 231 } | 230 } |
| 232 if (type.IsFunctionInterface()) { | 231 if (type.IsFunctionInterface()) { |
| 233 // Check if instance is a closure. | 232 // Check if instance is a closure. |
| 234 const Immediate raw_null = | 233 const Immediate raw_null = |
| 235 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 234 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 251 if (type.IsStringInterface()) { | 250 if (type.IsStringInterface()) { |
| 252 GenerateStringTypeCheck(kClassIdReg, is_instance_lbl, is_not_instance_lbl); | 251 GenerateStringTypeCheck(kClassIdReg, is_instance_lbl, is_not_instance_lbl); |
| 253 return; | 252 return; |
| 254 } | 253 } |
| 255 // Otherwise fallthrough. | 254 // Otherwise fallthrough. |
| 256 } | 255 } |
| 257 | 256 |
| 258 | 257 |
| 259 // Uses SubtypeTestCache to store instance class and result. | 258 // Uses SubtypeTestCache to store instance class and result. |
| 260 // RAX: instance to test. | 259 // RAX: instance to test. |
| 260 // Clobbers R10, R13. |
| 261 // Immediate class test already done. | 261 // Immediate class test already done. |
| 262 // TODO(srdjan): Implement a quicker subtype check, as type test |
| 263 // arrays can grow too high, but they may be useful when optimizing |
| 264 // code (type-feedback). |
| 262 RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup( | 265 RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup( |
| 263 intptr_t cid, | 266 intptr_t cid, |
| 264 intptr_t token_pos, | 267 intptr_t token_pos, |
| 265 const Class& type_class, | 268 const Class& type_class, |
| 266 Label* is_instance_lbl, | 269 Label* is_instance_lbl, |
| 267 Label* is_not_instance_lbl) { | 270 Label* is_not_instance_lbl) { |
| 268 const Register kInstanceReg = RAX; | 271 const Register kInstanceReg = RAX; |
| 269 __ LoadClass(R10, kInstanceReg); | 272 __ LoadClass(R10, kInstanceReg); |
| 273 // R10: instance class. |
| 270 // Check immediate superclass equality. | 274 // Check immediate superclass equality. |
| 271 __ movq(R13, FieldAddress(R10, Class::super_type_offset())); | 275 __ movq(R13, FieldAddress(R10, Class::super_type_offset())); |
| 272 __ movq(R13, FieldAddress(R13, Type::type_class_offset())); | 276 __ movq(R13, FieldAddress(R13, Type::type_class_offset())); |
| 273 __ CompareObject(R13, type_class); | 277 __ CompareObject(R13, type_class); |
| 274 __ j(EQUAL, is_instance_lbl); | 278 __ j(EQUAL, is_instance_lbl); |
| 275 | 279 |
| 276 const Register kTypeArgumentsReg = kNoRegister; | 280 const Register kTypeArgumentsReg = kNoRegister; |
| 277 const Register kTempReg = R10; | 281 const Register kTempReg = R10; |
| 278 return GenerateCallSubtypeTestStub(kTestTypeOneArg, | 282 return GenerateCallSubtypeTestStub(kTestTypeOneArg, |
| 279 kInstanceReg, | 283 kInstanceReg, |
| 280 kTypeArgumentsReg, | 284 kTypeArgumentsReg, |
| 281 kTempReg, | 285 kTempReg, |
| 282 is_instance_lbl, | 286 is_instance_lbl, |
| 283 is_not_instance_lbl); | 287 is_not_instance_lbl); |
| 284 } | 288 } |
| 285 | 289 |
| 286 | 290 |
| 287 // Generates inlined check if 'type' is a type parameter or type itsef | 291 // Generates inlined check if 'type' is a type parameter or type itsef |
| 288 // RAX: instance (preserved). | 292 // RAX: instance (preserved). |
| 293 // Clobbers RDI, RDX, R10. |
| 289 RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( | 294 RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( |
| 290 intptr_t cid, | 295 intptr_t cid, |
| 291 intptr_t token_pos, | 296 intptr_t token_pos, |
| 292 const AbstractType& type, | 297 const AbstractType& type, |
| 293 Label* is_instance_lbl, | 298 Label* is_instance_lbl, |
| 294 Label* is_not_instance_lbl) { | 299 Label* is_not_instance_lbl) { |
| 295 ASSERT(!type.IsInstantiated()); | 300 ASSERT(!type.IsInstantiated()); |
| 301 // Skip check if destination is a dynamic type. |
| 296 const Immediate raw_null = | 302 const Immediate raw_null = |
| 297 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 303 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 298 if (type.IsTypeParameter()) { | 304 if (type.IsTypeParameter()) { |
| 299 // Load instantiator (or null) and instantiator type arguments on stack. | 305 // Load instantiator (or null) and instantiator type arguments on stack. |
| 300 __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments. | 306 __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments. |
| 301 // RDX: instantiator type arguments. | 307 // RDX: instantiator type arguments. |
| 302 // Check if type argument is Dynamic. | 308 // Check if type argument is Dynamic. |
| 303 __ cmpq(RDX, raw_null); | 309 __ cmpq(RDX, raw_null); |
| 304 __ j(EQUAL, is_instance_lbl); | 310 __ j(EQUAL, is_instance_lbl); |
| 305 // Can handle only type arguments that are instances of TypeArguments. | 311 // Can handle only type arguments that are instances of TypeArguments. |
| 306 // (runtime checks canonicalize type arguments). | 312 // (runtime checks canonicalize type arguments). |
| 307 Label fall_through; | 313 Label fall_through; |
| 308 __ CompareClassId(RDX, kTypeArguments); | 314 __ CompareClassId(RDX, kTypeArguments); |
| 309 __ j(NOT_EQUAL, &fall_through); | 315 __ j(NOT_EQUAL, &fall_through); |
| 310 __ movq(RDI, | 316 __ movq(RDI, |
| 311 FieldAddress(RDX, TypeArguments::type_at_offset(type.Index()))); | 317 FieldAddress(RDX, TypeArguments::type_at_offset(type.Index()))); |
| 312 // RDI: Concrete type. | 318 // RDI: Concrete type of type. |
| 313 // Check if it is Dynamic, | 319 // Check if type argument is dynamic. |
| 314 __ CompareObject(RDI, Type::ZoneHandle(Type::DynamicType())); | 320 __ CompareObject(RDI, Type::ZoneHandle(Type::DynamicType())); |
| 315 __ j(EQUAL, is_instance_lbl); | 321 __ j(EQUAL, is_instance_lbl); |
| 316 __ cmpq(RDI, raw_null); | 322 __ cmpq(RDI, raw_null); |
| 317 __ j(EQUAL, is_instance_lbl); | 323 __ j(EQUAL, is_instance_lbl); |
| 318 const Type& object_type = Type::ZoneHandle(Type::ObjectType()); | 324 const Type& object_type = Type::ZoneHandle(Type::ObjectType()); |
| 319 __ CompareObject(RDI, object_type); | 325 __ CompareObject(RDI, object_type); |
| 320 __ j(EQUAL, is_instance_lbl); | 326 __ j(EQUAL, is_instance_lbl); |
| 321 | 327 |
| 322 // For Smi check quickly against int and num interface types. | 328 // For Smi check quickly against int and num interfaces. |
| 323 Label not_smi; | 329 Label not_smi; |
| 324 __ testq(RAX, Immediate(kSmiTagMask)); // Value is Smi? | 330 __ testq(RAX, Immediate(kSmiTagMask)); // Value is Smi? |
| 325 __ j(NOT_ZERO, ¬_smi, Assembler::kNearJump); | 331 __ j(NOT_ZERO, ¬_smi, Assembler::kNearJump); |
| 326 __ CompareObject(RDI, Type::ZoneHandle(Type::IntInterface())); | 332 __ CompareObject(RDI, Type::ZoneHandle(Type::IntInterface())); |
| 327 __ j(EQUAL, is_instance_lbl); | 333 __ j(EQUAL, is_instance_lbl); |
| 328 __ CompareObject(RDI, Type::ZoneHandle(Type::NumberInterface())); | 334 __ CompareObject(RDI, Type::ZoneHandle(Type::NumberInterface())); |
| 329 __ j(EQUAL, is_instance_lbl); | 335 __ j(EQUAL, is_instance_lbl); |
| 330 // Smi must be handled in runtime. | 336 // Smi must be handled in runtime. |
| 331 __ jmp(&fall_through); | 337 __ jmp(&fall_through); |
| 332 | 338 |
| 333 __ Bind(¬_smi); | 339 __ Bind(¬_smi); |
| 334 // RDX: instantiator type arguments. | 340 // RDX: instantiator type arguments. |
| 335 // RAX: instance. | 341 // RAX: instance. |
| 336 const Register kInstanceReg = RAX; | 342 const Register kInstanceReg = RAX; |
| 337 const Register kTypeArgumentsReg = RDX; | 343 const Register kTypeArgumentsReg = RDX; |
| 338 const Register kTempReg = R10; | 344 const Register kTempReg = R10; |
| 339 const SubtypeTestCache& type_test_cache = | 345 const SubtypeTestCache& type_test_cache = |
| 340 SubtypeTestCache::ZoneHandle( | 346 SubtypeTestCache::ZoneHandle( |
| 341 GenerateCallSubtypeTestStub(kTestTypeThreeArgs, | 347 GenerateCallSubtypeTestStub(kTestTypeThreeArgs, |
| 342 kInstanceReg, | 348 kInstanceReg, |
| 343 kTypeArgumentsReg, | 349 kTypeArgumentsReg, |
| 344 kTempReg, | 350 kTempReg, |
| 345 is_instance_lbl, | 351 is_instance_lbl, |
| 346 is_not_instance_lbl)); | 352 is_not_instance_lbl)); |
| 347 | |
| 348 __ Bind(&fall_through); | 353 __ Bind(&fall_through); |
| 349 return type_test_cache.raw(); | 354 return type_test_cache.raw(); |
| 350 } | 355 } |
| 351 if (type.IsType()) { | 356 if (type.IsType()) { |
| 352 const Register kInstanceReg = RAX; | 357 const Register kInstanceReg = RAX; |
| 353 const Register kTypeArgumentsReg = RDX; | 358 const Register kTypeArgumentsReg = RDX; |
| 354 __ testq(kInstanceReg, Immediate(kSmiTagMask)); // Is instance Smi? | 359 __ testq(kInstanceReg, Immediate(kSmiTagMask)); // Is instance Smi? |
| 355 __ j(ZERO, is_not_instance_lbl); | 360 __ j(ZERO, is_not_instance_lbl); |
| 356 __ movq(kTypeArgumentsReg, Address(RSP, 0)); // Instantiator type args. | 361 __ movq(kTypeArgumentsReg, Address(RSP, 0)); // Instantiator type args. |
| 357 // Uninstantiated type class is known at compile time, but the type | 362 // Uninstantiated type class is known at compile time, but the type |
| 358 // arguments are determined at runtime by the instantiator. | 363 // arguments are determined at runtime by the instantiator. |
| 359 const Register kTempReg = R10; | 364 const Register kTempReg = R10; |
| 360 return GenerateCallSubtypeTestStub(kTestTypeThreeArgs, | 365 return GenerateCallSubtypeTestStub(kTestTypeThreeArgs, |
| 361 kInstanceReg, | 366 kInstanceReg, |
| 362 kTypeArgumentsReg, | 367 kTypeArgumentsReg, |
| 363 kTempReg, | 368 kTempReg, |
| 364 is_instance_lbl, | 369 is_instance_lbl, |
| 365 is_not_instance_lbl); | 370 is_not_instance_lbl); |
| 366 } | 371 } |
| 367 return SubtypeTestCache::null(); | 372 return SubtypeTestCache::null(); |
| 368 } | 373 } |
| 369 | 374 |
| 370 | 375 |
| 371 // Inputs: | 376 // Inputs: |
| 372 // - RAX: instance to test against (preserved). | 377 // - RAX: instance to test against (preserved). |
| 373 // - RDX: optional instantiator type arguments (preserved). | 378 // - RDX: optional instantiator type arguments (preserved). |
| 374 // Destroys RCX. | 379 // Clobbers R10, R13. |
| 375 // Returns: | 380 // Returns: |
| 376 // - unchanged object in RAX and optional instantiator type arguments in RDX. | 381 // - preserved instance in RAX and optional instantiator type arguments in RDX. |
| 377 // Note that this inlined code must be followed by the runtime_call code, as it | 382 // Note that this inlined code must be followed by the runtime_call code, as it |
| 378 // may fall through to it. Otherwise, this inline code will jump to the label | 383 // may fall through to it. Otherwise, this inline code will jump to the label |
| 379 // is_instance or to the label is_not_instance. | 384 // is_instance or to the label is_not_instance. |
| 380 RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( | 385 RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( |
| 381 intptr_t cid, | 386 intptr_t cid, |
| 382 intptr_t token_pos, | 387 intptr_t token_pos, |
| 383 const AbstractType& type, | 388 const AbstractType& type, |
| 384 Label* is_instance_lbl, | 389 Label* is_instance_lbl, |
| 385 Label* is_not_instance_lbl) { | 390 Label* is_not_instance_lbl) { |
| 386 if (type.IsInstantiated()) { | 391 if (type.IsInstantiated()) { |
| (...skipping 24 matching lines...) Expand all Loading... |
| 411 return GenerateUninstantiatedTypeTest(cid, | 416 return GenerateUninstantiatedTypeTest(cid, |
| 412 token_pos, | 417 token_pos, |
| 413 type, | 418 type, |
| 414 is_instance_lbl, | 419 is_instance_lbl, |
| 415 is_not_instance_lbl); | 420 is_not_instance_lbl); |
| 416 } | 421 } |
| 417 return SubtypeTestCache::null(); | 422 return SubtypeTestCache::null(); |
| 418 } | 423 } |
| 419 | 424 |
| 420 | 425 |
| 426 // If instanceof type test cannot be performed successfully at compile time and |
| 427 // therefore eliminated, optimize it by adding inlined tests for: |
| 428 // - NULL -> return false. |
| 429 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
| 430 // - Class equality (only if class is not parameterized). |
| 431 // Inputs: |
| 432 // - RAX: object. |
| 433 // - RDX: instantiator type arguments or raw_null. |
| 434 // - RCX: instantiator or raw_null. |
| 435 // Clobbers RCX and RDX. |
| 436 // Returns: |
| 437 // - true or false in RAX. |
| 438 void FlowGraphCompiler::GenerateInstanceOf(intptr_t cid, |
| 439 intptr_t token_pos, |
| 440 intptr_t try_index, |
| 441 const AbstractType& type, |
| 442 bool negate_result) { |
| 443 ASSERT(type.IsFinalized() && !type.IsMalformed()); |
| 444 |
| 445 const Immediate raw_null = |
| 446 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 447 Label is_instance, is_not_instance; |
| 448 __ pushq(RCX); // Store instantiator on stack. |
| 449 __ pushq(RDX); // Store instantiator type arguments. |
| 450 // If type is instantiated and non-parameterized, we can inline code |
| 451 // checking whether the tested instance is a Smi. |
| 452 if (type.IsInstantiated()) { |
| 453 // A null object is only an instance of Object and Dynamic, which has |
| 454 // already been checked above (if the type is instantiated). So we can |
| 455 // return false here if the instance is null (and if the type is |
| 456 // instantiated). |
| 457 // We can only inline this null check if the type is instantiated at compile |
| 458 // time, since an uninstantiated type at compile time could be Object or |
| 459 // Dynamic at run time. |
| 460 __ cmpq(RAX, raw_null); |
| 461 __ j(EQUAL, &is_not_instance); |
| 462 } |
| 463 |
| 464 // Generate inline instanceof test. |
| 465 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); |
| 466 test_cache = GenerateInlineInstanceof(cid, token_pos, type, |
| 467 &is_instance, &is_not_instance); |
| 468 |
| 469 // Generate runtime call. |
| 470 __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments. |
| 471 __ movq(RCX, Address(RSP, kWordSize)); // Get instantiator. |
| 472 __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| 473 __ pushq(Immediate(Smi::RawValue(token_pos))); // Source location. |
| 474 __ pushq(Immediate(Smi::RawValue(cid))); // Computation id. |
| 475 __ pushq(RAX); // Push the instance. |
| 476 __ PushObject(type); // Push the type. |
| 477 __ pushq(RCX); // TODO(srdjan): Pass instantiator instead of null. |
| 478 __ pushq(RDX); // Instantiator type arguments. |
| 479 __ LoadObject(RAX, test_cache); |
| 480 __ pushq(RAX); |
| 481 GenerateCallRuntime(cid, token_pos, try_index, kInstanceofRuntimeEntry); |
| 482 // Pop the two parameters supplied to the runtime entry. The result of the |
| 483 // instanceof runtime call will be left as the result of the operation. |
| 484 __ Drop(7); |
| 485 Label done; |
| 486 if (negate_result) { |
| 487 __ popq(RDX); |
| 488 __ LoadObject(RAX, bool_true()); |
| 489 __ cmpq(RDX, RAX); |
| 490 __ j(NOT_EQUAL, &done, Assembler::kNearJump); |
| 491 __ LoadObject(RAX, bool_false()); |
| 492 } else { |
| 493 __ popq(RAX); |
| 494 } |
| 495 __ jmp(&done, Assembler::kNearJump); |
| 496 |
| 497 __ Bind(&is_not_instance); |
| 498 __ LoadObject(RAX, negate_result ? bool_true() : bool_false()); |
| 499 __ jmp(&done, Assembler::kNearJump); |
| 500 |
| 501 __ Bind(&is_instance); |
| 502 __ LoadObject(RAX, negate_result ? bool_false() : bool_true()); |
| 503 __ Bind(&done); |
| 504 __ popq(RDX); // Remove pushed instantiator type arguments. |
| 505 __ popq(RCX); // Remove pushed instantiator. |
| 506 } |
| 507 |
| 508 |
| 421 // Optimize assignable type check by adding inlined tests for: | 509 // Optimize assignable type check by adding inlined tests for: |
| 422 // - NULL -> return NULL. | 510 // - NULL -> return NULL. |
| 423 // - Smi -> compile time subtype check (only if dst class is not parameterized). | 511 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
| 424 // - Class equality (only if class is not parameterized). | 512 // - Class equality (only if class is not parameterized). |
| 425 // Inputs: | 513 // Inputs: |
| 426 // - RAX: object. | 514 // - RAX: object. |
| 427 // - RDX: instantiator type arguments or raw_null. | 515 // - RDX: instantiator type arguments or raw_null. |
| 428 // - RCX: instantiator or raw_null. | 516 // - RCX: instantiator or raw_null. |
| 429 // Returns: | 517 // Returns: |
| 430 // - object in RAX for successful assignable check (or throws TypeError). | 518 // - object in RAX for successful assignable check (or throws TypeError). |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 469 __ int3(); | 557 __ int3(); |
| 470 | 558 |
| 471 __ Bind(&is_assignable); // For a null object. | 559 __ Bind(&is_assignable); // For a null object. |
| 472 return; | 560 return; |
| 473 } | 561 } |
| 474 | 562 |
| 475 // Generate inline type check, linking to runtime call if not assignable. | 563 // Generate inline type check, linking to runtime call if not assignable. |
| 476 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); | 564 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); |
| 477 test_cache = GenerateInlineInstanceof(cid, token_pos, dst_type, | 565 test_cache = GenerateInlineInstanceof(cid, token_pos, dst_type, |
| 478 &is_assignable, &runtime_call); | 566 &is_assignable, &runtime_call); |
| 567 |
| 479 __ Bind(&runtime_call); | 568 __ Bind(&runtime_call); |
| 480 __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments. | 569 __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments. |
| 481 __ movq(RCX, Address(RSP, kWordSize)); // Get instantiator. | 570 __ movq(RCX, Address(RSP, kWordSize)); // Get instantiator. |
| 482 __ PushObject(Object::ZoneHandle()); // Make room for the result. | 571 __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| 483 __ pushq(Immediate(Smi::RawValue(token_pos))); // Source location. | 572 __ pushq(Immediate(Smi::RawValue(token_pos))); // Source location. |
| 484 __ pushq(Immediate(Smi::RawValue(cid))); // Computation id. | 573 __ pushq(Immediate(Smi::RawValue(cid))); // Computation id. |
| 485 __ pushq(RAX); // Push the source object. | 574 __ pushq(RAX); // Push the source object. |
| 486 __ PushObject(dst_type); // Push the type of the destination. | 575 __ PushObject(dst_type); // Push the type of the destination. |
| 487 __ pushq(RCX); // Instantiator. | 576 __ pushq(RCX); // Instantiator. |
| 488 __ pushq(RDX); // Instantiator type arguments. | 577 __ pushq(RDX); // Instantiator type arguments. |
| 489 __ PushObject(dst_name); // Push the name of the destination. | 578 __ PushObject(dst_name); // Push the name of the destination. |
| 490 __ LoadObject(RAX, test_cache); | 579 __ LoadObject(RAX, test_cache); |
| 491 __ pushq(RAX); | 580 __ pushq(RAX); |
| 492 GenerateCallRuntime(cid, | 581 GenerateCallRuntime(cid, |
| 493 token_pos, | 582 token_pos, |
| 494 try_index, | 583 try_index, |
| 495 kTypeCheckRuntimeEntry); | 584 kTypeCheckRuntimeEntry); |
| 496 // Pop the parameters supplied to the runtime entry. The result of the | 585 // Pop the parameters supplied to the runtime entry. The result of the |
| 497 // type check runtime call is the checked value. | 586 // type check runtime call is the checked value. |
| 498 __ Drop(8); | 587 __ Drop(8); |
| 499 __ popq(RAX); | 588 __ popq(RAX); |
| 500 | 589 |
| 501 __ Bind(&is_assignable); | 590 __ Bind(&is_assignable); |
| 502 __ popq(RDX); // Remove pushed instantiator type arguments.. | 591 __ popq(RDX); // Remove pushed instantiator type arguments.. |
| 503 __ popq(RCX); // Remove pushed instantiator. | 592 __ popq(RCX); // Remove pushed instantiator. |
| 504 } | 593 } |
| 505 | 594 |
| 506 | 595 |
| 507 void FlowGraphCompiler::LoadValue(Register dst, Value* value) { | |
| 508 if (value->IsConstant()) { | |
| 509 ConstantVal* constant = value->AsConstant(); | |
| 510 if (constant->value().IsSmi()) { | |
| 511 int64_t imm = reinterpret_cast<int64_t>(constant->value().raw()); | |
| 512 __ movq(dst, Immediate(imm)); | |
| 513 } else { | |
| 514 __ LoadObject(dst, value->AsConstant()->value()); | |
| 515 } | |
| 516 } else { | |
| 517 ASSERT(value->IsUse()); | |
| 518 __ popq(dst); | |
| 519 } | |
| 520 } | |
| 521 | |
| 522 | |
| 523 intptr_t FlowGraphCompiler::EmitInstanceCall(ExternalLabel* target_label, | |
| 524 const ICData& ic_data, | |
| 525 const Array& arguments_descriptor, | |
| 526 intptr_t argument_count) { | |
| 527 __ LoadObject(RBX, ic_data); | |
| 528 __ LoadObject(R10, arguments_descriptor); | |
| 529 | |
| 530 __ call(target_label); | |
| 531 const intptr_t descr_offset = assembler()->CodeSize(); | |
| 532 __ Drop(argument_count); | |
| 533 return descr_offset; | |
| 534 } | |
| 535 | |
| 536 intptr_t FlowGraphCompiler::EmitStaticCall(const Function& function, | |
| 537 const Array& arguments_descriptor, | |
| 538 intptr_t argument_count) { | |
| 539 __ LoadObject(RBX, function); | |
| 540 __ LoadObject(R10, arguments_descriptor); | |
| 541 __ call(&StubCode::CallStaticFunctionLabel()); | |
| 542 const intptr_t descr_offset = assembler()->CodeSize(); | |
| 543 __ Drop(argument_count); | |
| 544 return descr_offset; | |
| 545 } | |
| 546 | |
| 547 | |
| 548 // Optimize instanceof type test by adding inlined tests for: | |
| 549 // - NULL -> return false. | |
| 550 // - Smi -> compile time subtype check (only if dst class is not parameterized). | |
| 551 // - Class equality (only if class is not parameterized). | |
| 552 // Inputs: | |
| 553 // - RAX: object. | |
| 554 // - RDX: instantiator type arguments or raw_null. | |
| 555 // - RCX: instantiator or raw_null. | |
| 556 // Destroys RCX and RDX. | |
| 557 // Returns: | |
| 558 // - true or false in RAX. | |
| 559 void FlowGraphCompiler::GenerateInstanceOf(intptr_t cid, | |
| 560 intptr_t token_pos, | |
| 561 intptr_t try_index, | |
| 562 const AbstractType& type, | |
| 563 bool negate_result) { | |
| 564 ASSERT(type.IsFinalized() && !type.IsMalformed()); | |
| 565 | |
| 566 const Immediate raw_null = | |
| 567 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 568 Label is_instance, is_not_instance; | |
| 569 __ pushq(RCX); // Store instantiator on stack. | |
| 570 __ pushq(RDX); // Store instantiator type arguments. | |
| 571 // If type is instantiated and non-parameterized, we can inline code | |
| 572 // checking whether the tested instance is a Smi. | |
| 573 if (type.IsInstantiated()) { | |
| 574 // A null object is only an instance of Object and Dynamic, which has | |
| 575 // already been checked above (if the type is instantiated). So we can | |
| 576 // return false here if the instance is null (and if the type is | |
| 577 // instantiated). | |
| 578 // We can only inline this null check if the type is instantiated at compile | |
| 579 // time, since an uninstantiated type at compile time could be Object or | |
| 580 // Dynamic at run time. | |
| 581 __ cmpq(RAX, raw_null); | |
| 582 __ j(EQUAL, &is_not_instance); | |
| 583 } | |
| 584 | |
| 585 // Generate inline instanceof test. | |
| 586 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); | |
| 587 test_cache = GenerateInlineInstanceof(cid, token_pos, type, | |
| 588 &is_instance, &is_not_instance); | |
| 589 | |
| 590 // Generate runtime call. | |
| 591 __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments. | |
| 592 __ movq(RCX, Address(RSP, kWordSize)); // Get instantiator. | |
| 593 __ PushObject(Object::ZoneHandle()); // Make room for the result. | |
| 594 __ pushq(Immediate(Smi::RawValue(token_pos))); // Source location. | |
| 595 __ pushq(Immediate(Smi::RawValue(cid))); // Computation id. | |
| 596 __ pushq(RAX); // Push the instance. | |
| 597 __ PushObject(type); // Push the type. | |
| 598 __ pushq(RCX); // TODO(srdjan): Pass instantiator instead of null. | |
| 599 __ pushq(RDX); // Instantiator type arguments. | |
| 600 __ LoadObject(RAX, test_cache); | |
| 601 __ pushq(RAX); | |
| 602 GenerateCallRuntime(cid, token_pos, try_index, kInstanceofRuntimeEntry); | |
| 603 // Pop the two parameters supplied to the runtime entry. The result of the | |
| 604 // instanceof runtime call will be left as the result of the operation. | |
| 605 __ Drop(7); | |
| 606 Label done; | |
| 607 if (negate_result) { | |
| 608 __ popq(RDX); | |
| 609 __ LoadObject(RAX, bool_true()); | |
| 610 __ cmpq(RDX, RAX); | |
| 611 __ j(NOT_EQUAL, &done, Assembler::kNearJump); | |
| 612 __ LoadObject(RAX, bool_false()); | |
| 613 } else { | |
| 614 __ popq(RAX); | |
| 615 } | |
| 616 __ jmp(&done, Assembler::kNearJump); | |
| 617 | |
| 618 __ Bind(&is_not_instance); | |
| 619 __ LoadObject(RAX, negate_result ? bool_true() : bool_false()); | |
| 620 __ jmp(&done, Assembler::kNearJump); | |
| 621 | |
| 622 __ Bind(&is_instance); | |
| 623 __ LoadObject(RAX, negate_result ? bool_false() : bool_true()); | |
| 624 __ Bind(&done); | |
| 625 __ popq(RDX); // Remove pushed instantiator type arguments. | |
| 626 __ popq(RCX); // Remove pushed instantiator. | |
| 627 } | |
| 628 | |
| 629 | |
| 630 void FlowGraphCompiler::EmitInstructionPrologue(Instruction* instr) { | 596 void FlowGraphCompiler::EmitInstructionPrologue(Instruction* instr) { |
| 631 LocationSummary* locs = instr->locs(); | 597 LocationSummary* locs = instr->locs(); |
| 632 ASSERT(locs != NULL); | 598 ASSERT(locs != NULL); |
| 633 | 599 |
| 634 frame_register_allocator()->AllocateRegisters(instr); | 600 frame_register_allocator()->AllocateRegisters(instr); |
| 635 | 601 |
| 636 // TODO(vegorov): adjust assertion when we start removing comparison from the | 602 // TODO(vegorov): adjust assertion when we start removing comparison from the |
| 637 // graph when it is merged with a branch. | 603 // graph when it is merged with a branch. |
| 638 ASSERT(locs->is_call() || | 604 ASSERT(locs->is_call() || |
| 639 (instr->IsBranch() && instr->AsBranch()->is_fused_with_comparison()) || | 605 (instr->IsBranch() && instr->AsBranch()->is_fused_with_comparison()) || |
| 640 (locs->input_count() == instr->InputCount())); | 606 (locs->input_count() == instr->InputCount())); |
| 641 } | 607 } |
| 642 | 608 |
| 643 | 609 |
| 644 // Copied from CodeGenerator::CopyParameters (CodeGenerator will be deprecated). | |
| 645 void FlowGraphCompiler::CopyParameters() { | 610 void FlowGraphCompiler::CopyParameters() { |
| 646 const Function& function = parsed_function().function(); | 611 const Function& function = parsed_function().function(); |
| 647 const bool is_native_instance_closure = | 612 const bool is_native_instance_closure = |
| 648 function.is_native() && function.IsImplicitInstanceClosureFunction(); | 613 function.is_native() && function.IsImplicitInstanceClosureFunction(); |
| 649 LocalScope* scope = parsed_function().node_sequence()->scope(); | 614 LocalScope* scope = parsed_function().node_sequence()->scope(); |
| 650 const int num_fixed_params = function.num_fixed_parameters(); | 615 const int num_fixed_params = function.num_fixed_parameters(); |
| 651 const int num_opt_params = function.num_optional_parameters(); | 616 const int num_opt_params = function.num_optional_parameters(); |
| 652 int implicit_this_param_pos = is_native_instance_closure ? -1 : 0; | 617 int implicit_this_param_pos = is_native_instance_closure ? -1 : 0; |
| 653 ASSERT(parsed_function().first_parameter_index() == | 618 ASSERT(parsed_function().first_parameter_index() == |
| 654 ParsedFunction::kFirstLocalSlotIndex + implicit_this_param_pos); | 619 ParsedFunction::kFirstLocalSlotIndex + implicit_this_param_pos); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 669 __ movq(RCX, FieldAddress(R10, Array::data_offset() + (1 * kWordSize))); | 634 __ movq(RCX, FieldAddress(R10, Array::data_offset() + (1 * kWordSize))); |
| 670 // Check that num_pos_args >= num_fixed_params. | 635 // Check that num_pos_args >= num_fixed_params. |
| 671 __ cmpq(RCX, Immediate(Smi::RawValue(num_fixed_params))); | 636 __ cmpq(RCX, Immediate(Smi::RawValue(num_fixed_params))); |
| 672 __ j(LESS, &wrong_num_arguments); | 637 __ j(LESS, &wrong_num_arguments); |
| 673 | 638 |
| 674 // Since RBX and RCX are Smi, use TIMES_4 instead of TIMES_8. | 639 // Since RBX and RCX are Smi, use TIMES_4 instead of TIMES_8. |
| 675 // Let RBX point to the last passed positional argument, i.e. to | 640 // Let RBX point to the last passed positional argument, i.e. to |
| 676 // fp[1 + num_args - (num_pos_args - 1)]. | 641 // fp[1 + num_args - (num_pos_args - 1)]. |
| 677 __ subq(RBX, RCX); | 642 __ subq(RBX, RCX); |
| 678 __ leaq(RBX, Address(RBP, RBX, TIMES_4, 2 * kWordSize)); | 643 __ leaq(RBX, Address(RBP, RBX, TIMES_4, 2 * kWordSize)); |
| 644 |
| 679 // Let RDI point to the last copied positional argument, i.e. to | 645 // Let RDI point to the last copied positional argument, i.e. to |
| 680 // fp[ParsedFunction::kFirstLocalSlotIndex - (num_pos_args - 1)]. | 646 // fp[ParsedFunction::kFirstLocalSlotIndex - (num_pos_args - 1)]. |
| 681 const int index = | 647 const int index = |
| 682 ParsedFunction::kFirstLocalSlotIndex + 1 + implicit_this_param_pos; | 648 ParsedFunction::kFirstLocalSlotIndex + 1 + implicit_this_param_pos; |
| 683 // First copy captured receiver if function is an implicit native closure. | 649 // First copy captured receiver if function is an implicit native closure. |
| 684 if (is_native_instance_closure) { | 650 if (is_native_instance_closure) { |
| 685 __ movq(RAX, FieldAddress(CTX, Context::variable_offset(0))); | 651 __ movq(RAX, FieldAddress(CTX, Context::variable_offset(0))); |
| 686 __ movq(Address(RBP, (index * kWordSize)), RAX); | 652 __ movq(Address(RBP, (index * kWordSize)), RAX); |
| 687 } | 653 } |
| 688 __ SmiUntag(RCX); | 654 __ SmiUntag(RCX); |
| (...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 898 __ SmiUntag(RAX); | 864 __ SmiUntag(RAX); |
| 899 __ cvtsi2sd(XMM1, RAX); | 865 __ cvtsi2sd(XMM1, RAX); |
| 900 __ jmp(&double_op); | 866 __ jmp(&double_op); |
| 901 __ Bind(&call_method); | 867 __ Bind(&call_method); |
| 902 } | 868 } |
| 903 | 869 |
| 904 | 870 |
| 905 void FlowGraphCompiler::CompileGraph() { | 871 void FlowGraphCompiler::CompileGraph() { |
| 906 InitCompiler(); | 872 InitCompiler(); |
| 907 if (TryIntrinsify()) { | 873 if (TryIntrinsify()) { |
| 908 // Make it patchable: code must have a minimum code size, nop(2) increases | 874 // Although this intrinsified code will never be patched, it must satisfy |
| 909 // the minimum code size appropriately. | 875 // CodePatcher::CodeIsPatchable, which verifies that this code has a minimum |
| 876 // code size, and nop(2) increases the minimum code size appropriately. |
| 910 __ nop(2); | 877 __ nop(2); |
| 911 __ int3(); | 878 __ int3(); |
| 912 __ jmp(&StubCode::FixCallersTargetLabel()); | 879 __ jmp(&StubCode::FixCallersTargetLabel()); |
| 913 return; | 880 return; |
| 914 } | 881 } |
| 915 // Specialized version of entry code from CodeGenerator::GenerateEntryCode. | 882 // Specialized version of entry code from CodeGenerator::GenerateEntryCode. |
| 916 const Function& function = parsed_function().function(); | 883 const Function& function = parsed_function().function(); |
| 917 | 884 |
| 918 const int parameter_count = function.num_fixed_parameters(); | 885 const int parameter_count = function.num_fixed_parameters(); |
| 919 const int num_copied_params = parsed_function().copied_parameter_count(); | 886 const int num_copied_params = parsed_function().copied_parameter_count(); |
| 920 const int local_count = parsed_function().stack_local_count(); | 887 const int local_count = parsed_function().stack_local_count(); |
| 921 AssemblerMacros::EnterDartFrame(assembler(), (StackSize() * kWordSize)); | 888 AssemblerMacros::EnterDartFrame(assembler(), (StackSize() * kWordSize)); |
| 922 | |
| 923 // We check the number of passed arguments when we have to copy them due to | 889 // We check the number of passed arguments when we have to copy them due to |
| 924 // the presence of optional named parameters. | 890 // the presence of optional named parameters. |
| 925 // No such checking code is generated if only fixed parameters are declared, | 891 // No such checking code is generated if only fixed parameters are declared, |
| 926 // unless we are debug mode or unless we are compiling a closure. | 892 // unless we are debug mode or unless we are compiling a closure. |
| 927 if (num_copied_params == 0) { | 893 if (num_copied_params == 0) { |
| 928 #ifdef DEBUG | 894 #ifdef DEBUG |
| 929 const bool check_arguments = true; | 895 const bool check_arguments = true; |
| 930 #else | 896 #else |
| 931 const bool check_arguments = function.IsClosureFunction(); | 897 const bool check_arguments = function.IsClosureFunction(); |
| 932 #endif | 898 #endif |
| (...skipping 10 matching lines...) Expand all Loading... |
| 943 CatchClauseNode::kInvalidTryIndex, | 909 CatchClauseNode::kInvalidTryIndex, |
| 944 kClosureArgumentMismatchRuntimeEntry); | 910 kClosureArgumentMismatchRuntimeEntry); |
| 945 } else { | 911 } else { |
| 946 __ Stop("Wrong number of arguments"); | 912 __ Stop("Wrong number of arguments"); |
| 947 } | 913 } |
| 948 __ Bind(&argc_in_range); | 914 __ Bind(&argc_in_range); |
| 949 } | 915 } |
| 950 } else { | 916 } else { |
| 951 CopyParameters(); | 917 CopyParameters(); |
| 952 } | 918 } |
| 953 | 919 // Initialize (non-argument) stack allocated locals to null. |
| 954 // Initialize locals to null. | |
| 955 if (local_count > 0) { | 920 if (local_count > 0) { |
| 956 __ movq(RAX, Immediate(reinterpret_cast<intptr_t>(Object::null()))); | 921 const Immediate raw_null = |
| 922 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 923 __ movq(RAX, raw_null); |
| 957 const int base = parsed_function().first_stack_local_index(); | 924 const int base = parsed_function().first_stack_local_index(); |
| 958 for (int i = 0; i < local_count; ++i) { | 925 for (int i = 0; i < local_count; ++i) { |
| 959 // Subtract index i (locals lie at lower addresses than RBP). | 926 // Subtract index i (locals lie at lower addresses than RBP). |
| 960 __ movq(Address(RBP, (base - i) * kWordSize), RAX); | 927 __ movq(Address(RBP, (base - i) * kWordSize), RAX); |
| 961 } | 928 } |
| 962 } | 929 } |
| 963 | 930 |
| 964 // Generate stack overflow check. | 931 // Generate stack overflow check. |
| 965 __ movq(RDI, Immediate(Isolate::Current()->stack_limit_address())); | 932 __ movq(RDI, Immediate(Isolate::Current()->stack_limit_address())); |
| 966 __ cmpq(RSP, Address(RDI, 0)); | 933 __ cmpq(RSP, Address(RDI, 0)); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 990 // at entry point. | 957 // at entry point. |
| 991 pc_descriptors_list()->AddDescriptor(PcDescriptors::kPatchCode, | 958 pc_descriptors_list()->AddDescriptor(PcDescriptors::kPatchCode, |
| 992 assembler()->CodeSize(), | 959 assembler()->CodeSize(), |
| 993 AstNode::kNoId, | 960 AstNode::kNoId, |
| 994 0, | 961 0, |
| 995 -1); | 962 -1); |
| 996 __ jmp(&StubCode::FixCallersTargetLabel()); | 963 __ jmp(&StubCode::FixCallersTargetLabel()); |
| 997 } | 964 } |
| 998 | 965 |
| 999 | 966 |
| 1000 // Infrastructure copied from class CodeGenerator. | |
| 1001 void FlowGraphCompiler::GenerateCall(intptr_t token_pos, | 967 void FlowGraphCompiler::GenerateCall(intptr_t token_pos, |
| 1002 intptr_t try_index, | 968 intptr_t try_index, |
| 1003 const ExternalLabel* label, | 969 const ExternalLabel* label, |
| 1004 PcDescriptors::Kind kind) { | 970 PcDescriptors::Kind kind) { |
| 1005 ASSERT(frame_register_allocator()->IsSpilled()); | 971 ASSERT(frame_register_allocator()->IsSpilled()); |
| 1006 __ call(label); | 972 __ call(label); |
| 1007 AddCurrentDescriptor(kind, AstNode::kNoId, token_pos, try_index); | 973 AddCurrentDescriptor(kind, AstNode::kNoId, token_pos, try_index); |
| 1008 } | 974 } |
| 1009 | 975 |
| 1010 | 976 |
| 1011 void FlowGraphCompiler::GenerateCallRuntime(intptr_t cid, | 977 void FlowGraphCompiler::GenerateCallRuntime(intptr_t cid, |
| 1012 intptr_t token_pos, | 978 intptr_t token_pos, |
| 1013 intptr_t try_index, | 979 intptr_t try_index, |
| 1014 const RuntimeEntry& entry) { | 980 const RuntimeEntry& entry) { |
| 1015 ASSERT(frame_register_allocator()->IsSpilled()); | 981 ASSERT(frame_register_allocator()->IsSpilled()); |
| 1016 __ CallRuntime(entry); | 982 __ CallRuntime(entry); |
| 1017 AddCurrentDescriptor(PcDescriptors::kOther, cid, token_pos, try_index); | 983 AddCurrentDescriptor(PcDescriptors::kOther, cid, token_pos, try_index); |
| 1018 } | 984 } |
| 1019 | 985 |
| 1020 | 986 |
| 987 intptr_t FlowGraphCompiler::EmitInstanceCall(ExternalLabel* target_label, |
| 988 const ICData& ic_data, |
| 989 const Array& arguments_descriptor, |
| 990 intptr_t argument_count) { |
| 991 __ LoadObject(RBX, ic_data); |
| 992 __ LoadObject(R10, arguments_descriptor); |
| 993 |
| 994 __ call(target_label); |
| 995 const intptr_t descr_offset = assembler()->CodeSize(); |
| 996 __ Drop(argument_count); |
| 997 return descr_offset; |
| 998 } |
| 999 |
| 1000 |
| 1001 intptr_t FlowGraphCompiler::EmitStaticCall(const Function& function, |
| 1002 const Array& arguments_descriptor, |
| 1003 intptr_t argument_count) { |
| 1004 __ LoadObject(RBX, function); |
| 1005 __ LoadObject(R10, arguments_descriptor); |
| 1006 __ call(&StubCode::CallStaticFunctionLabel()); |
| 1007 const intptr_t descr_offset = assembler()->CodeSize(); |
| 1008 __ Drop(argument_count); |
| 1009 return descr_offset; |
| 1010 } |
| 1011 |
| 1012 |
| 1021 // Checks class id of instance against all 'class_ids'. Jump to 'deopt' label | 1013 // Checks class id of instance against all 'class_ids'. Jump to 'deopt' label |
| 1022 // if no match or instance is Smi. | 1014 // if no match or instance is Smi. |
| 1023 void FlowGraphCompiler::EmitClassChecksNoSmi(const ICData& ic_data, | 1015 void FlowGraphCompiler::EmitClassChecksNoSmi(const ICData& ic_data, |
| 1024 Register instance_reg, | 1016 Register instance_reg, |
| 1025 Register temp_reg, | 1017 Register temp_reg, |
| 1026 Label* deopt) { | 1018 Label* deopt) { |
| 1027 Label ok; | 1019 Label ok; |
| 1028 ASSERT(ic_data.GetReceiverClassIdAt(0) != kSmi); | 1020 ASSERT(ic_data.GetReceiverClassIdAt(0) != kSmi); |
| 1029 __ testq(instance_reg, Immediate(kSmiTagMask)); | 1021 __ testq(instance_reg, Immediate(kSmiTagMask)); |
| 1030 __ j(ZERO, deopt); | 1022 __ j(ZERO, deopt); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1065 __ cvtsi2sd(result, temp); | 1057 __ cvtsi2sd(result, temp); |
| 1066 __ Bind(&done); | 1058 __ Bind(&done); |
| 1067 } | 1059 } |
| 1068 | 1060 |
| 1069 | 1061 |
| 1070 #undef __ | 1062 #undef __ |
| 1071 | 1063 |
| 1072 } // namespace dart | 1064 } // namespace dart |
| 1073 | 1065 |
| 1074 #endif // defined TARGET_ARCH_X64 | 1066 #endif // defined TARGET_ARCH_X64 |
| OLD | NEW |