Chromium Code Reviews| Index: runtime/vm/intermediate_language_arm64.cc |
| diff --git a/runtime/vm/intermediate_language_arm64.cc b/runtime/vm/intermediate_language_arm64.cc |
| index 809387a744d883aa406ca30307236b1f1874a926..bcbc1eef5910eda0058104ec0d95a9ed642f2d9d 100644 |
| --- a/runtime/vm/intermediate_language_arm64.cc |
| +++ b/runtime/vm/intermediate_language_arm64.cc |
| @@ -1358,48 +1358,58 @@ static void LoadValueCid(FlowGraphCompiler* compiler, |
| } |
| -LocationSummary* GuardFieldInstr::MakeLocationSummary(Isolate* isolate, |
| - bool opt) const { |
| +LocationSummary* GuardFieldClassInstr::MakeLocationSummary(Isolate* isolate, |
| + bool opt) const { |
| const intptr_t kNumInputs = 1; |
| LocationSummary* summary = new(isolate) LocationSummary( |
| isolate, kNumInputs, 0, LocationSummary::kNoCall); |
| summary->set_in(0, Location::RequiresRegister()); |
| - const bool field_has_length = field().needs_length_check(); |
| - summary->AddTemp(Location::RequiresRegister()); |
| - summary->AddTemp(Location::RequiresRegister()); |
| - const bool need_field_temp_reg = |
| - field_has_length || (field().guarded_cid() == kIllegalCid); |
| - if (need_field_temp_reg) { |
| + |
| + const intptr_t value_cid = value()->Type()->ToCid(); |
| + const intptr_t field_cid = field().guarded_cid(); |
| + |
| + const bool emit_full_guard = |
| + !opt || (field_cid == kIllegalCid); |
| + |
| + const bool needs_value_cid_temp_reg = emit_full_guard || |
| + ((value_cid == kDynamicCid) && (field_cid != kSmiCid)); |
| + |
| + const bool needs_field_temp_reg = emit_full_guard; |
| + |
| + if (needs_value_cid_temp_reg) { |
| summary->AddTemp(Location::RequiresRegister()); |
| } |
| + |
| + if (needs_field_temp_reg) { |
| + summary->AddTemp(Location::RequiresRegister()); |
| + } |
| + |
| return summary; |
| } |
| -void GuardFieldInstr::EmitNativeCode(FlowGraphCompiler* compiler) { |
| +void GuardFieldClassInstr::EmitNativeCode(FlowGraphCompiler* compiler) { |
| + const intptr_t value_cid = value()->Type()->ToCid(); |
| const intptr_t field_cid = field().guarded_cid(); |
| const intptr_t nullability = field().is_nullable() ? kNullCid : kIllegalCid; |
| - const intptr_t field_length = field().guarded_list_length(); |
| - const bool field_has_length = field().needs_length_check(); |
| - const bool needs_field_temp_reg = |
| - field_has_length || (field().guarded_cid() == kIllegalCid); |
| - if (field_has_length) { |
| - // Currently, we should only see final fields that remember length. |
| - ASSERT(field().is_final()); |
| - } |
| if (field_cid == kDynamicCid) { |
| ASSERT(!compiler->is_optimizing()); |
| return; // Nothing to emit. |
| } |
| - const intptr_t value_cid = value()->Type()->ToCid(); |
| + const bool emit_full_guard = |
| + !compiler->is_optimizing() || (field_cid == kIllegalCid); |
| - const Register value_reg = locs()->in(0).reg(); |
| + const bool needs_value_cid_temp_reg = emit_full_guard || |
| + ((value_cid == kDynamicCid) && (field_cid != kSmiCid)); |
| - const Register value_cid_reg = locs()->temp(0).reg(); |
| + const bool needs_field_temp_reg = emit_full_guard; |
| - const Register temp_reg = locs()->temp(1).reg(); |
| + Register value_reg = locs()->in(0).reg(); |
|
zra
2014/05/29 18:20:34
const Register here and below.
Vyacheslav Egorov (Google)
2014/06/02 10:48:37
Done.
|
| + |
| + Register value_cid_reg = needs_value_cid_temp_reg ? |
| + locs()->temp(0).reg() : kNoRegister; |
| Register field_reg = needs_field_temp_reg ? |
| locs()->temp(locs()->temp_count() - 1).reg() : kNoRegister; |
| @@ -1411,91 +1421,19 @@ void GuardFieldInstr::EmitNativeCode(FlowGraphCompiler* compiler) { |
| Label* fail = (deopt != NULL) ? deopt : &fail_label; |
| - if (!compiler->is_optimizing() || (field_cid == kIllegalCid)) { |
| - if (!compiler->is_optimizing() && (field_reg == kNoRegister)) { |
| - // Currently we can't have different location summaries for optimized |
| - // and non-optimized code. So instead we manually pick up a register |
| - // that is known to be free because we know how non-optimizing compiler |
| - // allocates registers. |
| - field_reg = R2; |
| - ASSERT((field_reg != value_reg) && (field_reg != value_cid_reg)); |
| - } |
| - |
| + if (emit_full_guard) { |
| __ LoadObject(field_reg, Field::ZoneHandle(field().raw()), PP); |
| FieldAddress field_cid_operand(field_reg, Field::guarded_cid_offset()); |
| FieldAddress field_nullability_operand( |
| field_reg, Field::is_nullable_offset()); |
| - FieldAddress field_length_operand( |
| - field_reg, Field::guarded_list_length_offset()); |
| - |
| - ASSERT(value_cid_reg != kNoRegister); |
| - ASSERT((value_cid_reg != value_reg) && (field_reg != value_cid_reg)); |
| if (value_cid == kDynamicCid) { |
| LoadValueCid(compiler, value_cid_reg, value_reg); |
| Label skip_length_check; |
| __ ldr(TMP, field_cid_operand); |
| __ CompareRegisters(value_cid_reg, TMP); |
| - __ b(&skip_length_check, NE); |
| - if (field_has_length) { |
| - ASSERT(temp_reg != kNoRegister); |
| - // Field guard may have remembered list length, check it. |
| - if ((field_cid == kArrayCid) || (field_cid == kImmutableArrayCid)) { |
| - __ LoadFieldFromOffset( |
| - temp_reg, value_reg, Array::length_offset(), PP); |
| - __ CompareImmediate(temp_reg, Smi::RawValue(field_length), PP); |
| - } else if (RawObject::IsTypedDataClassId(field_cid)) { |
| - __ LoadFieldFromOffset( |
| - temp_reg, value_reg, TypedData::length_offset(), PP); |
| - __ CompareImmediate(temp_reg, Smi::RawValue(field_length), PP); |
| - } else { |
| - ASSERT(field_cid == kIllegalCid); |
| - ASSERT(field_length == Field::kUnknownFixedLength); |
| - // At compile time we do not know the type of the field nor its |
| - // length. At execution time we may have set the class id and |
| - // list length so we compare the guarded length with the |
| - // list length here, without this check the list length could change |
| - // without triggering a deoptimization. |
| - Label check_array, length_compared, no_fixed_length; |
| - // If length is negative the length guard is either disabled or |
| - // has not been initialized, either way it is safe to skip the |
| - // length check. |
| - __ ldr(TMP, field_length_operand); |
| - __ CompareImmediate(TMP, 0, PP); |
| - __ b(&skip_length_check, LT); |
| - __ CompareImmediate(value_cid_reg, kNullCid, PP); |
| - __ b(&no_fixed_length, EQ); |
| - // Check for typed data array. |
| - __ CompareImmediate(value_cid_reg, kTypedDataInt32x4ArrayCid, PP); |
| - __ b(&no_fixed_length, GT); |
| - __ CompareImmediate(value_cid_reg, kTypedDataInt8ArrayCid, PP); |
| - // Could still be a regular array. |
| - __ b(&check_array, LT); |
| - __ LoadFieldFromOffset( |
| - temp_reg, value_reg, TypedData::length_offset(), PP); |
| - __ ldr(TMP, field_length_operand); |
| - __ CompareRegisters(temp_reg, TMP); |
| - __ b(&length_compared); |
| - // Check for regular array. |
| - __ Bind(&check_array); |
| - __ CompareImmediate(value_cid_reg, kImmutableArrayCid, PP); |
| - __ b(&no_fixed_length, GT); |
| - __ CompareImmediate(value_cid_reg, kArrayCid, PP); |
| - __ b(&no_fixed_length, LT); |
| - __ LoadFieldFromOffset( |
| - temp_reg, value_reg, Array::length_offset(), PP); |
| - __ ldr(TMP, field_length_operand); |
| - __ CompareRegisters(temp_reg, TMP); |
| - __ b(&length_compared); |
| - __ Bind(&no_fixed_length); |
| - __ b(fail); |
| - __ Bind(&length_compared); |
| - // Following branch cannot not occur, fall through. |
| - } |
| - __ b(fail, NE); |
| - } |
| - __ Bind(&skip_length_check); |
| + __ b(&ok, EQ); |
| __ ldr(TMP, field_nullability_operand); |
| __ CompareRegisters(value_cid_reg, TMP); |
| } else if (value_cid == kNullCid) { |
| @@ -1505,100 +1443,39 @@ void GuardFieldInstr::EmitNativeCode(FlowGraphCompiler* compiler) { |
| Label skip_length_check; |
| __ ldr(value_cid_reg, field_cid_operand); |
| __ CompareImmediate(value_cid_reg, value_cid, PP); |
| - __ b(&skip_length_check, NE); |
| - if (field_has_length) { |
| - ASSERT(value_cid_reg != kNoRegister); |
| - ASSERT(temp_reg != kNoRegister); |
| - if ((value_cid == kArrayCid) || (value_cid == kImmutableArrayCid)) { |
| - __ LoadFieldFromOffset( |
| - temp_reg, value_reg, Array::length_offset(), PP); |
| - __ CompareImmediate(temp_reg, Smi::RawValue(field_length), PP); |
| - } else if (RawObject::IsTypedDataClassId(value_cid)) { |
| - __ LoadFieldFromOffset( |
| - temp_reg, value_reg, TypedData::length_offset(), PP); |
| - __ CompareImmediate(temp_reg, Smi::RawValue(field_length), PP); |
| - } else if (field_cid != kIllegalCid) { |
| - ASSERT(field_cid != value_cid); |
| - ASSERT(field_length >= 0); |
| - // Field has a known class id and length. At compile time it is |
| - // known that the value's class id is not a fixed length list. |
| - __ b(fail); |
| - } else { |
| - ASSERT(field_cid == kIllegalCid); |
| - ASSERT(field_length == Field::kUnknownFixedLength); |
| - // Following jump cannot not occur, fall through. |
| - } |
| - __ b(fail, NE); |
| - } |
| - // Not identical, possibly null. |
| - __ Bind(&skip_length_check); |
| } |
| __ b(&ok, EQ); |
| - __ ldr(TMP, field_cid_operand); |
| - __ CompareImmediate(TMP, kIllegalCid, PP); |
| - __ b(fail, NE); |
| + // Check if the tracked state of the guarded field can be initialized |
| + // inline. If the field needs length check we fall through to runtime |
| + // which is responsible for computing offset of the length field |
| + // based on the class id. |
| + // Length guard will be emitted separately when needed via GuardFieldLength |
| + // instruction after GuardFieldClass. |
| + if (!field().needs_length_check()) { |
| + // Uninitialized field can be handled inline. Check if the |
| + // field is still unitialized. |
| + __ ldr(TMP, field_cid_operand); |
| + __ CompareImmediate(TMP, kIllegalCid, PP); |
| + __ b(fail, NE); |
| - if (value_cid == kDynamicCid) { |
| - __ str(value_cid_reg, field_cid_operand); |
| - __ str(value_cid_reg, field_nullability_operand); |
| - if (field_has_length) { |
| - Label check_array, length_set, no_fixed_length; |
| - __ CompareImmediate(value_cid_reg, kNullCid, PP); |
| - __ b(&no_fixed_length, EQ); |
| - // Check for typed data array. |
| - __ CompareImmediate(value_cid_reg, kTypedDataInt32x4ArrayCid, PP); |
| - __ b(&no_fixed_length, GT); |
| - __ CompareImmediate(value_cid_reg, kTypedDataInt8ArrayCid, PP); |
| - // Could still be a regular array. |
| - __ b(&check_array, LT); |
| - // Destroy value_cid_reg (safe because we are finished with it). |
| - __ LoadFieldFromOffset( |
| - value_cid_reg, value_reg, TypedData::length_offset(), PP); |
| - __ str(value_cid_reg, field_length_operand); |
| - __ b(&length_set); // Updated field length typed data array. |
| - // Check for regular array. |
| - __ Bind(&check_array); |
| - __ CompareImmediate(value_cid_reg, kImmutableArrayCid, PP); |
| - __ b(&no_fixed_length, GT); |
| - __ CompareImmediate(value_cid_reg, kArrayCid, PP); |
| - __ b(&no_fixed_length, LT); |
| - // Destroy value_cid_reg (safe because we are finished with it). |
| - __ LoadFieldFromOffset( |
| - value_cid_reg, value_reg, Array::length_offset(), PP); |
| - __ str(value_cid_reg, field_length_operand); |
| - // Updated field length from regular array. |
| - __ b(&length_set); |
| - __ Bind(&no_fixed_length); |
| - __ LoadImmediate(TMP, Smi::RawValue(Field::kNoFixedLength), PP); |
| - __ str(TMP, field_length_operand); |
| - __ Bind(&length_set); |
| + if (value_cid == kDynamicCid) { |
| + __ str(value_cid_reg, field_cid_operand); |
| + __ str(value_cid_reg, field_nullability_operand); |
| + } else { |
| + __ LoadImmediate(TMP, value_cid, PP); |
| + __ str(TMP, field_cid_operand); |
| + __ str(TMP, field_nullability_operand); |
| } |
| - } else { |
| - __ LoadImmediate(TMP, value_cid, PP); |
| - __ str(TMP, field_cid_operand); |
| - __ str(TMP, field_nullability_operand); |
| - if (field_has_length) { |
| - if ((value_cid == kArrayCid) || (value_cid == kImmutableArrayCid)) { |
| - // Destroy value_cid_reg (safe because we are finished with it). |
| - __ LoadFieldFromOffset( |
| - value_cid_reg, value_reg, Array::length_offset(), PP); |
| - __ str(value_cid_reg, field_length_operand); |
| - } else if (RawObject::IsTypedDataClassId(value_cid)) { |
| - // Destroy value_cid_reg (safe because we are finished with it). |
| - __ LoadFieldFromOffset( |
| - value_cid_reg, value_reg, TypedData::length_offset(), PP); |
| - __ str(value_cid_reg, field_length_operand); |
| - } else { |
| - __ LoadImmediate(TMP, Smi::RawValue(Field::kNoFixedLength), PP); |
| - __ str(TMP, field_length_operand); |
| - } |
| + |
| + if (deopt == NULL) { |
| + ASSERT(!compiler->is_optimizing()); |
| + __ b(&ok); |
| } |
| } |
| if (deopt == NULL) { |
| ASSERT(!compiler->is_optimizing()); |
| - __ b(&ok); |
| __ Bind(fail); |
| __ LoadFieldFromOffset(TMP, field_reg, Field::guarded_cid_offset(), PP); |
| @@ -1613,12 +1490,10 @@ void GuardFieldInstr::EmitNativeCode(FlowGraphCompiler* compiler) { |
| } else { |
| ASSERT(compiler->is_optimizing()); |
| ASSERT(deopt != NULL); |
| + |
| // Field guard class has been initialized and is known. |
| - if (field_reg != kNoRegister) { |
| - __ LoadObject(field_reg, Field::ZoneHandle(field().raw()), PP); |
| - } |
| if (value_cid == kDynamicCid) { |
| - // Field's guarded class id is fixed by value's class id is not known. |
| + // Value's class id is not known. |
| __ tsti(value_reg, kSmiTagMask); |
| if (field_cid != kSmiCid) { |
| @@ -1627,57 +1502,96 @@ void GuardFieldInstr::EmitNativeCode(FlowGraphCompiler* compiler) { |
| __ CompareImmediate(value_cid_reg, field_cid, PP); |
| } |
| - if (field_has_length) { |
| - __ b(fail, NE); |
| - // Classes are same, perform guarded list length check. |
| - ASSERT(field_reg != kNoRegister); |
| - ASSERT(value_cid_reg != kNoRegister); |
| - FieldAddress field_length_operand( |
| - field_reg, Field::guarded_list_length_offset()); |
| - if ((field_cid == kArrayCid) || (field_cid == kImmutableArrayCid)) { |
| - // Destroy value_cid_reg (safe because we are finished with it). |
| - __ LoadFieldFromOffset( |
| - value_cid_reg, value_reg, Array::length_offset(), PP); |
| - } else if (RawObject::IsTypedDataClassId(field_cid)) { |
| - // Destroy value_cid_reg (safe because we are finished with it). |
| - __ LoadFieldFromOffset( |
| - value_cid_reg, value_reg, TypedData::length_offset(), PP); |
| - } |
| - __ ldr(TMP, field_length_operand); |
| - __ CompareRegisters(value_cid_reg, TMP); |
| - } |
| - |
| if (field().is_nullable() && (field_cid != kNullCid)) { |
| __ b(&ok, EQ); |
| __ CompareObject(value_reg, Object::null_object(), PP); |
| } |
| + |
| __ b(fail, NE); |
| } else { |
| // Both value's and field's class id is known. |
| - if ((value_cid != field_cid) && (value_cid != nullability)) { |
| - __ b(fail); |
| - } else if (field_has_length && (value_cid == field_cid)) { |
| - ASSERT(value_cid_reg != kNoRegister); |
| - if ((field_cid == kArrayCid) || (field_cid == kImmutableArrayCid)) { |
| - // Destroy value_cid_reg (safe because we are finished with it). |
| - __ LoadFieldFromOffset( |
| - value_cid_reg, value_reg, Array::length_offset(), PP); |
| - } else if (RawObject::IsTypedDataClassId(field_cid)) { |
| - // Destroy value_cid_reg (safe because we are finished with it). |
| - __ LoadFieldFromOffset( |
| - value_cid_reg, value_reg, TypedData::length_offset(), PP); |
| - } |
| - __ CompareImmediate(value_cid_reg, field_length, PP); |
| - __ b(fail, NE); |
| - } else { |
| - UNREACHABLE(); |
| - } |
| + ASSERT((value_cid != field_cid) && (value_cid != nullability)); |
| + __ b(fail); |
| } |
| } |
| __ Bind(&ok); |
| } |
| +LocationSummary* GuardFieldLengthInstr::MakeLocationSummary(Isolate* isolate, |
| + bool opt) const { |
| + const intptr_t kNumInputs = 1; |
| + LocationSummary* summary = new(isolate) LocationSummary( |
| + isolate, kNumInputs, 0, LocationSummary::kNoCall); |
| + summary->set_in(0, Location::RequiresRegister()); |
| + |
| + if (!opt || (field().guarded_list_length() == Field::kUnknownFixedLength)) { |
| + // We need temporaries for field object, length offset and expected length. |
| + summary->AddTemp(Location::RequiresRegister()); |
|
zra
2014/05/29 18:20:34
arm64 has a second assembler temporary TMP2 that y
Vyacheslav Egorov (Google)
2014/06/02 10:48:37
I would prefer not to use it for long living value
|
| + summary->AddTemp(Location::RequiresRegister()); |
| + summary->AddTemp(Location::RequiresRegister()); |
| + } |
| + |
| + return summary; |
| +} |
| + |
| + |
| +void GuardFieldLengthInstr::EmitNativeCode(FlowGraphCompiler* compiler) { |
| + if (field().guarded_list_length() == Field::kNoFixedLength) { |
| + ASSERT(!compiler->is_optimizing()); |
| + return; // Nothing to emit. |
| + } |
| + |
| + Register value_reg = locs()->in(0).reg(); |
| + |
| + if (!compiler->is_optimizing() || |
| + (field().guarded_list_length() == Field::kUnknownFixedLength)) { |
| + Register field_reg = locs()->temp(0).reg(); |
| + Register offset_reg = locs()->temp(1).reg(); |
| + Register length_reg = locs()->temp(2).reg(); |
| + |
| + Label ok; |
| + |
| + __ LoadObject(field_reg, Field::ZoneHandle(field().raw()), PP); |
| + |
| + __ ldrsb(offset_reg, FieldAddress(field_reg, |
| + Field::guarded_list_length_in_object_offset_offset())); |
| + __ ldr(length_reg, FieldAddress(field_reg, |
| + Field::guarded_list_length_offset())); |
| + |
| + __ tst(offset_reg, Operand(offset_reg)); |
| + __ b(&ok, MI); |
| + |
| + // Load the length from the value. GuardFieldClass already verified that |
| + // value's class matches guarded class id of the field. |
| + // offset_reg contains offset already corrected by -kHeapObjectTag that is |
| + // why we use Address instead of FieldAddress. |
| + __ ldr(TMP, Address(value_reg, offset_reg)); |
| + __ CompareRegisters(length_reg, TMP); |
| + __ b(&ok, EQ); |
| + |
| + __ Push(field_reg); |
| + __ Push(value_reg); |
| + __ CallRuntime(kUpdateFieldCidRuntimeEntry, 2); |
| + __ Drop(2); // Drop the field and the value. |
| + |
| + __ Bind(&ok); |
| + } else { |
| + Label* deopt = compiler->AddDeoptStub(deopt_id(), ICData::kDeoptGuardField); |
| + |
| + ASSERT(compiler->is_optimizing()); |
| + ASSERT(field().guarded_list_length() >= 0); |
| + ASSERT(field().guarded_list_length_in_object_offset() != |
| + Field::kUnknownLengthOffset); |
| + |
| + __ ldr(TMP, FieldAddress(value_reg, |
| + field().guarded_list_length_in_object_offset())); |
| + __ CompareImmediate(TMP, Smi::RawValue(field().guarded_list_length()), PP); |
| + __ b(deopt, NE); |
| + } |
| +} |
| + |
| + |
| class StoreInstanceFieldSlowPath : public SlowPathCode { |
| public: |
| StoreInstanceFieldSlowPath(StoreInstanceFieldInstr* instruction, |