Index: runtime/vm/flow_graph_compiler_x64.cc |
=================================================================== |
--- runtime/vm/flow_graph_compiler_x64.cc (revision 8286) |
+++ runtime/vm/flow_graph_compiler_x64.cc (working copy) |
@@ -60,6 +60,60 @@ |
#define __ assembler()-> |
+ |
+// 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; |
+ __ cmpq(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); |
+} |
+ |
+ |
+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); |
+ __ pushq(temp_reg); // Subtype test cache. |
+ __ pushq(instance_reg); // Instance. |
+ if (test_kind == kTestTypeOneArg) { |
+ ASSERT(type_arguments_reg == kNoRegister); |
+ __ pushq(raw_null); |
+ __ call(&StubCode::Subtype1TestCacheLabel()); |
+ } else if (test_kind == kTestTypeTwoArgs) { |
+ ASSERT(type_arguments_reg == kNoRegister); |
+ __ pushq(raw_null); |
+ __ call(&StubCode::Subtype2TestCacheLabel()); |
+ } else if (test_kind == kTestTypeThreeArgs) { |
+ __ pushq(type_arguments_reg); |
+ __ call(&StubCode::Subtype3TestCacheLabel()); |
+ } else { |
+ UNREACHABLE(); |
+ } |
+ // Result is in ECX: null -> not found, otherwise Bool::True or Bool::False. |
+ __ popq(instance_reg); // Discard. |
+ __ popq(instance_reg); // Restore receiver. |
+ __ popq(temp_reg); // Discard. |
+ GenerateBoolToJump(RCX, 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. |
@@ -74,29 +128,25 @@ |
ASSERT(type.IsInstantiated()); |
const Class& type_class = Class::ZoneHandle(type.type_class()); |
ASSERT(type_class.HasTypeArguments()); |
+ const Register kInstanceReg = RAX; |
// A Smi object cannot be the instance of a parameterized class. |
- __ testq(RAX, Immediate(kSmiTagMask)); |
+ __ testq(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 = R10; |
// Dynamic type argument, check only classes. |
// List is a very common case. |
- __ LoadClassId(R10, RAX); |
+ __ LoadClassId(kClassIdReg, kInstanceReg); |
if (!type_class.is_interface()) { |
- __ cmpl(R10, Immediate(type_class.id())); |
+ __ cmpl(kClassIdReg, Immediate(type_class.id())); |
__ j(EQUAL, is_instance_lbl); |
} |
if (type.IsListInterface()) { |
- Label unknown; |
- GrowableArray<intptr_t> args; |
- args.Add(kArray); |
- args.Add(kGrowableObjectArray); |
- args.Add(kImmutableArray); |
- CheckClassIds(args, is_instance_lbl, &unknown); |
- __ Bind(&unknown); |
+ GenerateListTypeCheck(kClassIdReg, is_instance_lbl); |
} |
return GenerateSubtype1TestCacheLookup( |
cid, token_index, type_class, is_instance_lbl, is_not_instance_lbl); |
@@ -121,41 +171,26 @@ |
} |
// Regular subtype test cache involving instance's type arguments. |
- const SubtypeTestCache& type_test_cache = |
- SubtypeTestCache::ZoneHandle(SubtypeTestCache::New()); |
- Label runtime_call; |
- const Immediate raw_null = |
- Immediate(reinterpret_cast<intptr_t>(Object::null())); |
- __ LoadObject(R10, type_test_cache); |
- __ pushq(R10); // Subtype test cache. |
- __ pushq(RAX); // Instance. |
- __ pushq(raw_null); // Unused. |
- __ call(&StubCode::Subtype2TestCacheLabel()); |
- __ popq(RAX); // Discard. |
- __ popq(RAX); // Restore receiver. |
- __ popq(RDX); // Discard. |
- // Result is in RCX: null -> not found, otherwise Bool::True or Bool::False. |
- |
- __ cmpq(RCX, raw_null); |
- __ j(EQUAL, &runtime_call, Assembler::kNearJump); |
- const Bool& bool_true = Bool::ZoneHandle(Bool::True()); |
- __ CompareObject(RCX, bool_true); |
- __ j(EQUAL, is_instance_lbl); |
- __ jmp(is_not_instance_lbl); |
- __ Bind(&runtime_call); |
- return type_test_cache.raw(); |
+ const Register kTypeArgumentsReg = kNoRegister; |
+ const Register kTempReg = R10; |
+ return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, |
+ kInstanceReg, |
+ kTypeArgumentsReg, |
+ kTempReg, |
+ is_instance_lbl, |
+ is_not_instance_lbl); |
} |
-// R10: instance class id to check. |
-void FlowGraphCompiler::CheckClassIds(const GrowableArray<intptr_t>& class_ids, |
- Label* is_instance_lbl, |
- Label* 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(R10, Immediate(class_ids[i])); |
- __ j(EQUAL, is_instance_lbl); |
+ __ cmpl(class_id_reg, Immediate(class_ids[i])); |
+ __ j(EQUAL, is_equal_lbl); |
} |
- __ jmp(is_not_instance_lbl); |
+ __ jmp(is_not_equal_lbl); |
} |
@@ -191,17 +226,18 @@ |
// Compare if the classes are equal. Instance is not Smi. |
__ Bind(&compare_classes); |
- __ LoadClassId(R10, RAX); |
+ const Register kClassIdReg = R10; |
+ __ LoadClassId(kClassIdReg, RAX); |
// If type is an interface, we can skip the class equality check. |
if (!type_class.is_interface()) { |
- __ cmpl(R10, Immediate(type_class.id())); |
+ __ cmpl(kClassIdReg, Immediate(type_class.id())); |
__ j(EQUAL, is_instance_lbl); |
} |
// Check for interfaces that cannot be implemented by user. |
// (see ClassFinalizer::ResolveInterfaces for list of restricted interfaces). |
// Bool interface can be implemented only by core class Bool. |
if (type.IsBoolInterface()) { |
- __ cmpl(R10, Immediate(kBool)); |
+ __ cmpl(kClassIdReg, Immediate(kBool)); |
__ j(EQUAL, is_instance_lbl); |
__ jmp(is_not_instance_lbl); |
return; |
@@ -210,7 +246,7 @@ |
// Check if instance is a closure. |
const Immediate raw_null = |
Immediate(reinterpret_cast<intptr_t>(Object::null())); |
- __ LoadClassById(R13, R10); |
+ __ LoadClassById(R13, kClassIdReg); |
__ movq(R13, FieldAddress(R13, Class::signature_function_offset())); |
__ cmpq(R13, raw_null); |
__ j(NOT_EQUAL, is_instance_lbl); |
@@ -221,29 +257,12 @@ |
// Note that instance is not Smi(checked above). |
if (type.IsSubtypeOf( |
Type::Handle(Type::NumberInterface()), &malformed_error)) { |
- GrowableArray<intptr_t> args; |
- if (type.IsNumberInterface()) { |
- args.Add(kDouble); |
- args.Add(kMint); |
- args.Add(kBigint); |
- } else if (type.IsIntInterface()) { |
- args.Add(kMint); |
- args.Add(kBigint); |
- } else if (type.IsDoubleInterface()) { |
- args.Add(kDouble); |
- } |
- CheckClassIds(args, is_instance_lbl, is_not_instance_lbl); |
+ GenerateNumberTypeCheck( |
+ kClassIdReg, type, is_instance_lbl, is_not_instance_lbl); |
return; |
} |
if (type.IsStringInterface()) { |
- GrowableArray<intptr_t> args; |
- args.Add(kOneByteString); |
- args.Add(kTwoByteString); |
- args.Add(kFourByteString); |
- args.Add(kExternalOneByteString); |
- args.Add(kExternalTwoByteString); |
- args.Add(kExternalFourByteString); |
- CheckClassIds(args, is_instance_lbl, is_not_instance_lbl); |
+ GenerateStringTypeCheck(kClassIdReg, is_instance_lbl, is_not_instance_lbl); |
return; |
} |
// Otherwise fallthrough. |
@@ -259,45 +278,31 @@ |
const Class& type_class, |
Label* is_instance_lbl, |
Label* is_not_instance_lbl) { |
- const SubtypeTestCache& type_test_cache = |
- SubtypeTestCache::ZoneHandle(SubtypeTestCache::New()); |
- const Bool& bool_true = Bool::ZoneHandle(Bool::True()); |
- const Immediate raw_null = |
- Immediate(reinterpret_cast<intptr_t>(Object::null())); |
- __ LoadClass(R10, RAX); |
+ const Register kInstanceReg = RAX; |
+ __ LoadClass(R10, kInstanceReg); |
// Check immediate superclass equality. |
__ movq(R13, FieldAddress(R10, Class::super_type_offset())); |
__ movq(R13, FieldAddress(R13, Type::type_class_offset())); |
__ CompareObject(R13, type_class); |
__ j(EQUAL, is_instance_lbl); |
- __ LoadObject(R10, type_test_cache); |
- __ pushq(R10); // Cache array. |
- __ pushq(RAX); // Instance. |
- __ pushq(raw_null); // Unused |
- __ call(&StubCode::Subtype1TestCacheLabel()); |
- __ popq(RAX); // Discard. |
- __ popq(RAX); // Restore receiver. |
- __ popq(RDX); // Discard. |
- // Result is in RCX: null -> not found, otherwise Bool::True or Bool::False. |
- |
- Label runtime_call; |
- __ cmpq(RCX, raw_null); |
- __ j(EQUAL, &runtime_call, Assembler::kNearJump); |
- __ CompareObject(RCX, bool_true); |
- __ j(EQUAL, is_instance_lbl); |
- __ jmp(is_not_instance_lbl); |
- __ Bind(&runtime_call); |
- return type_test_cache.raw(); |
+ const Register kTypeArgumentsReg = kNoRegister; |
+ const Register kTempReg = R10; |
+ return GenerateCallSubtypeTestStub(kTestTypeOneArg, |
+ kInstanceReg, |
+ kTypeArgumentsReg, |
+ kTempReg, |
+ is_instance_lbl, |
+ is_not_instance_lbl); |
} |
// Generates inlined check if 'type' is a type parameter or type itsef |
// RAX: instance (preserved). |
RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( |
- const AbstractType& type, |
intptr_t cid, |
intptr_t token_index, |
+ const AbstractType& type, |
Label* is_instance_lbl, |
Label* is_not_instance_lbl) { |
ASSERT(!type.IsInstantiated()); |
@@ -323,6 +328,11 @@ |
__ j(EQUAL, is_instance_lbl); |
__ cmpq(RDI, raw_null); |
__ j(EQUAL, is_instance_lbl); |
+ const Type& object_type = |
+ Type::ZoneHandle(Isolate::Current()->object_store()->object_type()); |
+ __ CompareObject(RDI, object_type); |
+ __ j(EQUAL, is_instance_lbl); |
+ |
// For Smi check quickly against int and num interface types. |
Label not_smi; |
__ testq(RAX, Immediate(kSmiTagMask)); // Value is Smi? |
@@ -331,56 +341,42 @@ |
__ j(EQUAL, is_instance_lbl); |
__ CompareObject(RDI, Type::ZoneHandle(Type::NumberInterface())); |
__ j(EQUAL, is_instance_lbl); |
+ // Smi must be handled in runtime. |
__ jmp(&fall_through); |
+ |
__ Bind(¬_smi); |
// RDX: instantiator type arguments. |
// RAX: instance. |
+ const Register kInstanceReg = RAX; |
+ const Register kTypeArgumentsReg = RDX; |
+ const Register kTempReg = R10; |
const SubtypeTestCache& type_test_cache = |
- SubtypeTestCache::ZoneHandle(SubtypeTestCache::New()); |
- __ LoadObject(R10, type_test_cache); |
- __ pushq(R10); // Subtype test cache. |
- __ pushq(RAX); // Instance |
- __ pushq(RDX); // Instantiator type arguments. |
- __ call(&StubCode::Subtype3TestCacheLabel()); |
- __ popq(RDX); // Discard type arguments. |
- __ popq(RAX); // Restore receiver. |
- __ popq(RDX); // Discard subtype test cache. |
- // Result is in RCX: null -> not found, otherwise Bool::True or Bool::False. |
- __ cmpq(RCX, raw_null); |
- __ j(EQUAL, &fall_through, Assembler::kNearJump); |
- const Bool& bool_true = Bool::ZoneHandle(Bool::True()); |
- __ CompareObject(RCX, bool_true); |
- __ j(EQUAL, is_instance_lbl); |
- __ jmp(is_not_instance_lbl); |
+ 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()) { |
- Label fall_through; |
- __ testq(RAX, Immediate(kSmiTagMask)); // Is instance Smi? |
+ const Register kInstanceReg = RAX; |
+ const Register kTypeArgumentsReg = RDX; |
+ __ testq(kInstanceReg, Immediate(kSmiTagMask)); // Is instance Smi? |
__ j(ZERO, is_not_instance_lbl); |
- __ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments. |
+ __ movq(kTypeArgumentsReg, Address(RSP, 0)); // Instantiator type args. |
// Uninstantiated type class is known at compile time, but the type |
// arguments are determined at runtime by the instantiator. |
- const SubtypeTestCache& type_test_cache = |
- SubtypeTestCache::ZoneHandle(SubtypeTestCache::New()); |
- __ LoadObject(R10, type_test_cache); |
- __ pushq(R10); // Subtype test cache. |
- __ pushq(RAX); // Instance. |
- __ pushq(RDX); // Instantiator type arguments. |
- __ call(&StubCode::Subtype3TestCacheLabel()); |
- __ popq(RDX); // Discard type arguments. |
- __ popq(RAX); // Restore receiver. |
- __ popq(RDX); // Discard subtype test cache. |
- // Result is in RCX: null -> not found, otherwise Bool::True or Bool::False. |
- __ cmpq(RCX, raw_null); |
- __ j(EQUAL, &fall_through, Assembler::kNearJump); |
- const Bool& bool_true = Bool::ZoneHandle(Bool::True()); |
- __ CompareObject(RCX, bool_true); |
- __ j(EQUAL, is_instance_lbl); |
- __ jmp(is_not_instance_lbl); |
- __ Bind(&fall_through); |
- return type_test_cache.raw(); |
+ const Register kTempReg = R10; |
+ return GenerateCallSubtypeTestStub(kTestTypeThreeArgs, |
+ kInstanceReg, |
+ kTypeArgumentsReg, |
+ kTempReg, |
+ is_instance_lbl, |
+ is_not_instance_lbl); |
} |
return SubtypeTestCache::null(); |
} |
@@ -426,9 +422,9 @@ |
is_instance_lbl, is_not_instance_lbl); |
} |
} else { |
- return GenerateUninstantiatedTypeTest(type, |
- cid, |
+ return GenerateUninstantiatedTypeTest(cid, |
token_index, |
+ type, |
is_instance_lbl, |
is_not_instance_lbl); |
} |