Chromium Code Reviews| 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. | |
|
srdjan
2012/04/13 21:47:34
Add comment that it can fall-through or jump to is
regis
2012/04/13 22:05:50
I expanded the comment just above.
| |
| 93 void FlowGraphCompiler::GenerateInlineInstanceof(const AbstractType& type, | |
| 94 Label* is_instance, | |
| 95 Label* is_not_instance) { | |
| 96 Label runtime_call; | |
| 97 if (type.IsInstantiated()) { | |
| 98 const Class& type_class = Class::ZoneHandle(type.type_class()); | |
| 99 const bool requires_type_arguments = type_class.HasTypeArguments(); | |
| 100 // A Smi object cannot be the instance of a parameterized class. | |
| 101 // A class equality check is only applicable with a dst type of a | |
| 102 // non-parameterized class or with a raw dst type of a parameterized class. | |
| 103 if (requires_type_arguments) { | |
| 104 const AbstractTypeArguments& type_arguments = | |
| 105 AbstractTypeArguments::Handle(type.arguments()); | |
| 106 const bool is_raw_type = type_arguments.IsNull() || | |
| 107 type_arguments.IsRaw(type_arguments.Length()); | |
| 108 __ testq(RAX, Immediate(kSmiTagMask)); | |
| 109 __ j(ZERO, &runtime_call); | |
| 110 // Object not Smi. | |
| 111 if (is_raw_type) { | |
| 112 // Dynamic type argument, check only classes. | |
| 113 if (type.IsListInterface()) { | |
| 114 // TODO(srdjan) also accept List<Object>. | |
| 115 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); | |
| 116 __ CompareObject(RCX, *CoreClass("ObjectArray")); | |
| 117 __ j(EQUAL, is_instance); | |
| 118 __ CompareObject(RCX, *CoreClass("GrowableObjectArray")); | |
| 119 __ j(EQUAL, is_instance); | |
| 120 } else if (!type_class.is_interface()) { | |
| 121 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); | |
| 122 __ CompareObject(RCX, type_class); | |
| 123 __ j(EQUAL, is_instance); | |
| 124 } | |
| 125 // Fall through to runtime class. | |
| 126 } | |
| 127 } else { // type has NO type arguments. | |
| 128 Label compare_classes; | |
| 129 __ testq(RAX, Immediate(kSmiTagMask)); | |
| 130 __ j(NOT_ZERO, &compare_classes); | |
| 131 // Object is Smi. | |
| 132 const Class& smi_class = Class::Handle(Smi::Class()); | |
| 133 // TODO(regis): We should introduce a SmiType. | |
| 134 Error& malformed_error = Error::Handle(); | |
| 135 if (smi_class.IsSubtypeOf(TypeArguments::Handle(), | |
| 136 type_class, | |
| 137 TypeArguments::Handle(), | |
| 138 &malformed_error)) { | |
| 139 // Successful assignable type check: return object in RAX. | |
| 140 __ jmp(is_instance); | |
| 141 } else { | |
| 142 // Failed assignable type check: call runtime to throw TypeError. | |
| 143 __ jmp(&runtime_call); | |
| 144 } | |
| 145 // Compare if the classes are equal. | |
| 146 __ Bind(&compare_classes); | |
| 147 // If type is an interface, we can skip the class equality check, | |
| 148 // because instances cannot be of an interface type. | |
| 149 if (!type_class.is_interface()) { | |
| 150 __ LoadObject(RCX, type_class); | |
| 151 __ movq(R10, FieldAddress(RAX, Object::class_offset())); | |
| 152 __ cmpq(R10, RCX); | |
| 153 __ j(EQUAL, is_instance); | |
| 154 // RAX, RCX, and RDX are preserved in stub, result is in RBX. | |
| 155 __ call(&StubCode::IsRawSubTypeLabel()); | |
| 156 // Result in RBX: 1 is raw subtype. | |
| 157 __ cmpq(RBX, Immediate(1)); | |
| 158 __ j(EQUAL, is_instance); | |
| 159 // Otherwise fall through to runtime call. | |
| 160 } else { | |
| 161 // However, for specific core library interfaces, we can check for | |
| 162 // specific core library classes. | |
| 163 Error& malformed_error = Error::Handle(); | |
| 164 if (type.IsBoolInterface()) { | |
| 165 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); | |
| 166 const Class& bool_class = Class::ZoneHandle( | |
| 167 Isolate::Current()->object_store()->bool_class()); | |
| 168 __ CompareObject(RCX, bool_class); | |
| 169 __ j(EQUAL, is_instance); | |
| 170 } else if (type.IsSubtypeOf( | |
| 171 Type::Handle(Type::NumberInterface()), &malformed_error)) { | |
| 172 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); | |
| 173 if (type.IsIntInterface() || type.IsNumberInterface()) { | |
| 174 // We already checked for Smi above. | |
| 175 const Class& mint_class = Class::ZoneHandle( | |
| 176 Isolate::Current()->object_store()->mint_class()); | |
| 177 __ CompareObject(RCX, mint_class); | |
| 178 __ j(EQUAL, is_instance); | |
| 179 const Class& bigint_class = Class::ZoneHandle( | |
| 180 Isolate::Current()->object_store()->bigint_class()); | |
| 181 __ CompareObject(RCX, bigint_class); | |
| 182 __ j(EQUAL, is_instance); | |
| 183 } | |
| 184 if (type.IsDoubleInterface() || type.IsNumberInterface()) { | |
| 185 const Class& double_class = Class::ZoneHandle( | |
| 186 Isolate::Current()->object_store()->double_class()); | |
| 187 __ CompareObject(RCX, double_class); | |
| 188 __ j(EQUAL, is_instance); | |
| 189 } | |
| 190 } else if (type.IsStringInterface()) { | |
| 191 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); | |
| 192 const Class& one_byte_string_class = Class::ZoneHandle( | |
| 193 Isolate::Current()->object_store()->one_byte_string_class()); | |
| 194 __ CompareObject(RCX, one_byte_string_class); | |
| 195 __ j(EQUAL, is_instance); | |
| 196 const Class& two_byte_string_class = Class::ZoneHandle( | |
| 197 Isolate::Current()->object_store()->two_byte_string_class()); | |
| 198 __ CompareObject(RCX, two_byte_string_class); | |
| 199 __ j(EQUAL, is_instance); | |
| 200 const Class& four_byte_string_class = Class::ZoneHandle( | |
| 201 Isolate::Current()->object_store()->four_byte_string_class()); | |
| 202 __ CompareObject(RCX, four_byte_string_class); | |
| 203 __ j(EQUAL, is_instance); | |
| 204 } else if (type.IsFunctionInterface()) { | |
| 205 const Immediate raw_null = | |
| 206 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 207 __ movq(RCX, FieldAddress(RAX, Object::class_offset())); | |
| 208 __ movq(RCX, FieldAddress(RCX, Class::signature_function_offset())); | |
| 209 __ cmpq(RCX, raw_null); | |
| 210 __ j(NOT_EQUAL, is_instance); | |
| 211 } else { | |
| 212 __ LoadObject(RCX, type_class); | |
| 213 // RAX: Instance (preserved). | |
| 214 // RCX: test class (preserved). | |
| 215 // RDX: instantiator type arguments (preserved). | |
| 216 __ call(&StubCode::IsRawSubTypeLabel()); | |
| 217 // Result in RBX: 1 is raw subtype. | |
| 218 __ cmpq(RBX, Immediate(1)); | |
| 219 __ j(EQUAL, is_instance); | |
| 220 // Otherwise fallthrough to runtime call. | |
| 221 } | |
| 222 } | |
| 223 } | |
| 224 } else { | |
| 225 ASSERT(!type.IsInstantiated()); | |
| 226 // Skip check if destination is a dynamic type. | |
| 227 if (type.IsTypeParameter()) { | |
| 228 // Check if dynamic. | |
| 229 const Immediate raw_null = | |
| 230 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 231 // Instantiator type arguments are in RDX. | |
| 232 __ cmpq(RDX, raw_null); | |
| 233 __ j(EQUAL, is_instance); | |
| 234 | |
| 235 // For now handle only TypeArguments and bail out if InstantiatedTypeArgs. | |
| 236 __ movq(RCX, FieldAddress(RDX, Object::class_offset())); | |
| 237 __ CompareObject(RCX, Object::ZoneHandle(Object::type_arguments_class())); | |
| 238 __ j(NOT_EQUAL, &runtime_call); | |
| 239 __ movq(RCX, | |
| 240 FieldAddress(RDX, TypeArguments::type_at_offset(type.Index()))); | |
| 241 // RCX: instantiated type parameter. | |
| 242 __ CompareObject(RCX, Type::ZoneHandle(Type::DynamicType())); | |
| 243 __ j(EQUAL, is_instance); | |
| 244 // Check if the type has type parameters, if not, do the class comparison. | |
| 245 Label not_smi; | |
| 246 __ testq(RAX, Immediate(kSmiTagMask)); // Value is Smi? | |
| 247 __ j(NOT_ZERO, ¬_smi, Assembler::kNearJump); | |
| 248 __ CompareObject(RCX, Type::ZoneHandle(Type::IntInterface())); | |
| 249 __ j(EQUAL, is_instance); | |
| 250 __ CompareObject(RCX, Type::ZoneHandle(Type::NumberInterface())); | |
| 251 __ j(EQUAL, is_instance); | |
| 252 __ Bind(¬_smi); | |
| 253 // The instantiated type parameter RCX may not be a Type, but could be an | |
| 254 // InstantiatedType. It is therefore necessary to check its class. | |
| 255 __ movq(R10, FieldAddress(RCX, Object::class_offset())); | |
| 256 __ CompareObject(R10, Object::ZoneHandle(Object::type_class())); | |
| 257 __ j(NOT_EQUAL, &runtime_call, Assembler::kNearJump); | |
| 258 __ movq(RCX, FieldAddress(RCX, Type::type_class_offset())); | |
| 259 __ movq(R10, FieldAddress(RCX, Class::type_parameters_offset())); | |
| 260 // Check that class of type has no type parameters. | |
| 261 __ cmpq(R10, raw_null); | |
| 262 __ j(NOT_EQUAL, &runtime_call, Assembler::kNearJump); | |
| 263 // We have a non-parameterized class in RCX, compare with class of | |
| 264 // value in RAX. RAX, RCX, and RDX are preserved in stub. | |
| 265 #if 1 | |
|
srdjan
2012/04/13 21:47:34
remove #if 1
regis
2012/04/13 22:05:50
Oops. Searching for "if 0" is not good enough :-)
| |
| 266 __ call(&StubCode::IsRawSubTypeLabel()); | |
| 267 // Result in EBX: 1 is raw subtype. | |
| 268 __ cmpq(RBX, Immediate(1)); | |
| 269 __ j(EQUAL, is_instance); | |
| 270 #endif | |
| 271 // Fall through to runtime call. | |
| 272 } | |
| 273 } | |
| 274 __ Bind(&runtime_call); | |
| 275 } | |
| 276 | |
| 277 | |
| 278 // If type check cannot be performed successfully at compile time and therefore | |
| 279 // eliminated, optimize it by adding inlined tests for: | |
|
srdjan
2012/04/13 21:47:34
Add TODO: move compile time tests to flow_graph_bu
regis
2012/04/13 22:05:50
The TODO is actually below on line 318. Edited the
| |
| 280 // - NULL -> return NULL. | |
| 281 // - Smi -> compile time subtype check (only if dst class is not parameterized). | |
| 282 // - Class equality (only if class is not parameterized). | |
| 283 // Inputs: | |
| 284 // - RAX: object. | |
| 285 // - RDX: optional instantiator type arguments. | |
| 286 // Destroys RCX and RDX. | |
| 287 // Returns: | |
| 288 // - object in RAX for successful assignable check (or throws TypeError). | |
| 289 // Performance notes: positive checks must be quick, negative checks can be slow | |
| 290 // as they throw an exception. | |
| 73 void FlowGraphCompiler::GenerateAssertAssignable(intptr_t node_id, | 291 void FlowGraphCompiler::GenerateAssertAssignable(intptr_t node_id, |
| 74 intptr_t token_index, | 292 intptr_t token_index, |
| 293 intptr_t try_index, | |
| 294 Value* value, | |
| 75 const AbstractType& dst_type, | 295 const AbstractType& dst_type, |
| 76 const String& dst_name) { | 296 const String& dst_name) { |
| 77 Bailout("GenerateAssertAssignable"); | 297 ASSERT(FLAG_enable_type_checks); |
| 298 ASSERT(token_index >= 0); | |
| 299 ASSERT(!dst_type.IsNull()); | |
| 300 ASSERT(dst_type.IsFinalized()); | |
| 301 | |
| 302 // Any expression is assignable to the Dynamic type and to the Object type. | |
| 303 // Skip the test. | |
| 304 if (!dst_type.IsMalformed() && | |
| 305 (dst_type.IsDynamicType() || dst_type.IsObjectType())) { | |
| 306 return; | |
| 307 } | |
| 308 | |
| 309 // It is a compile-time error to explicitly return a value (including null) | |
| 310 // from a void function. However, functions that do not explicitly return a | |
| 311 // value, implicitly return null. This includes void functions. Therefore, we | |
| 312 // skip the type test here and trust the parser to only return null in void | |
| 313 // function. | |
| 314 if (dst_type.IsVoidType()) { | |
| 315 return; | |
| 316 } | |
| 317 | |
| 318 // TODO(regis): Move this compile time check to the graph builder. | |
| 319 // Eliminate the test if it can be performed successfully at compile time. | |
| 320 if ((value != NULL) && value->IsConstant()) { | |
| 321 Instance& literal_value = Instance::Handle(); | |
| 322 literal_value ^= value->AsConstant()->value().raw(); | |
| 323 const Class& cls = Class::Handle(literal_value.clazz()); | |
| 324 if (cls.IsNullClass()) { | |
| 325 ASSERT(literal_value.IsNull() || | |
| 326 (literal_value.raw() == Object::sentinel()) || | |
| 327 (literal_value.raw() == Object::transition_sentinel())); | |
| 328 return; | |
| 329 } | |
| 330 Error& malformed_error = Error::Handle(); | |
| 331 if (!dst_type.IsMalformed() && | |
| 332 dst_type.IsInstantiated() && | |
| 333 literal_value.IsInstanceOf(dst_type, | |
| 334 TypeArguments::Handle(), | |
| 335 &malformed_error)) { | |
| 336 return; | |
| 337 } | |
| 338 } | |
| 339 | |
| 340 // A null object is always assignable and is returned as result. | |
| 341 const Immediate raw_null = | |
| 342 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 343 Label is_assignable, runtime_call; | |
| 344 __ cmpq(RAX, raw_null); | |
| 345 __ j(EQUAL, &is_assignable); | |
| 346 | |
| 347 // Generate throw new TypeError() if the type is malformed. | |
| 348 if (dst_type.IsMalformed()) { | |
| 349 const Error& error = Error::Handle(dst_type.malformed_error()); | |
| 350 const String& error_message = String::ZoneHandle( | |
| 351 String::NewSymbol(error.ToErrorCString())); | |
| 352 __ PushObject(Object::ZoneHandle()); // Make room for the result. | |
| 353 const Immediate location = | |
| 354 Immediate(reinterpret_cast<int64_t>(Smi::New(token_index))); | |
| 355 __ pushq(location); // Push the source location. | |
| 356 __ pushq(RAX); // Push the source object. | |
| 357 __ PushObject(dst_name); // Push the name of the destination. | |
| 358 __ PushObject(error_message); | |
| 359 GenerateCallRuntime(node_id, | |
| 360 token_index, | |
| 361 try_index, | |
| 362 kMalformedTypeErrorRuntimeEntry); | |
| 363 // We should never return here. | |
| 364 __ int3(); | |
| 365 | |
| 366 __ Bind(&is_assignable); // For a null object. | |
| 367 return; | |
| 368 } | |
| 369 | |
| 370 // Generate inline type check, linking to runtime call if not assignable. | |
| 371 GenerateInlineInstanceof(dst_type, &is_assignable, &runtime_call); | |
| 372 | |
| 373 __ Bind(&runtime_call); | |
| 374 __ PushObject(Object::ZoneHandle()); // Make room for the result. | |
| 375 const Immediate location = | |
| 376 Immediate(reinterpret_cast<int64_t>(Smi::New(token_index))); | |
| 377 __ pushq(location); // Push the source location. | |
| 378 __ pushq(RAX); // Push the source object. | |
| 379 __ PushObject(dst_type); // Push the type of the destination. | |
| 380 if (!dst_type.IsInstantiated()) { | |
| 381 __ pushq(RDX); // Instantiator type arguments. | |
| 382 } else { | |
| 383 __ pushq(raw_null); // Null instantiator. | |
| 384 } | |
| 385 __ PushObject(dst_name); // Push the name of the destination. | |
| 386 GenerateCallRuntime(node_id, | |
| 387 token_index, | |
| 388 try_index, | |
| 389 kTypeCheckRuntimeEntry); | |
| 390 // Pop the parameters supplied to the runtime entry. The result of the | |
| 391 // type check runtime call is the checked value. | |
| 392 __ addq(RSP, Immediate(5 * kWordSize)); | |
| 393 __ popq(RAX); | |
| 394 | |
| 395 __ Bind(&is_assignable); | |
| 78 } | 396 } |
| 79 | 397 |
| 80 | 398 |
| 81 void FlowGraphCompiler::LoadValue(Register dst, Value* value) { | 399 void FlowGraphCompiler::LoadValue(Register dst, Value* value) { |
| 82 if (value->IsConstant()) { | 400 if (value->IsConstant()) { |
| 83 ConstantVal* constant = value->AsConstant(); | 401 ConstantVal* constant = value->AsConstant(); |
| 84 if (constant->value().IsSmi()) { | 402 if (constant->value().IsSmi()) { |
| 85 int64_t imm = reinterpret_cast<int64_t>(constant->value().raw()); | 403 int64_t imm = reinterpret_cast<int64_t>(constant->value().raw()); |
| 86 __ movq(dst, Immediate(imm)); | 404 __ movq(dst, Immediate(imm)); |
| 87 } else { | 405 } else { |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 98 LoadValue(RAX, val); | 416 LoadValue(RAX, val); |
| 99 } | 417 } |
| 100 | 418 |
| 101 | 419 |
| 102 void FlowGraphCompiler::VisitConstant(ConstantVal* val) { | 420 void FlowGraphCompiler::VisitConstant(ConstantVal* val) { |
| 103 LoadValue(RAX, val); | 421 LoadValue(RAX, val); |
| 104 } | 422 } |
| 105 | 423 |
| 106 | 424 |
| 107 void FlowGraphCompiler::VisitAssertAssignable(AssertAssignableComp* comp) { | 425 void FlowGraphCompiler::VisitAssertAssignable(AssertAssignableComp* comp) { |
| 108 Bailout("AssertAssignableComp"); | 426 if (comp->type_arguments() != NULL) { |
| 427 __ popq(RDX); | |
| 428 } | |
| 429 LoadValue(RAX, comp->value()); | |
| 430 GenerateAssertAssignable(comp->node_id(), | |
| 431 comp->token_index(), | |
| 432 comp->try_index(), | |
| 433 comp->value(), | |
| 434 comp->dst_type(), | |
| 435 comp->dst_name()); | |
| 436 } | |
| 437 | |
| 438 | |
| 439 void FlowGraphCompiler::VisitAssertBoolean(AssertBooleanComp* comp) { | |
| 440 LoadValue(RAX, comp->value()); | |
| 441 // Check that the type of the value is allowed in conditional context. | |
| 442 // Call the runtime if the object is null or not of type bool. | |
|
srdjan
2012/04/13 21:47:34
A quicker comparison wold be to compare RAX with B
regis
2012/04/13 22:05:50
Good point. Done.
| |
| 443 const Immediate raw_null = | |
| 444 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 445 Label runtime_call, done; | |
| 446 __ popq(RAX); | |
| 447 __ cmpq(RAX, raw_null); | |
| 448 __ j(EQUAL, &runtime_call, Assembler::kNearJump); | |
| 449 __ testq(RAX, Immediate(kSmiTagMask)); | |
| 450 __ j(ZERO, &runtime_call, Assembler::kNearJump); // Call runtime for Smi. | |
| 451 // This check should pass if the receiver's class implements the interface | |
| 452 // 'bool'. Check only class 'Bool' since it is the only legal implementation | |
| 453 // of the interface 'bool'. | |
| 454 const Class& bool_class = | |
| 455 Class::ZoneHandle(Isolate::Current()->object_store()->bool_class()); | |
| 456 __ movq(RDX, FieldAddress(RAX, Object::class_offset())); | |
| 457 __ CompareObject(RDX, bool_class); | |
| 458 __ j(EQUAL, &done, Assembler::kNearJump); | |
| 459 | |
| 460 __ Bind(&runtime_call); | |
| 461 const Immediate location = | |
| 462 Immediate(reinterpret_cast<int64_t>(Smi::New(comp->token_index()))); | |
| 463 __ pushq(location); // Push the source location. | |
| 464 __ pushq(RAX); // Push the source object. | |
| 465 GenerateCallRuntime(comp->node_id(), | |
| 466 comp->token_index(), | |
| 467 comp->try_index(), | |
| 468 kConditionTypeErrorRuntimeEntry); | |
| 469 // We should never return here. | |
| 470 __ int3(); | |
| 471 | |
| 472 __ Bind(&done); | |
| 109 } | 473 } |
| 110 | 474 |
| 111 | 475 |
| 112 // True iff. the arguments to a call will be properly pushed and can | 476 // True iff. the arguments to a call will be properly pushed and can |
| 113 // be popped after the call. | 477 // be popped after the call. |
| 114 template <typename T> static bool VerifyCallComputation(T* comp) { | 478 template <typename T> static bool VerifyCallComputation(T* comp) { |
| 115 // Argument values should be consecutive temps. | 479 // Argument values should be consecutive temps. |
| 116 // | 480 // |
| 117 // TODO(kmillikin): implement stack height tracking so we can also assert | 481 // TODO(kmillikin): implement stack height tracking so we can also assert |
| 118 // they are on top of the stack. | 482 // they are on top of the stack. |
| (...skipping 307 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 426 Label done; | 790 Label done; |
| 427 LoadValue(RDX, comp->value()); | 791 LoadValue(RDX, comp->value()); |
| 428 __ LoadObject(RAX, bool_true); | 792 __ LoadObject(RAX, bool_true); |
| 429 __ cmpq(RAX, RDX); | 793 __ cmpq(RAX, RDX); |
| 430 __ j(NOT_EQUAL, &done, Assembler::kNearJump); | 794 __ j(NOT_EQUAL, &done, Assembler::kNearJump); |
| 431 __ LoadObject(RAX, bool_false); | 795 __ LoadObject(RAX, bool_false); |
| 432 __ Bind(&done); | 796 __ Bind(&done); |
| 433 } | 797 } |
| 434 | 798 |
| 435 | 799 |
| 436 static const Class* CoreClass(const char* c_name) { | |
| 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 | 800 // If instanceof type test cannot be performed successfully at compile time and |
| 447 // therefore eliminated, optimize it by adding inlined tests for: | 801 // therefore eliminated, optimize it by adding inlined tests for: |
| 448 // - NULL -> return false. | 802 // - NULL -> return false. |
| 449 // - Smi -> compile time subtype check (only if dst class is not parameterized). | 803 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
| 450 // - Class equality (only if class is not parameterized). | 804 // - Class equality (only if class is not parameterized). |
| 451 // Inputs: | 805 // Inputs: |
| 452 // - RAX: object. | 806 // - RAX: object. |
| 453 // - RDX: optional type-arguments. | 807 // - RDX: optional instantiator type arguments. |
| 454 // Destroys RCX. | 808 // Destroys RCX and RDX. |
| 455 // Returns: | 809 // Returns: |
| 456 // - true or false in RAX. | 810 // - true or false in RAX. |
| 457 void FlowGraphCompiler::GenerateInstanceOf(intptr_t node_id, | 811 void FlowGraphCompiler::GenerateInstanceOf(intptr_t node_id, |
| 458 intptr_t token_index, | 812 intptr_t token_index, |
| 459 intptr_t try_index, | 813 intptr_t try_index, |
| 460 Value* value, | 814 Value* value, |
| 461 const AbstractType& type, | 815 const AbstractType& type, |
| 462 bool negate_result) { | 816 bool negate_result) { |
| 463 ASSERT(type.IsFinalized() && !type.IsMalformed()); | 817 ASSERT(type.IsFinalized() && !type.IsMalformed()); |
| 464 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | 818 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); |
| 465 const Bool& bool_false = Bool::ZoneHandle(Bool::False()); | 819 const Bool& bool_false = Bool::ZoneHandle(Bool::False()); |
| 466 | 820 |
| 467 const Immediate raw_null = | 821 const Immediate raw_null = |
| 468 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 822 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 469 Label done; | 823 Label is_instance, is_not_instance; |
| 470 // If type is instantiated and non-parameterized, we can inline code | 824 // If type is instantiated and non-parameterized, we can inline code |
| 471 // checking whether the tested instance is a Smi. | 825 // checking whether the tested instance is a Smi. |
| 472 if (type.IsInstantiated()) { | 826 if (type.IsInstantiated()) { |
| 473 // A null object is only an instance of Object and Dynamic, which has | 827 // 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 | 828 // 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 | 829 // return false here if the instance is null (and if the type is |
| 476 // instantiated). | 830 // instantiated). |
| 477 // We can only inline this null check if the type is instantiated at compile | 831 // 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 | 832 // time, since an uninstantiated type at compile time could be Object or |
| 479 // Dynamic at run time. | 833 // Dynamic at run time. |
| 480 Label non_null; | |
| 481 __ cmpq(RAX, raw_null); | 834 __ cmpq(RAX, raw_null); |
| 482 __ j(NOT_EQUAL, &non_null, Assembler::kNearJump); | 835 __ j(EQUAL, &is_not_instance); |
| 483 __ PushObject(negate_result ? bool_true : bool_false); | 836 } |
| 484 __ jmp(&done); | |
| 485 | 837 |
| 486 __ Bind(&non_null); | 838 // Generate inline instanceof test. |
| 839 GenerateInlineInstanceof(type, &is_instance, &is_not_instance); | |
| 487 | 840 |
| 488 const Class& type_class = Class::ZoneHandle(type.type_class()); | 841 // 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. | 842 __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| 568 const Immediate location = | 843 const Immediate location = |
| 569 Immediate(reinterpret_cast<int64_t>(Smi::New(token_index))); | 844 Immediate(reinterpret_cast<int64_t>(Smi::New(token_index))); |
| 570 const Immediate node_id_as_smi = | 845 const Immediate node_id_as_smi = |
| 571 Immediate(reinterpret_cast<int64_t>(Smi::New(node_id))); | 846 Immediate(reinterpret_cast<int64_t>(Smi::New(node_id))); |
| 572 __ pushq(location); // Push the source location. | 847 __ pushq(location); // Push the source location. |
| 573 __ pushq(node_id_as_smi); | 848 __ pushq(node_id_as_smi); |
| 574 __ pushq(RAX); // Push the instance. | 849 __ pushq(RAX); // Push the instance. |
| 575 __ PushObject(type); // Push the type. | 850 __ PushObject(type); // Push the type. |
| 576 if (!type.IsInstantiated()) { | 851 if (!type.IsInstantiated()) { |
| 577 __ pushq(RDX); // Type arguments. | 852 __ pushq(RDX); // Instantiator type arguments. |
| 578 } else { | 853 } else { |
| 579 __ pushq(raw_null); // Null instantiator. | 854 __ pushq(raw_null); // Null instantiator. |
| 580 } | 855 } |
| 581 GenerateCallRuntime(node_id, token_index, try_index, kInstanceofRuntimeEntry); | 856 GenerateCallRuntime(node_id, token_index, try_index, kInstanceofRuntimeEntry); |
| 582 // Pop the two parameters supplied to the runtime entry. The result of the | 857 // 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. | 858 // instanceof runtime call will be left as the result of the operation. |
| 584 __ addq(RSP, Immediate(5 * kWordSize)); | 859 __ addq(RSP, Immediate(5 * kWordSize)); |
| 860 Label done; | |
| 585 if (negate_result) { | 861 if (negate_result) { |
| 586 Label negate_done; | |
| 587 __ popq(RDX); | 862 __ popq(RDX); |
| 588 __ LoadObject(RAX, bool_true); | 863 __ LoadObject(RAX, bool_true); |
| 589 __ cmpq(RDX, RAX); | 864 __ cmpq(RDX, RAX); |
| 590 __ j(NOT_EQUAL, &negate_done, Assembler::kNearJump); | 865 __ j(NOT_EQUAL, &done, Assembler::kNearJump); |
| 591 __ LoadObject(RAX, bool_false); | 866 __ LoadObject(RAX, bool_false); |
| 592 __ Bind(&negate_done); | 867 } else { |
| 593 __ pushq(RAX); | 868 __ popq(RAX); |
| 594 } | 869 } |
| 870 __ jmp(&done, Assembler::kNearJump); | |
| 871 | |
| 872 __ Bind(&is_not_instance); | |
| 873 __ LoadObject(RAX, negate_result ? bool_true : bool_false); | |
| 874 __ jmp(&done, Assembler::kNearJump); | |
| 875 | |
| 876 __ Bind(&is_instance); | |
| 877 __ LoadObject(RAX, negate_result ? bool_false : bool_true); | |
| 595 __ Bind(&done); | 878 __ Bind(&done); |
| 596 __ popq(RAX); | |
| 597 } | 879 } |
| 598 | 880 |
| 599 | 881 |
| 600 void FlowGraphCompiler::VisitInstanceOf(InstanceOfComp* comp) { | 882 void FlowGraphCompiler::VisitInstanceOf(InstanceOfComp* comp) { |
| 601 if (comp->type_arguments() != NULL) { | 883 if (comp->type_arguments() != NULL) { |
| 602 __ popq(RDX); | 884 __ popq(RDX); |
| 603 } | 885 } |
| 604 __ popq(RAX); | 886 LoadValue(RAX, comp->value()); |
| 605 GenerateInstanceOf(comp->node_id(), | 887 GenerateInstanceOf(comp->node_id(), |
| 606 comp->token_index(), | 888 comp->token_index(), |
| 607 comp->try_index(), | 889 comp->try_index(), |
| 608 comp->value(), | 890 comp->value(), |
| 609 comp->type(), | 891 comp->type(), |
| 610 comp->negate_result()); | 892 comp->negate_result()); |
| 611 } | 893 } |
| 612 | 894 |
| 613 | 895 |
| 614 void FlowGraphCompiler::VisitAllocateObject(AllocateObjectComp* comp) { | 896 void FlowGraphCompiler::VisitAllocateObject(AllocateObjectComp* comp) { |
| (...skipping 738 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1353 ASSERT(exception_handlers_list_ != NULL); | 1635 ASSERT(exception_handlers_list_ != NULL); |
| 1354 const ExceptionHandlers& handlers = ExceptionHandlers::Handle( | 1636 const ExceptionHandlers& handlers = ExceptionHandlers::Handle( |
| 1355 exception_handlers_list_->FinalizeExceptionHandlers(code.EntryPoint())); | 1637 exception_handlers_list_->FinalizeExceptionHandlers(code.EntryPoint())); |
| 1356 code.set_exception_handlers(handlers); | 1638 code.set_exception_handlers(handlers); |
| 1357 } | 1639 } |
| 1358 | 1640 |
| 1359 | 1641 |
| 1360 } // namespace dart | 1642 } // namespace dart |
| 1361 | 1643 |
| 1362 #endif // defined TARGET_ARCH_X64 | 1644 #endif // defined TARGET_ARCH_X64 |
| OLD | NEW |