Chromium Code Reviews| Index: runtime/vm/flow_graph_compiler_arm.cc |
| =================================================================== |
| --- runtime/vm/flow_graph_compiler_arm.cc (revision 20598) |
| +++ runtime/vm/flow_graph_compiler_arm.cc (working copy) |
| @@ -23,6 +23,7 @@ |
| DECLARE_FLAG(bool, print_ast); |
| DECLARE_FLAG(bool, print_scopes); |
| DECLARE_FLAG(bool, enable_type_checks); |
| +DECLARE_FLAG(bool, eliminate_type_checks); |
| FlowGraphCompiler::~FlowGraphCompiler() { |
| @@ -82,17 +83,74 @@ |
| const GrowableArray<intptr_t>& class_ids, |
| Label* is_equal_lbl, |
| Label* is_not_equal_lbl) { |
| - UNIMPLEMENTED(); |
| + for (intptr_t i = 0; i < class_ids.length(); i++) { |
| + __ CompareImmediate(class_id_reg, class_ids[i]); |
| + __ b(is_equal_lbl, EQ); |
| + } |
| + __ b(is_not_equal_lbl); |
| } |
| +// Testing against an instantiated type with no arguments, without |
| +// SubtypeTestCache. |
| +// R0: instance to test against (preserved). |
| +// Clobbers R2, R3. |
| +// Returns true if there is a fallthrough. |
| bool FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest( |
| intptr_t token_pos, |
| const AbstractType& type, |
| Label* is_instance_lbl, |
| Label* is_not_instance_lbl) { |
| - UNIMPLEMENTED(); |
| - return false; |
| + __ Comment("InstantiatedTypeNoArgumentsTest"); |
| + ASSERT(type.IsInstantiated()); |
| + const Class& type_class = Class::Handle(type.type_class()); |
| + ASSERT(!type_class.HasTypeArguments()); |
| + |
| + const Register kInstanceReg = R0; |
| + __ tst(kInstanceReg, ShifterOperand(kSmiTagMask)); |
| + // If instance is Smi, check directly. |
| + const Class& smi_class = Class::Handle(Smi::Class()); |
| + if (smi_class.IsSubtypeOf(TypeArguments::Handle(), |
| + type_class, |
| + TypeArguments::Handle(), |
| + NULL)) { |
| + __ b(is_instance_lbl, EQ); |
| + } else { |
| + __ b(is_not_instance_lbl, EQ); |
| + } |
| + // Compare if the classes are equal. |
| + const Register kClassIdReg = R2; |
| + __ LoadClassId(kClassIdReg, kInstanceReg); |
| + __ CompareImmediate(kClassIdReg, type_class.id()); |
| + __ b(is_instance_lbl, EQ); |
| + // Bool interface can be implemented only by core class Bool. |
| + // (see ClassFinalizer::ResolveInterfaces for list of restricted interfaces). |
|
zra
2013/03/28 16:57:51
I can't seem to find ClassFinalizer::ResolveInterf
regis
2013/03/28 17:40:21
The function was renamed to ClassFinalizer::Resolv
|
| + if (type.IsBoolType()) { |
| + __ CompareImmediate(kClassIdReg, kBoolCid); |
| + __ b(is_instance_lbl, EQ); |
| + __ b(is_not_instance_lbl); |
| + return false; |
| + } |
| + if (type.IsFunctionType()) { |
| + // Check if instance is a closure. |
| + __ LoadClassById(R3, kClassIdReg); |
| + __ ldr(R3, FieldAddress(R3, Class::signature_function_offset())); |
| + __ CompareImmediate(R3, reinterpret_cast<int32_t>(Object::null())); |
| + __ b(is_instance_lbl, NE); |
| + } |
| + // Custom checking for numbers (Smi, Mint, Bigint and Double). |
| + // Note that instance is not Smi (checked above). |
| + if (type.IsSubtypeOf(Type::Handle(Type::Number()), NULL)) { |
| + GenerateNumberTypeCheck( |
| + kClassIdReg, type, is_instance_lbl, is_not_instance_lbl); |
| + return false; |
| + } |
| + if (type.IsStringType()) { |
| + GenerateStringTypeCheck(kClassIdReg, is_instance_lbl, is_not_instance_lbl); |
| + return false; |
| + } |
| + // Otherwise fallthrough. |
| + return true; |
| } |
| @@ -116,13 +174,70 @@ |
| } |
| +// Inputs: |
| +// - R0: instance to test against (preserved). |
| +// - R1: optional instantiator type arguments (preserved). |
| +// Clobbers R2, R3. |
| +// Returns: |
| +// - preserved instance in R0 and optional instantiator type arguments in R1. |
| +// Note that this inlined code must be followed by the runtime_call code, as it |
| +// may fall through to it. Otherwise, this inline code will jump to the label |
| +// is_instance or to the label is_not_instance. |
| RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( |
| intptr_t token_pos, |
| const AbstractType& type, |
| Label* is_instance_lbl, |
| Label* is_not_instance_lbl) { |
| - UNIMPLEMENTED(); |
| - return NULL; |
| + __ Comment("InlineInstanceof"); |
| + if (type.IsVoidType()) { |
| + // A non-null value is returned from a void function, which will result in a |
| + // type error. A null value is handled prior to executing this inline code. |
| + return SubtypeTestCache::null(); |
| + } |
| + if (TypeCheckAsClassEquality(type)) { |
| + const intptr_t type_cid = Class::Handle(type.type_class()).id(); |
| + const Register kInstanceReg = R0; |
| + __ tst(kInstanceReg, ShifterOperand(kSmiTagMask)); |
| + if (type_cid == kSmiCid) { |
| + __ b(is_instance_lbl, EQ); |
| + } else { |
| + __ b(is_not_instance_lbl, EQ); |
| + __ CompareClassId(kInstanceReg, type_cid, R3); |
| + __ b(is_instance_lbl, EQ); |
| + } |
| + __ b(is_not_instance_lbl); |
| + return SubtypeTestCache::null(); |
| + } |
| + if (type.IsInstantiated()) { |
| + const Class& type_class = Class::ZoneHandle(type.type_class()); |
| + // A Smi object cannot be the instance of a parameterized class. |
| + // A class equality check is only applicable with a dst type of a |
| + // non-parameterized class or with a raw dst type of a parameterized class. |
| + if (type_class.HasTypeArguments()) { |
| + return GenerateInstantiatedTypeWithArgumentsTest(token_pos, |
| + type, |
| + is_instance_lbl, |
| + is_not_instance_lbl); |
| + // Fall through to runtime call. |
| + } |
| + const bool has_fall_through = |
| + GenerateInstantiatedTypeNoArgumentsTest(token_pos, |
| + type, |
| + is_instance_lbl, |
| + is_not_instance_lbl); |
| + if (has_fall_through) { |
| + // If test non-conclusive so far, try the inlined type-test cache. |
| + // 'type' is known at compile time. |
| + return GenerateSubtype1TestCacheLookup( |
| + token_pos, type_class, is_instance_lbl, is_not_instance_lbl); |
| + } else { |
| + return SubtypeTestCache::null(); |
| + } |
| + } |
| + return GenerateUninstantiatedTypeTest(token_pos, |
| + type, |
| + is_instance_lbl, |
| + is_not_instance_lbl); |
| } |
| @@ -135,12 +250,90 @@ |
| } |
| +// Optimize assignable type check by adding inlined tests for: |
| +// - NULL -> return NULL. |
| +// - Smi -> compile time subtype check (only if dst class is not parameterized). |
| +// - Class equality (only if class is not parameterized). |
| +// Inputs: |
| +// - R0: object. |
|
zra
2013/03/28 16:57:51
Source object?
regis
2013/03/28 17:40:21
Changed to "instance being type checked."
|
| +// - R1: instantiator type arguments or raw_null. |
| +// - R2: instantiator or raw_null. |
| +// Returns: |
| +// - object in R0 for successful assignable check (or throws TypeError). |
| +// Performance notes: positive checks must be quick, negative checks can be slow |
| +// as they throw an exception. |
| void FlowGraphCompiler::GenerateAssertAssignable(intptr_t token_pos, |
| intptr_t deopt_id, |
| const AbstractType& dst_type, |
| const String& dst_name, |
| LocationSummary* locs) { |
| - UNIMPLEMENTED(); |
| + ASSERT(token_pos >= 0); |
| + ASSERT(!dst_type.IsNull()); |
| + ASSERT(dst_type.IsFinalized()); |
| + // Assignable check is skipped in FlowGraphBuilder, not here. |
| + ASSERT(dst_type.IsMalformed() || |
| + (!dst_type.IsDynamicType() && !dst_type.IsObjectType())); |
| + // Preserve instantiator and its type arguments. |
| + __ PushList((1 << R1) | (1 << R2)); |
| + // A null object is always assignable and is returned as result. |
| + Label is_assignable, runtime_call; |
| + __ CompareImmediate(R0, reinterpret_cast<int32_t>(Object::null())); |
| + __ b(&is_assignable, EQ); |
| + |
| + if (!FLAG_eliminate_type_checks) { |
| + // If type checks are not eliminated during the graph building then |
| + // a transition sentinel can be seen here. |
| + __ CompareObject(R0, Object::transition_sentinel()); |
| + __ b(&is_assignable, EQ); |
| + } |
| + |
| + // Generate throw new TypeError() if the type is malformed. |
| + if (dst_type.IsMalformed()) { |
| + const Error& error = Error::Handle(dst_type.malformed_error()); |
| + const String& error_message = String::ZoneHandle( |
| + Symbols::New(error.ToErrorCString())); |
| + __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| + __ Push(R0); // Push the source object. |
| + __ PushObject(dst_name); // Push the name of the destination. |
| + __ PushObject(error_message); |
| + GenerateCallRuntime(token_pos, |
| + deopt_id, |
| + kMalformedTypeErrorRuntimeEntry, |
| + locs); |
| + // We should never return here. |
| + __ bkpt(0); |
| + |
| + __ Bind(&is_assignable); // For a null object. |
| + // Restore instantiator and its type arguments. |
| + __ PopList((1 << R1) | (1 << R2)); |
| + return; |
| + } |
| + |
| + // Generate inline type check, linking to runtime call if not assignable. |
| + SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); |
| + test_cache = GenerateInlineInstanceof(token_pos, dst_type, |
| + &is_assignable, &runtime_call); |
| + |
| + __ Bind(&runtime_call); |
| + // Load instantiator and its type arguments. |
| + __ ldm(IA, SP, (1 << R1) | (1 << R2)); |
| + __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| + __ Push(R0); // Push the source object. |
| + __ PushObject(dst_type); // Push the type of the destination. |
| + // Push instantiator and its type arguments. |
| + __ PushList((1 << R1) | (1 << R2)); |
| + __ PushObject(dst_name); // Push the name of the destination. |
| + __ LoadObject(R0, test_cache); |
| + __ Push(R0); |
| + GenerateCallRuntime(token_pos, deopt_id, kTypeCheckRuntimeEntry, locs); |
| + // Pop the parameters supplied to the runtime entry. The result of the |
| + // type check runtime call is the checked value. |
| + __ Drop(6); |
| + __ Pop(R0); |
| + |
| + __ Bind(&is_assignable); |
| + // Restore instantiator and its type arguments. |
| + __ PopList((1 << R1) | (1 << R2)); |
| } |