| 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 "vm/ast_printer.h" | 11 #include "vm/ast_printer.h" |
| 11 #include "vm/code_descriptors.h" | 12 #include "vm/code_descriptors.h" |
| 12 #include "vm/code_generator.h" | 13 #include "vm/code_generator.h" |
| 13 #include "vm/disassembler.h" | 14 #include "vm/disassembler.h" |
| 14 #include "vm/longjump.h" | 15 #include "vm/longjump.h" |
| 15 #include "vm/object_store.h" | 16 #include "vm/object_store.h" |
| 16 #include "vm/parser.h" | 17 #include "vm/parser.h" |
| 17 #include "vm/stub_code.h" | 18 #include "vm/stub_code.h" |
| 18 | 19 |
| 19 namespace dart { | 20 namespace dart { |
| 20 | 21 |
| 22 DECLARE_FLAG(bool, enable_type_checks); |
| 21 DECLARE_FLAG(bool, print_ast); | 23 DECLARE_FLAG(bool, print_ast); |
| 22 DECLARE_FLAG(bool, print_scopes); | 24 DECLARE_FLAG(bool, print_scopes); |
| 23 DECLARE_FLAG(bool, trace_functions); | 25 DECLARE_FLAG(bool, trace_functions); |
| 24 | 26 |
| 25 FlowGraphCompiler::FlowGraphCompiler( | 27 FlowGraphCompiler::FlowGraphCompiler( |
| 26 Assembler* assembler, | 28 Assembler* assembler, |
| 27 const ParsedFunction& parsed_function, | 29 const ParsedFunction& parsed_function, |
| 28 const GrowableArray<BlockEntryInstr*>& block_order) | 30 const GrowableArray<BlockEntryInstr*>& block_order) |
| 29 : FlowGraphVisitor(block_order), | 31 : FlowGraphVisitor(block_order), |
| 30 assembler_(assembler), | 32 assembler_(assembler), |
| (...skipping 29 matching lines...) Expand all Loading... |
| 60 const char* function_name = parsed_function_.function().ToCString(); | 62 const char* function_name = parsed_function_.function().ToCString(); |
| 61 intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name, reason) + 1; | 63 intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name, reason) + 1; |
| 62 char* chars = reinterpret_cast<char*>( | 64 char* chars = reinterpret_cast<char*>( |
| 63 Isolate::Current()->current_zone()->Allocate(len)); | 65 Isolate::Current()->current_zone()->Allocate(len)); |
| 64 OS::SNPrint(chars, len, kFormat, function_name, reason); | 66 OS::SNPrint(chars, len, kFormat, function_name, reason); |
| 65 const Error& error = Error::Handle( | 67 const Error& error = Error::Handle( |
| 66 LanguageError::New(String::Handle(String::New(chars)))); | 68 LanguageError::New(String::Handle(String::New(chars)))); |
| 67 Isolate::Current()->long_jump_base()->Jump(1, error); | 69 Isolate::Current()->long_jump_base()->Jump(1, error); |
| 68 } | 70 } |
| 69 | 71 |
| 72 |
| 73 static const Class* CoreClass(const char* c_name) { |
| 74 const String& class_name = String::Handle(String::NewSymbol(c_name)); |
| 75 const Class& cls = Class::ZoneHandle(Library::Handle( |
| 76 Library::CoreImplLibrary()).LookupClass(class_name)); |
| 77 ASSERT(!cls.IsNull()); |
| 78 return &cls; |
| 79 } |
| 80 |
| 81 |
| 70 #define __ assembler_-> | 82 #define __ assembler_-> |
| 71 | 83 |
| 72 | 84 |
| 85 // Inputs: |
| 86 // - RAX: object (preserved). |
| 87 // - RDX: optional instantiator type arguments (preserved). |
| 88 // Destroys RCX. |
| 89 // Returns: |
| 90 // - unchanged object in RAX and optional instantiator type arguments in RDX. |
| 91 // Note that this inlined code must be followed by the runtime_call code, as it |
| 92 // may fall through to it. Otherwise, this inline code will jump to the label |
| 93 // is_instance or to the label is_not_instance. |
| 94 void FlowGraphCompiler::GenerateInlineInstanceof(const AbstractType& type, |
| 95 Label* is_instance, |
| 96 Label* is_not_instance) { |
| 97 Label runtime_call; |
| 98 if (type.IsInstantiated()) { |
| 99 const Class& type_class = Class::ZoneHandle(type.type_class()); |
| 100 const bool requires_type_arguments = type_class.HasTypeArguments(); |
| 101 // A Smi object cannot be the instance of a parameterized class. |
| 102 // A class equality check is only applicable with a dst type of a |
| 103 // non-parameterized class or with a raw dst type of a parameterized class. |
| 104 if (requires_type_arguments) { |
| 105 const AbstractTypeArguments& type_arguments = |
| 106 AbstractTypeArguments::Handle(type.arguments()); |
| 107 const bool is_raw_type = type_arguments.IsNull() || |
| 108 type_arguments.IsRaw(type_arguments.Length()); |
| 109 __ testq(RAX, Immediate(kSmiTagMask)); |
| 110 __ j(ZERO, &runtime_call); |
| 111 // Object not Smi. |
| 112 if (is_raw_type) { |
| 113 // Dynamic type argument, check only classes. |
| 114 if (type.IsListInterface()) { |
| 115 // TODO(srdjan) also accept List<Object>. |
| 116 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); |
| 117 __ CompareObject(RCX, *CoreClass("ObjectArray")); |
| 118 __ j(EQUAL, is_instance); |
| 119 __ CompareObject(RCX, *CoreClass("GrowableObjectArray")); |
| 120 __ j(EQUAL, is_instance); |
| 121 } else if (!type_class.is_interface()) { |
| 122 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); |
| 123 __ CompareObject(RCX, type_class); |
| 124 __ j(EQUAL, is_instance); |
| 125 } |
| 126 // Fall through to runtime class. |
| 127 } |
| 128 } else { // type has NO type arguments. |
| 129 Label compare_classes; |
| 130 __ testq(RAX, Immediate(kSmiTagMask)); |
| 131 __ j(NOT_ZERO, &compare_classes); |
| 132 // Object is Smi. |
| 133 const Class& smi_class = Class::Handle(Smi::Class()); |
| 134 // TODO(regis): We should introduce a SmiType. |
| 135 Error& malformed_error = Error::Handle(); |
| 136 if (smi_class.IsSubtypeOf(TypeArguments::Handle(), |
| 137 type_class, |
| 138 TypeArguments::Handle(), |
| 139 &malformed_error)) { |
| 140 // Successful assignable type check: return object in RAX. |
| 141 __ jmp(is_instance); |
| 142 } else { |
| 143 // Failed assignable type check: call runtime to throw TypeError. |
| 144 __ jmp(&runtime_call); |
| 145 } |
| 146 // Compare if the classes are equal. |
| 147 __ Bind(&compare_classes); |
| 148 // If type is an interface, we can skip the class equality check, |
| 149 // because instances cannot be of an interface type. |
| 150 if (!type_class.is_interface()) { |
| 151 __ LoadObject(RCX, type_class); |
| 152 __ movq(R10, FieldAddress(RAX, Object::class_offset())); |
| 153 __ cmpq(R10, RCX); |
| 154 __ j(EQUAL, is_instance); |
| 155 // RAX, RCX, and RDX are preserved in stub, result is in RBX. |
| 156 __ call(&StubCode::IsRawSubTypeLabel()); |
| 157 // Result in RBX: 1 is raw subtype. |
| 158 __ cmpq(RBX, Immediate(1)); |
| 159 __ j(EQUAL, is_instance); |
| 160 // Otherwise fall through to runtime call. |
| 161 } else { |
| 162 // However, for specific core library interfaces, we can check for |
| 163 // specific core library classes. |
| 164 Error& malformed_error = Error::Handle(); |
| 165 if (type.IsBoolInterface()) { |
| 166 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); |
| 167 const Class& bool_class = Class::ZoneHandle( |
| 168 Isolate::Current()->object_store()->bool_class()); |
| 169 __ CompareObject(RCX, bool_class); |
| 170 __ j(EQUAL, is_instance); |
| 171 } else if (type.IsSubtypeOf( |
| 172 Type::Handle(Type::NumberInterface()), &malformed_error)) { |
| 173 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); |
| 174 if (type.IsIntInterface() || type.IsNumberInterface()) { |
| 175 // We already checked for Smi above. |
| 176 const Class& mint_class = Class::ZoneHandle( |
| 177 Isolate::Current()->object_store()->mint_class()); |
| 178 __ CompareObject(RCX, mint_class); |
| 179 __ j(EQUAL, is_instance); |
| 180 const Class& bigint_class = Class::ZoneHandle( |
| 181 Isolate::Current()->object_store()->bigint_class()); |
| 182 __ CompareObject(RCX, bigint_class); |
| 183 __ j(EQUAL, is_instance); |
| 184 } |
| 185 if (type.IsDoubleInterface() || type.IsNumberInterface()) { |
| 186 const Class& double_class = Class::ZoneHandle( |
| 187 Isolate::Current()->object_store()->double_class()); |
| 188 __ CompareObject(RCX, double_class); |
| 189 __ j(EQUAL, is_instance); |
| 190 } |
| 191 } else if (type.IsStringInterface()) { |
| 192 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); |
| 193 const Class& one_byte_string_class = Class::ZoneHandle( |
| 194 Isolate::Current()->object_store()->one_byte_string_class()); |
| 195 __ CompareObject(RCX, one_byte_string_class); |
| 196 __ j(EQUAL, is_instance); |
| 197 const Class& two_byte_string_class = Class::ZoneHandle( |
| 198 Isolate::Current()->object_store()->two_byte_string_class()); |
| 199 __ CompareObject(RCX, two_byte_string_class); |
| 200 __ j(EQUAL, is_instance); |
| 201 const Class& four_byte_string_class = Class::ZoneHandle( |
| 202 Isolate::Current()->object_store()->four_byte_string_class()); |
| 203 __ CompareObject(RCX, four_byte_string_class); |
| 204 __ j(EQUAL, is_instance); |
| 205 } else if (type.IsFunctionInterface()) { |
| 206 const Immediate raw_null = |
| 207 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 208 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); |
| 209 __ movq(RCX, FieldAddress(RCX, Class::signature_function_offset())); |
| 210 __ cmpq(RCX, raw_null); |
| 211 __ j(NOT_EQUAL, is_instance); |
| 212 } else { |
| 213 __ LoadObject(RCX, type_class); |
| 214 // RAX: Instance (preserved). |
| 215 // RCX: test class (preserved). |
| 216 // RDX: instantiator type arguments (preserved). |
| 217 __ call(&StubCode::IsRawSubTypeLabel()); |
| 218 // Result in RBX: 1 is raw subtype. |
| 219 __ cmpq(RBX, Immediate(1)); |
| 220 __ j(EQUAL, is_instance); |
| 221 // Otherwise fallthrough to runtime call. |
| 222 } |
| 223 } |
| 224 } |
| 225 } else { |
| 226 ASSERT(!type.IsInstantiated()); |
| 227 // Skip check if destination is a dynamic type. |
| 228 if (type.IsTypeParameter()) { |
| 229 // Check if dynamic. |
| 230 const Immediate raw_null = |
| 231 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 232 // Instantiator type arguments are in RDX. |
| 233 __ cmpq(RDX, raw_null); |
| 234 __ j(EQUAL, is_instance); |
| 235 |
| 236 // For now handle only TypeArguments and bail out if InstantiatedTypeArgs. |
| 237 __ movq(RCX, FieldAddress(RDX, Object::class_offset())); |
| 238 __ CompareObject(RCX, Object::ZoneHandle(Object::type_arguments_class())); |
| 239 __ j(NOT_EQUAL, &runtime_call); |
| 240 __ movq(RCX, |
| 241 FieldAddress(RDX, TypeArguments::type_at_offset(type.Index()))); |
| 242 // RCX: instantiated type parameter. |
| 243 __ CompareObject(RCX, Type::ZoneHandle(Type::DynamicType())); |
| 244 __ j(EQUAL, is_instance); |
| 245 // Check if the type has type parameters, if not, do the class comparison. |
| 246 Label not_smi; |
| 247 __ testq(RAX, Immediate(kSmiTagMask)); // Value is Smi? |
| 248 __ j(NOT_ZERO, ¬_smi, Assembler::kNearJump); |
| 249 __ CompareObject(RCX, Type::ZoneHandle(Type::IntInterface())); |
| 250 __ j(EQUAL, is_instance); |
| 251 __ CompareObject(RCX, Type::ZoneHandle(Type::NumberInterface())); |
| 252 __ j(EQUAL, is_instance); |
| 253 __ Bind(¬_smi); |
| 254 // The instantiated type parameter RCX may not be a Type, but could be an |
| 255 // InstantiatedType. It is therefore necessary to check its class. |
| 256 __ movq(R10, FieldAddress(RCX, Object::class_offset())); |
| 257 __ CompareObject(R10, Object::ZoneHandle(Object::type_class())); |
| 258 __ j(NOT_EQUAL, &runtime_call, Assembler::kNearJump); |
| 259 __ movq(RCX, FieldAddress(RCX, Type::type_class_offset())); |
| 260 __ movq(R10, FieldAddress(RCX, Class::type_parameters_offset())); |
| 261 // Check that class of type has no type parameters. |
| 262 __ cmpq(R10, raw_null); |
| 263 __ j(NOT_EQUAL, &runtime_call, Assembler::kNearJump); |
| 264 // We have a non-parameterized class in RCX, compare with class of |
| 265 // value in RAX. RAX, RCX, and RDX are preserved in stub. |
| 266 __ call(&StubCode::IsRawSubTypeLabel()); |
| 267 // Result in EBX: 1 is raw subtype. |
| 268 __ cmpq(RBX, Immediate(1)); |
| 269 __ j(EQUAL, is_instance); |
| 270 // Fall through to runtime call. |
| 271 } |
| 272 } |
| 273 __ Bind(&runtime_call); |
| 274 } |
| 275 |
| 276 |
| 277 // Optimize assignable type check by adding inlined tests for: |
| 278 // - NULL -> return NULL. |
| 279 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
| 280 // - Class equality (only if class is not parameterized). |
| 281 // Inputs: |
| 282 // - RAX: object. |
| 283 // - RDX: optional instantiator type arguments. |
| 284 // Destroys RCX and RDX. |
| 285 // Returns: |
| 286 // - object in RAX for successful assignable check (or throws TypeError). |
| 287 // Performance notes: positive checks must be quick, negative checks can be slow |
| 288 // as they throw an exception. |
| 73 void FlowGraphCompiler::GenerateAssertAssignable(intptr_t node_id, | 289 void FlowGraphCompiler::GenerateAssertAssignable(intptr_t node_id, |
| 74 intptr_t token_index, | 290 intptr_t token_index, |
| 291 intptr_t try_index, |
| 292 Value* value, |
| 75 const AbstractType& dst_type, | 293 const AbstractType& dst_type, |
| 76 const String& dst_name) { | 294 const String& dst_name) { |
| 77 Bailout("GenerateAssertAssignable"); | 295 ASSERT(FLAG_enable_type_checks); |
| 296 ASSERT(token_index >= 0); |
| 297 ASSERT(!dst_type.IsNull()); |
| 298 ASSERT(dst_type.IsFinalized()); |
| 299 |
| 300 // Any expression is assignable to the Dynamic type and to the Object type. |
| 301 // Skip the test. |
| 302 if (!dst_type.IsMalformed() && |
| 303 (dst_type.IsDynamicType() || dst_type.IsObjectType())) { |
| 304 return; |
| 305 } |
| 306 |
| 307 // It is a compile-time error to explicitly return a value (including null) |
| 308 // from a void function. However, functions that do not explicitly return a |
| 309 // value, implicitly return null. This includes void functions. Therefore, we |
| 310 // skip the type test here and trust the parser to only return null in void |
| 311 // function. |
| 312 if (dst_type.IsVoidType()) { |
| 313 return; |
| 314 } |
| 315 |
| 316 // TODO(regis): Move this compile time check to the graph builder. |
| 317 // Eliminate the test if it can be performed successfully at compile time. |
| 318 if ((value != NULL) && value->IsConstant()) { |
| 319 Instance& literal_value = Instance::Handle(); |
| 320 literal_value ^= value->AsConstant()->value().raw(); |
| 321 const Class& cls = Class::Handle(literal_value.clazz()); |
| 322 if (cls.IsNullClass()) { |
| 323 ASSERT(literal_value.IsNull() || |
| 324 (literal_value.raw() == Object::sentinel()) || |
| 325 (literal_value.raw() == Object::transition_sentinel())); |
| 326 return; |
| 327 } |
| 328 Error& malformed_error = Error::Handle(); |
| 329 if (!dst_type.IsMalformed() && |
| 330 dst_type.IsInstantiated() && |
| 331 literal_value.IsInstanceOf(dst_type, |
| 332 TypeArguments::Handle(), |
| 333 &malformed_error)) { |
| 334 return; |
| 335 } |
| 336 } |
| 337 |
| 338 // A null object is always assignable and is returned as result. |
| 339 const Immediate raw_null = |
| 340 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 341 Label is_assignable, runtime_call; |
| 342 __ cmpq(RAX, raw_null); |
| 343 __ j(EQUAL, &is_assignable); |
| 344 |
| 345 // Generate throw new TypeError() if the type is malformed. |
| 346 if (dst_type.IsMalformed()) { |
| 347 const Error& error = Error::Handle(dst_type.malformed_error()); |
| 348 const String& error_message = String::ZoneHandle( |
| 349 String::NewSymbol(error.ToErrorCString())); |
| 350 __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| 351 const Immediate location = |
| 352 Immediate(reinterpret_cast<int64_t>(Smi::New(token_index))); |
| 353 __ pushq(location); // Push the source location. |
| 354 __ pushq(RAX); // Push the source object. |
| 355 __ PushObject(dst_name); // Push the name of the destination. |
| 356 __ PushObject(error_message); |
| 357 GenerateCallRuntime(node_id, |
| 358 token_index, |
| 359 try_index, |
| 360 kMalformedTypeErrorRuntimeEntry); |
| 361 // We should never return here. |
| 362 __ int3(); |
| 363 |
| 364 __ Bind(&is_assignable); // For a null object. |
| 365 return; |
| 366 } |
| 367 |
| 368 // Generate inline type check, linking to runtime call if not assignable. |
| 369 GenerateInlineInstanceof(dst_type, &is_assignable, &runtime_call); |
| 370 |
| 371 __ Bind(&runtime_call); |
| 372 __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| 373 const Immediate location = |
| 374 Immediate(reinterpret_cast<int64_t>(Smi::New(token_index))); |
| 375 __ pushq(location); // Push the source location. |
| 376 __ pushq(RAX); // Push the source object. |
| 377 __ PushObject(dst_type); // Push the type of the destination. |
| 378 if (!dst_type.IsInstantiated()) { |
| 379 __ pushq(RDX); // Instantiator type arguments. |
| 380 } else { |
| 381 __ pushq(raw_null); // Null instantiator. |
| 382 } |
| 383 __ PushObject(dst_name); // Push the name of the destination. |
| 384 GenerateCallRuntime(node_id, |
| 385 token_index, |
| 386 try_index, |
| 387 kTypeCheckRuntimeEntry); |
| 388 // Pop the parameters supplied to the runtime entry. The result of the |
| 389 // type check runtime call is the checked value. |
| 390 __ addq(RSP, Immediate(5 * kWordSize)); |
| 391 __ popq(RAX); |
| 392 |
| 393 __ Bind(&is_assignable); |
| 78 } | 394 } |
| 79 | 395 |
| 80 | 396 |
| 81 void FlowGraphCompiler::LoadValue(Register dst, Value* value) { | 397 void FlowGraphCompiler::LoadValue(Register dst, Value* value) { |
| 82 if (value->IsConstant()) { | 398 if (value->IsConstant()) { |
| 83 ConstantVal* constant = value->AsConstant(); | 399 ConstantVal* constant = value->AsConstant(); |
| 84 if (constant->value().IsSmi()) { | 400 if (constant->value().IsSmi()) { |
| 85 int64_t imm = reinterpret_cast<int64_t>(constant->value().raw()); | 401 int64_t imm = reinterpret_cast<int64_t>(constant->value().raw()); |
| 86 __ movq(dst, Immediate(imm)); | 402 __ movq(dst, Immediate(imm)); |
| 87 } else { | 403 } else { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 98 LoadValue(RAX, val); | 414 LoadValue(RAX, val); |
| 99 } | 415 } |
| 100 | 416 |
| 101 | 417 |
| 102 void FlowGraphCompiler::VisitConstant(ConstantVal* val) { | 418 void FlowGraphCompiler::VisitConstant(ConstantVal* val) { |
| 103 LoadValue(RAX, val); | 419 LoadValue(RAX, val); |
| 104 } | 420 } |
| 105 | 421 |
| 106 | 422 |
| 107 void FlowGraphCompiler::VisitAssertAssignable(AssertAssignableComp* comp) { | 423 void FlowGraphCompiler::VisitAssertAssignable(AssertAssignableComp* comp) { |
| 108 Bailout("AssertAssignableComp"); | 424 if (comp->type_arguments() != NULL) { |
| 425 __ popq(RDX); |
| 426 } |
| 427 LoadValue(RAX, comp->value()); |
| 428 GenerateAssertAssignable(comp->node_id(), |
| 429 comp->token_index(), |
| 430 comp->try_index(), |
| 431 comp->value(), |
| 432 comp->dst_type(), |
| 433 comp->dst_name()); |
| 434 } |
| 435 |
| 436 |
| 437 void FlowGraphCompiler::VisitAssertBoolean(AssertBooleanComp* comp) { |
| 438 LoadValue(RAX, comp->value()); |
| 439 // Check that the type of the value is allowed in conditional context. |
| 440 // Call the runtime if the object is not bool::true or bool::false. |
| 441 Label done; |
| 442 __ popq(RAX); |
| 443 __ CompareObject(RAX, Bool::ZoneHandle(Bool::True())); |
| 444 __ j(EQUAL, &done, Assembler::kNearJump); |
| 445 __ CompareObject(RAX, Bool::ZoneHandle(Bool::False())); |
| 446 __ j(EQUAL, &done, Assembler::kNearJump); |
| 447 |
| 448 const Immediate location = |
| 449 Immediate(reinterpret_cast<int64_t>(Smi::New(comp->token_index()))); |
| 450 __ pushq(location); // Push the source location. |
| 451 __ pushq(RAX); // Push the source object. |
| 452 GenerateCallRuntime(comp->node_id(), |
| 453 comp->token_index(), |
| 454 comp->try_index(), |
| 455 kConditionTypeErrorRuntimeEntry); |
| 456 // We should never return here. |
| 457 __ int3(); |
| 458 |
| 459 __ Bind(&done); |
| 109 } | 460 } |
| 110 | 461 |
| 111 | 462 |
| 112 // True iff. the arguments to a call will be properly pushed and can | 463 // True iff. the arguments to a call will be properly pushed and can |
| 113 // be popped after the call. | 464 // be popped after the call. |
| 114 template <typename T> static bool VerifyCallComputation(T* comp) { | 465 template <typename T> static bool VerifyCallComputation(T* comp) { |
| 115 // Argument values should be consecutive temps. | 466 // Argument values should be consecutive temps. |
| 116 // | 467 // |
| 117 // TODO(kmillikin): implement stack height tracking so we can also assert | 468 // TODO(kmillikin): implement stack height tracking so we can also assert |
| 118 // they are on top of the stack. | 469 // they are on top of the stack. |
| (...skipping 307 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 426 Label done; | 777 Label done; |
| 427 LoadValue(RDX, comp->value()); | 778 LoadValue(RDX, comp->value()); |
| 428 __ LoadObject(RAX, bool_true); | 779 __ LoadObject(RAX, bool_true); |
| 429 __ cmpq(RAX, RDX); | 780 __ cmpq(RAX, RDX); |
| 430 __ j(NOT_EQUAL, &done, Assembler::kNearJump); | 781 __ j(NOT_EQUAL, &done, Assembler::kNearJump); |
| 431 __ LoadObject(RAX, bool_false); | 782 __ LoadObject(RAX, bool_false); |
| 432 __ Bind(&done); | 783 __ Bind(&done); |
| 433 } | 784 } |
| 434 | 785 |
| 435 | 786 |
| 436 static const Class* CoreClass(const char* c_name) { | 787 // Optimize instanceof type test by adding inlined tests for: |
| 437 const String& class_name = String::Handle(String::NewSymbol(c_name)); | |
| 438 const Class& cls = Class::ZoneHandle(Library::Handle( | |
| 439 Library::CoreImplLibrary()).LookupClass(class_name)); | |
| 440 ASSERT(!cls.IsNull()); | |
| 441 return &cls; | |
| 442 } | |
| 443 | |
| 444 | |
| 445 // Copied from CodeGenerator. | |
| 446 // If instanceof type test cannot be performed successfully at compile time and | |
| 447 // therefore eliminated, optimize it by adding inlined tests for: | |
| 448 // - NULL -> return false. | 788 // - NULL -> return false. |
| 449 // - Smi -> compile time subtype check (only if dst class is not parameterized). | 789 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
| 450 // - Class equality (only if class is not parameterized). | 790 // - Class equality (only if class is not parameterized). |
| 451 // Inputs: | 791 // Inputs: |
| 452 // - RAX: object. | 792 // - RAX: object. |
| 453 // - RDX: optional type-arguments. | 793 // - RDX: optional instantiator type arguments. |
| 454 // Destroys RCX. | 794 // Destroys RCX and RDX. |
| 455 // Returns: | 795 // Returns: |
| 456 // - true or false in RAX. | 796 // - true or false in RAX. |
| 457 void FlowGraphCompiler::GenerateInstanceOf(intptr_t node_id, | 797 void FlowGraphCompiler::GenerateInstanceOf(intptr_t node_id, |
| 458 intptr_t token_index, | 798 intptr_t token_index, |
| 459 intptr_t try_index, | 799 intptr_t try_index, |
| 460 Value* value, | 800 Value* value, |
| 461 const AbstractType& type, | 801 const AbstractType& type, |
| 462 bool negate_result) { | 802 bool negate_result) { |
| 463 ASSERT(type.IsFinalized() && !type.IsMalformed()); | 803 ASSERT(type.IsFinalized() && !type.IsMalformed()); |
| 464 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | 804 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); |
| 465 const Bool& bool_false = Bool::ZoneHandle(Bool::False()); | 805 const Bool& bool_false = Bool::ZoneHandle(Bool::False()); |
| 466 | 806 |
| 467 const Immediate raw_null = | 807 const Immediate raw_null = |
| 468 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 808 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 469 Label done; | 809 Label is_instance, is_not_instance; |
| 470 // If type is instantiated and non-parameterized, we can inline code | 810 // If type is instantiated and non-parameterized, we can inline code |
| 471 // checking whether the tested instance is a Smi. | 811 // checking whether the tested instance is a Smi. |
| 472 if (type.IsInstantiated()) { | 812 if (type.IsInstantiated()) { |
| 473 // A null object is only an instance of Object and Dynamic, which has | 813 // A null object is only an instance of Object and Dynamic, which has |
| 474 // already been checked above (if the type is instantiated). So we can | 814 // already been checked above (if the type is instantiated). So we can |
| 475 // return false here if the instance is null (and if the type is | 815 // return false here if the instance is null (and if the type is |
| 476 // instantiated). | 816 // instantiated). |
| 477 // We can only inline this null check if the type is instantiated at compile | 817 // We can only inline this null check if the type is instantiated at compile |
| 478 // time, since an uninstantiated type at compile time could be Object or | 818 // time, since an uninstantiated type at compile time could be Object or |
| 479 // Dynamic at run time. | 819 // Dynamic at run time. |
| 480 Label non_null; | |
| 481 __ cmpq(RAX, raw_null); | 820 __ cmpq(RAX, raw_null); |
| 482 __ j(NOT_EQUAL, &non_null, Assembler::kNearJump); | 821 __ j(EQUAL, &is_not_instance); |
| 483 __ PushObject(negate_result ? bool_true : bool_false); | 822 } |
| 484 __ jmp(&done); | |
| 485 | 823 |
| 486 __ Bind(&non_null); | 824 // Generate inline instanceof test. |
| 825 GenerateInlineInstanceof(type, &is_instance, &is_not_instance); |
| 487 | 826 |
| 488 const Class& type_class = Class::ZoneHandle(type.type_class()); | 827 // Generate runtime call. |
| 489 const bool requires_type_arguments = type_class.HasTypeArguments(); | |
| 490 // A Smi object cannot be the instance of a parameterized class. | |
| 491 // A class equality check is only applicable with a dst type of a | |
| 492 // non-parameterized class or with a raw dst type of a parameterized class. | |
| 493 if (requires_type_arguments) { | |
| 494 const AbstractTypeArguments& type_arguments = | |
| 495 AbstractTypeArguments::Handle(type.arguments()); | |
| 496 const bool is_raw_type = type_arguments.IsNull() || | |
| 497 type_arguments.IsRaw(type_arguments.Length()); | |
| 498 Label runtime_call; | |
| 499 __ testq(RAX, Immediate(kSmiTagMask)); | |
| 500 __ j(ZERO, &runtime_call, Assembler::kNearJump); | |
| 501 // Object not Smi. | |
| 502 if (is_raw_type) { | |
| 503 if (type.IsListInterface()) { | |
| 504 Label push_result; | |
| 505 // TODO(srdjan) also accept List<Object>. | |
| 506 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); | |
| 507 __ CompareObject(RCX, *CoreClass("ObjectArray")); | |
| 508 __ j(EQUAL, &push_result, Assembler::kNearJump); | |
| 509 __ CompareObject(RCX, *CoreClass("GrowableObjectArray")); | |
| 510 __ j(NOT_EQUAL, &runtime_call, Assembler::kNearJump); | |
| 511 __ Bind(&push_result); | |
| 512 __ PushObject(negate_result ? bool_false : bool_true); | |
| 513 __ jmp(&done); | |
| 514 } else if (!type_class.is_interface()) { | |
| 515 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); | |
| 516 __ CompareObject(RCX, type_class); | |
| 517 __ j(NOT_EQUAL, &runtime_call, Assembler::kNearJump); | |
| 518 __ PushObject(negate_result ? bool_false : bool_true); | |
| 519 __ jmp(&done); | |
| 520 } | |
| 521 } | |
| 522 __ Bind(&runtime_call); | |
| 523 // Fall through to runtime call. | |
| 524 } else { | |
| 525 ASSERT(!requires_type_arguments); | |
| 526 // Test if object is Smi and for a couple known test-classes. | |
| 527 Label compare_classes; | |
| 528 __ testq(RAX, Immediate(kSmiTagMask)); | |
| 529 __ j(NOT_ZERO, &compare_classes, Assembler::kNearJump); | |
| 530 // Object is Smi. | |
| 531 const Class& smi_class = Class::Handle(Smi::Class()); | |
| 532 // TODO(regis): We should introduce a SmiType. | |
| 533 Error& malformed_error = Error::Handle(); | |
| 534 if (smi_class.IsSubtypeOf(TypeArguments::Handle(), | |
| 535 type_class, | |
| 536 TypeArguments::Handle(), | |
| 537 &malformed_error)) { | |
| 538 __ PushObject(negate_result ? bool_false : bool_true); | |
| 539 } else { | |
| 540 __ PushObject(negate_result ? bool_true : bool_false); | |
| 541 } | |
| 542 __ jmp(&done); | |
| 543 | |
| 544 // Compare if the classes are equal. | |
| 545 __ Bind(&compare_classes); | |
| 546 const Class* compare_class = NULL; | |
| 547 if (type.IsStringInterface()) { | |
| 548 compare_class = &Class::ZoneHandle( | |
| 549 Isolate::Current()->object_store()->one_byte_string_class()); | |
| 550 } else if (type.IsBoolInterface()) { | |
| 551 compare_class = &Class::ZoneHandle( | |
| 552 Isolate::Current()->object_store()->bool_class()); | |
| 553 } else if (!type_class.is_interface()) { | |
| 554 compare_class = &type_class; | |
| 555 } | |
| 556 if (compare_class != NULL) { | |
| 557 Label runtime_call; | |
| 558 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); | |
| 559 __ CompareObject(RCX, *compare_class); | |
| 560 __ j(NOT_EQUAL, &runtime_call, Assembler::kNearJump); | |
| 561 __ PushObject(negate_result ? bool_false : bool_true); | |
| 562 __ jmp(&done, Assembler::kNearJump); | |
| 563 __ Bind(&runtime_call); | |
| 564 } | |
| 565 } | |
| 566 } | |
| 567 __ PushObject(Object::ZoneHandle()); // Make room for the result. | 828 __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| 568 const Immediate location = | 829 const Immediate location = |
| 569 Immediate(reinterpret_cast<int64_t>(Smi::New(token_index))); | 830 Immediate(reinterpret_cast<int64_t>(Smi::New(token_index))); |
| 570 const Immediate node_id_as_smi = | 831 const Immediate node_id_as_smi = |
| 571 Immediate(reinterpret_cast<int64_t>(Smi::New(node_id))); | 832 Immediate(reinterpret_cast<int64_t>(Smi::New(node_id))); |
| 572 __ pushq(location); // Push the source location. | 833 __ pushq(location); // Push the source location. |
| 573 __ pushq(node_id_as_smi); | 834 __ pushq(node_id_as_smi); |
| 574 __ pushq(RAX); // Push the instance. | 835 __ pushq(RAX); // Push the instance. |
| 575 __ PushObject(type); // Push the type. | 836 __ PushObject(type); // Push the type. |
| 576 if (!type.IsInstantiated()) { | 837 if (!type.IsInstantiated()) { |
| 577 __ pushq(RDX); // Type arguments. | 838 __ pushq(RDX); // Instantiator type arguments. |
| 578 } else { | 839 } else { |
| 579 __ pushq(raw_null); // Null instantiator. | 840 __ pushq(raw_null); // Null instantiator. |
| 580 } | 841 } |
| 581 GenerateCallRuntime(node_id, token_index, try_index, kInstanceofRuntimeEntry); | 842 GenerateCallRuntime(node_id, token_index, try_index, kInstanceofRuntimeEntry); |
| 582 // Pop the two parameters supplied to the runtime entry. The result of the | 843 // Pop the two parameters supplied to the runtime entry. The result of the |
| 583 // instanceof runtime call will be left as the result of the operation. | 844 // instanceof runtime call will be left as the result of the operation. |
| 584 __ addq(RSP, Immediate(5 * kWordSize)); | 845 __ addq(RSP, Immediate(5 * kWordSize)); |
| 846 Label done; |
| 585 if (negate_result) { | 847 if (negate_result) { |
| 586 Label negate_done; | |
| 587 __ popq(RDX); | 848 __ popq(RDX); |
| 588 __ LoadObject(RAX, bool_true); | 849 __ LoadObject(RAX, bool_true); |
| 589 __ cmpq(RDX, RAX); | 850 __ cmpq(RDX, RAX); |
| 590 __ j(NOT_EQUAL, &negate_done, Assembler::kNearJump); | 851 __ j(NOT_EQUAL, &done, Assembler::kNearJump); |
| 591 __ LoadObject(RAX, bool_false); | 852 __ LoadObject(RAX, bool_false); |
| 592 __ Bind(&negate_done); | 853 } else { |
| 593 __ pushq(RAX); | 854 __ popq(RAX); |
| 594 } | 855 } |
| 856 __ jmp(&done, Assembler::kNearJump); |
| 857 |
| 858 __ Bind(&is_not_instance); |
| 859 __ LoadObject(RAX, negate_result ? bool_true : bool_false); |
| 860 __ jmp(&done, Assembler::kNearJump); |
| 861 |
| 862 __ Bind(&is_instance); |
| 863 __ LoadObject(RAX, negate_result ? bool_false : bool_true); |
| 595 __ Bind(&done); | 864 __ Bind(&done); |
| 596 __ popq(RAX); | |
| 597 } | 865 } |
| 598 | 866 |
| 599 | 867 |
| 600 void FlowGraphCompiler::VisitInstanceOf(InstanceOfComp* comp) { | 868 void FlowGraphCompiler::VisitInstanceOf(InstanceOfComp* comp) { |
| 601 if (comp->type_arguments() != NULL) { | 869 if (comp->type_arguments() != NULL) { |
| 602 __ popq(RDX); | 870 __ popq(RDX); |
| 603 } | 871 } |
| 604 __ popq(RAX); | 872 LoadValue(RAX, comp->value()); |
| 605 GenerateInstanceOf(comp->node_id(), | 873 GenerateInstanceOf(comp->node_id(), |
| 606 comp->token_index(), | 874 comp->token_index(), |
| 607 comp->try_index(), | 875 comp->try_index(), |
| 608 comp->value(), | 876 comp->value(), |
| 609 comp->type(), | 877 comp->type(), |
| 610 comp->negate_result()); | 878 comp->negate_result()); |
| 611 } | 879 } |
| 612 | 880 |
| 613 | 881 |
| 614 void FlowGraphCompiler::VisitAllocateObject(AllocateObjectComp* comp) { | 882 void FlowGraphCompiler::VisitAllocateObject(AllocateObjectComp* comp) { |
| (...skipping 738 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1353 ASSERT(exception_handlers_list_ != NULL); | 1621 ASSERT(exception_handlers_list_ != NULL); |
| 1354 const ExceptionHandlers& handlers = ExceptionHandlers::Handle( | 1622 const ExceptionHandlers& handlers = ExceptionHandlers::Handle( |
| 1355 exception_handlers_list_->FinalizeExceptionHandlers(code.EntryPoint())); | 1623 exception_handlers_list_->FinalizeExceptionHandlers(code.EntryPoint())); |
| 1356 code.set_exception_handlers(handlers); | 1624 code.set_exception_handlers(handlers); |
| 1357 } | 1625 } |
| 1358 | 1626 |
| 1359 | 1627 |
| 1360 } // namespace dart | 1628 } // namespace dart |
| 1361 | 1629 |
| 1362 #endif // defined TARGET_ARCH_X64 | 1630 #endif // defined TARGET_ARCH_X64 |
| OLD | NEW |