Index: runtime/vm/flow_graph_compiler_ia32.cc |
=================================================================== |
--- runtime/vm/flow_graph_compiler_ia32.cc (revision 8286) |
+++ runtime/vm/flow_graph_compiler_ia32.cc (working copy) |
@@ -12,6 +12,7 @@ |
#include "vm/compiler_stats.h" |
#include "vm/il_printer.h" |
#include "vm/locations.h" |
+#include "vm/object_store.h" |
#include "vm/stub_code.h" |
namespace dart { |
@@ -409,6 +410,380 @@ |
} |
+// Fall through if bool_register contains null. |
+void FlowGraphCompiler::GenerateBoolToJump(Register bool_register, |
+ Label* is_true, |
+ Label* is_false) { |
+ const Immediate raw_null = |
+ Immediate(reinterpret_cast<intptr_t>(Object::null())); |
+ Label fall_through; |
+ __ cmpl(bool_register, raw_null); |
+ __ j(EQUAL, &fall_through, Assembler::kNearJump); |
+ const Bool& bool_true = Bool::ZoneHandle(Bool::True()); |
+ __ CompareObject(bool_register, bool_true); |
+ __ j(EQUAL, is_true); |
+ __ jmp(is_false); |
+ __ Bind(&fall_through); |
+} |
+ |
+ |
+// Clobbers ECX. |
+RawSubtypeTestCache* FlowGraphCompiler::GenerateCallSubtypeTestStub( |
+ TypeTestStubKind test_kind, |
+ Register instance_reg, |
+ Register type_arguments_reg, |
+ Register temp_reg, |
+ Label* is_instance_lbl, |
+ Label* is_not_instance_lbl) { |
+ const SubtypeTestCache& type_test_cache = |
+ SubtypeTestCache::ZoneHandle(SubtypeTestCache::New()); |
+ const Immediate raw_null = |
+ Immediate(reinterpret_cast<intptr_t>(Object::null())); |
+ __ LoadObject(temp_reg, type_test_cache); |
+ __ pushl(temp_reg); // Subtype test cache. |
+ __ pushl(instance_reg); // Instance. |
+ if (test_kind == kTestTypeOneArg) { |
+ ASSERT(type_arguments_reg == kNoRegister); |
+ __ pushl(raw_null); |
+ __ call(&StubCode::Subtype1TestCacheLabel()); |
+ } else if (test_kind == kTestTypeTwoArgs) { |
+ ASSERT(type_arguments_reg == kNoRegister); |
+ __ pushl(raw_null); |
+ __ call(&StubCode::Subtype2TestCacheLabel()); |
+ } else if (test_kind == kTestTypeThreeArgs) { |
+ __ pushl(type_arguments_reg); |
+ __ call(&StubCode::Subtype3TestCacheLabel()); |
+ } else { |
+ UNREACHABLE(); |
+ } |
+ // Result is in ECX: null -> not found, otherwise Bool::True or Bool::False. |
+ ASSERT(instance_reg != ECX); |
+ ASSERT(temp_reg != ECX); |
+ __ popl(instance_reg); // Discard. |
+ __ popl(instance_reg); // Restore receiver. |
+ __ popl(temp_reg); // Discard. |
+ GenerateBoolToJump(ECX, is_instance_lbl, is_not_instance_lbl); |
+ return type_test_cache.raw(); |
+} |
+ |
+ |
+// Jumps to labels 'is_instance' or 'is_not_instance' respectively, if |
+// type test is conclusive, otherwise fallthrough if a type test could not |
+// be completed. |
+// EAX: instance (must survive), clobbers ECX, EDI |
+RawSubtypeTestCache* |
+FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest( |
+ intptr_t cid, |
+ intptr_t token_index, |
+ const AbstractType& type, |
+ Label* is_instance_lbl, |
+ Label* is_not_instance_lbl) { |
+ ASSERT(type.IsInstantiated()); |
+ const Class& type_class = Class::ZoneHandle(type.type_class()); |
+ ASSERT(type_class.HasTypeArguments()); |
+ const Register kInstanceReg = EAX; |
+ // A Smi object cannot be the instance of a parameterized class. |
+ __ testl(kInstanceReg, Immediate(kSmiTagMask)); |
+ __ j(ZERO, is_not_instance_lbl); |
+ const AbstractTypeArguments& type_arguments = |
+ AbstractTypeArguments::ZoneHandle(type.arguments()); |
+ const bool is_raw_type = type_arguments.IsNull() || |
+ type_arguments.IsRaw(type_arguments.Length()); |
+ if (is_raw_type) { |
+ const Register kClassIdReg = ECX; |
+ // Dynamic type argument, check only classes. |
+ __ LoadClassId(kClassIdReg, kInstanceReg); |
+ if (!type_class.is_interface()) { |
+ __ cmpl(kClassIdReg, Immediate(type_class.id())); |
+ __ j(EQUAL, is_instance_lbl); |
+ } |
+ if (type.IsListInterface()) { |
+ GenerateListTypeCheck(kClassIdReg, is_instance_lbl); |
+ } |
+ return GenerateSubtype1TestCacheLookup( |
+ cid, token_index, type_class, is_instance_lbl, is_not_instance_lbl); |
+ } |
+ // If one type argument only, check if type argument is Object or Dynamic. |
+ if (type_arguments.Length() == 1) { |
+ const AbstractType& tp_argument = AbstractType::ZoneHandle( |
+ type_arguments.TypeAt(0)); |
+ ASSERT(!tp_argument.IsMalformed()); |
+ if (tp_argument.IsType()) { |
+ ASSERT(tp_argument.HasResolvedTypeClass()); |
+ // Check if type argument is dynamic or Object. |
+ const Type& object_type = |
+ Type::Handle(Isolate::Current()->object_store()->object_type()); |
+ Error& malformed_error = Error::Handle(); |
+ if (object_type.IsSubtypeOf(tp_argument, &malformed_error)) { |
+ // Instance class test only necessary. |
+ return GenerateSubtype1TestCacheLookup( |
+ cid, token_index, type_class, is_instance_lbl, is_not_instance_lbl); |
+ } |
+ } |
+ } |
+ // Regular subtype test cache involving instance's type arguments. |
+ const Register kTypeArgumentsReg = kNoRegister; |
+ const Register kTempReg = EDI; |
+ return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, |
+ kInstanceReg, |
+ kTypeArgumentsReg, |
+ kTempReg, |
+ is_instance_lbl, |
+ is_not_instance_lbl); |
+} |
+ |
+ |
+void FlowGraphCompiler::CheckClassIds(Register class_id_reg, |
+ const GrowableArray<intptr_t>& class_ids, |
+ Label* is_equal_lbl, |
+ Label* is_not_equal_lbl) { |
+ for (intptr_t i = 0; i < class_ids.length(); i++) { |
+ __ cmpl(class_id_reg, Immediate(class_ids[i])); |
+ __ j(EQUAL, is_equal_lbl); |
+ } |
+ __ jmp(is_not_equal_lbl); |
+} |
+ |
+ |
+// Testing against an instantiated type with no arguments, without |
+// SubtypeTestCache. |
+// EAX: instance to test against (preserved). Clobbers ECX, EDI. |
+void FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest( |
+ intptr_t cid, |
+ intptr_t token_index, |
+ const AbstractType& type, |
+ Label* is_instance_lbl, |
+ Label* is_not_instance_lbl) { |
+ ASSERT(type.IsInstantiated()); |
+ const Class& type_class = Class::Handle(type.type_class()); |
+ ASSERT(!type_class.HasTypeArguments()); |
+ |
+ const Register kInstanceReg = EAX; |
+ Label compare_classes; |
+ __ testl(kInstanceReg, Immediate(kSmiTagMask)); |
+ __ j(NOT_ZERO, &compare_classes, Assembler::kNearJump); |
+ // Instance is Smi, check directly. |
+ const Class& smi_class = Class::Handle(Smi::Class()); |
+ // TODO(regis): We should introduce a SmiType. |
+ Error& malformed_error = Error::Handle(); |
+ if (smi_class.IsSubtypeOf(TypeArguments::Handle(), |
+ type_class, |
+ TypeArguments::Handle(), |
+ &malformed_error)) { |
+ __ jmp(is_instance_lbl); |
+ } else { |
+ __ jmp(is_not_instance_lbl); |
+ } |
+ // Compare if the classes are equal. |
+ __ Bind(&compare_classes); |
+ const Register kClassIdReg = ECX; |
+ __ LoadClassId(kClassIdReg, kInstanceReg); |
+ // If type is an interface, we can skip the class equality check. |
+ if (!type_class.is_interface()) { |
+ __ cmpl(kClassIdReg, Immediate(type_class.id())); |
+ __ j(EQUAL, is_instance_lbl); |
+ } |
+ // (see ClassFinalizer::ResolveInterfaces for list of restricted interfaces). |
+ // Bool interface can be implemented only by core class Bool. |
+ if (type.IsBoolInterface()) { |
+ __ cmpl(kClassIdReg, Immediate(kBool)); |
+ __ j(EQUAL, is_instance_lbl); |
+ __ jmp(is_not_instance_lbl); |
+ return; |
+ } |
+ if (type.IsFunctionInterface()) { |
+ // Check if instance is a closure. |
+ const Immediate raw_null = |
+ Immediate(reinterpret_cast<intptr_t>(Object::null())); |
+ __ LoadClassById(EDI, kClassIdReg); |
+ __ movl(EDI, FieldAddress(EDI, Class::signature_function_offset())); |
+ __ cmpl(EDI, raw_null); |
+ __ j(NOT_EQUAL, is_instance_lbl); |
+ __ jmp(is_not_instance_lbl); |
+ return; |
+ } |
+ // Custom checking for numbers (Smi, Mint, Bigint and Double). |
+ // Note that instance is not Smi(checked above). |
+ if (type.IsSubtypeOf( |
+ Type::Handle(Type::NumberInterface()), &malformed_error)) { |
+ GenerateNumberTypeCheck( |
+ kClassIdReg, type, is_instance_lbl, is_not_instance_lbl); |
+ return; |
+ } |
+ if (type.IsStringInterface()) { |
+ GenerateStringTypeCheck(kClassIdReg, is_instance_lbl, is_not_instance_lbl); |
+ return; |
+ } |
+} |
+ |
+ |
+// Generates inlined check if 'type' is a type parameter or type itsef |
+// EAX: instance (preserved). Clobbers EDX, EDI, ECX. |
+RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( |
+ intptr_t cid, |
+ intptr_t token_index, |
+ const AbstractType& type, |
+ Label* is_instance_lbl, |
+ Label* is_not_instance_lbl) { |
+ ASSERT(!type.IsInstantiated()); |
+ // Skip check if destination is a dynamic type. |
+ const Immediate raw_null = |
+ Immediate(reinterpret_cast<intptr_t>(Object::null())); |
+ if (type.IsTypeParameter()) { |
+ // Load instantiator (or null) and instantiator type arguments on stack. |
+ __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. |
+ // EDX: instantiator type arguments. |
+ // Check if type argument is Dynamic. |
+ __ cmpl(EDX, raw_null); |
+ __ j(EQUAL, is_instance_lbl); |
+ // Can handle only type arguments that are instances of TypeArguments. |
+ // (runtime checks canonicalize type arguments). |
+ Label fall_through; |
+ __ CompareClassId(EDX, kTypeArguments, EDI); |
+ __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump); |
+ |
+ __ movl(EDI, |
+ FieldAddress(EDX, TypeArguments::type_at_offset(type.Index()))); |
+ // EDI: concrete type of type. |
+ // Check if type argument is dynamic. |
+ __ CompareObject(EDI, Type::ZoneHandle(Type::DynamicType())); |
+ __ j(EQUAL, is_instance_lbl); |
+ __ cmpl(EDI, raw_null); |
+ __ j(EQUAL, is_instance_lbl); |
+ const Type& object_type = |
+ Type::ZoneHandle(Isolate::Current()->object_store()->object_type()); |
+ __ CompareObject(EDI, object_type); |
+ __ j(EQUAL, is_instance_lbl); |
+ |
+ // For Smi check quickly against int and num interfaces. |
+ Label not_smi; |
+ __ testl(EAX, Immediate(kSmiTagMask)); // Value is Smi? |
+ __ j(NOT_ZERO, ¬_smi, Assembler::kNearJump); |
+ __ CompareObject(EDI, Type::ZoneHandle(Type::IntInterface())); |
+ __ j(EQUAL, is_instance_lbl); |
+ __ CompareObject(EDI, Type::ZoneHandle(Type::NumberInterface())); |
+ __ j(EQUAL, is_instance_lbl); |
+ // Smi must be handled in runtime. |
+ __ jmp(&fall_through); |
+ |
+ __ Bind(¬_smi); |
+ // EDX: instantiator type arguments. |
+ // EAX: instance. |
+ const Register kInstanceReg = EAX; |
+ const Register kTypeArgumentsReg = EDX; |
+ const Register kTempReg = EDI; |
+ const SubtypeTestCache& type_test_cache = |
+ SubtypeTestCache::ZoneHandle( |
+ GenerateCallSubtypeTestStub(kTestTypeThreeArgs, |
+ kInstanceReg, |
+ kTypeArgumentsReg, |
+ kTempReg, |
+ is_instance_lbl, |
+ is_not_instance_lbl)); |
+ __ Bind(&fall_through); |
+ return type_test_cache.raw(); |
+ } |
+ if (type.IsType()) { |
+ const Register kInstanceReg = EAX; |
+ const Register kTypeArgumentsReg = EDX; |
+ __ testl(kInstanceReg, Immediate(kSmiTagMask)); // Is instance Smi? |
+ __ j(ZERO, is_not_instance_lbl); |
+ __ movl(kTypeArgumentsReg, Address(ESP, 0)); // Instantiator type args. |
+ // Uninstantiated type class is known at compile time, but the type |
+ // arguments are determined at runtime by the instantiator. |
+ const Register kTempReg = EDI; |
+ return GenerateCallSubtypeTestStub(kTestTypeThreeArgs, |
+ kInstanceReg, |
+ kTypeArgumentsReg, |
+ kTempReg, |
+ is_instance_lbl, |
+ is_not_instance_lbl); |
+ } |
+ return SubtypeTestCache::null(); |
+} |
+ |
+ |
+// Uses SubtypeTestCache to store instance class and result. |
+// EAX: instance to test. Clobbers EDI, ECX. |
+// Immediate class test already done. |
+// TODO(srdjan): Implement a quicker subtype check, as type test |
+// arrays can grow too high, but they may be useful when optimizing |
+// code (type-feedback). |
+RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup( |
+ intptr_t cid, |
+ intptr_t token_index, |
+ const Class& type_class, |
+ Label* is_instance_lbl, |
+ Label* is_not_instance_lbl) { |
+ const Register kInstanceReg = EAX; |
+ __ LoadClass(ECX, kInstanceReg, EDI); |
+ // ECX: instance class. |
+ // Check immediate superclass equality. |
+ __ movl(EDI, FieldAddress(ECX, Class::super_type_offset())); |
+ __ movl(EDI, FieldAddress(EDI, Type::type_class_offset())); |
+ __ CompareObject(EDI, type_class); |
+ __ j(EQUAL, is_instance_lbl); |
+ |
+ const Register kTypeArgumentsReg = kNoRegister; |
+ const Register kTempReg = EDI; |
+ return GenerateCallSubtypeTestStub(kTestTypeOneArg, |
+ kInstanceReg, |
+ kTypeArgumentsReg, |
+ kTempReg, |
+ is_instance_lbl, |
+ is_not_instance_lbl); |
+} |
+ |
+ |
+// Inputs: |
+// - EAX: instance to test against (preserved). |
+// - EDX: optional instantiator type arguments (preserved). |
+// Returns: |
+// - preserved instance in EAX and optional instantiator type arguments in EDX. |
+// 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 cid, |
+ intptr_t token_index, |
+ const AbstractType& type, |
+ Label* is_instance_lbl, |
+ Label* is_not_instance_lbl) { |
+ 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(cid, |
+ token_index, |
+ type, |
+ is_instance_lbl, |
+ is_not_instance_lbl); |
+ // Fall through to runtime call. |
+ } else { |
+ GenerateInstantiatedTypeNoArgumentsTest(cid, |
+ token_index, |
+ type, |
+ is_instance_lbl, |
+ is_not_instance_lbl); |
+ // If test non-conclusive so far, try the inlined type-test cache. |
+ // 'type' is known at compile time. |
+ return GenerateSubtype1TestCacheLookup( |
+ cid, token_index, type_class, |
+ is_instance_lbl, is_not_instance_lbl); |
+ } |
+ } else { |
+ return GenerateUninstantiatedTypeTest(cid, |
+ token_index, |
+ type, |
+ is_instance_lbl, |
+ is_not_instance_lbl); |
+ } |
+ return SubtypeTestCache::null(); |
+} |
+ |
+ |
// If instanceof type test cannot be performed successfully at compile time and |
// therefore eliminated, optimize it by adding inlined tests for: |
// - NULL -> return false. |
@@ -450,8 +825,8 @@ |
// TODO(srdjan): Enable inlined checks. |
// Generate inline instanceof test. |
SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); |
- // test_cache = GenerateInlineInstanceof(cid, token_index, type, |
- // &is_instance, &is_not_instance); |
+ test_cache = GenerateInlineInstanceof(cid, token_index, type, |
+ &is_instance, &is_not_instance); |
// Generate runtime call. |
__ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. |
@@ -551,8 +926,8 @@ |
// TODO(srdjan): Enable subtype test cache. |
// Generate inline type check, linking to runtime call if not assignable. |
SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); |
- // test_cache = GenerateInlineInstanceof(cid, token_index, dst_type, |
- // &is_assignable, &runtime_call); |
+ test_cache = GenerateInlineInstanceof(cid, token_index, dst_type, |
+ &is_assignable, &runtime_call); |
__ Bind(&runtime_call); |
__ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. |