// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #if !defined(DART_PRECOMPILED_RUNTIME) #include "vm/deferred_objects.h" #include "vm/code_patcher.h" #include "vm/compiler/jit/compiler.h" #include "vm/deopt_instructions.h" #include "vm/flags.h" #include "vm/object.h" #include "vm/object_store.h" namespace dart { DECLARE_FLAG(bool, trace_deoptimization); DECLARE_FLAG(bool, trace_deoptimization_verbose); void DeferredDouble::Materialize(DeoptContext* deopt_context) { DoublePtr* double_slot = reinterpret_cast(slot()); *double_slot = Double::New(value()); if (FLAG_trace_deoptimization_verbose) { OS::PrintErr("materializing double at %" Px ": %g\n", reinterpret_cast(slot()), value()); } } void DeferredMint::Materialize(DeoptContext* deopt_context) { MintPtr* mint_slot = reinterpret_cast(slot()); ASSERT(!Smi::IsValid(value())); Mint& mint = Mint::Handle(); mint ^= Integer::New(value()); *mint_slot = mint.ptr(); if (FLAG_trace_deoptimization_verbose) { OS::PrintErr("materializing mint at %" Px ": %" Pd64 "\n", reinterpret_cast(slot()), value()); } } void DeferredFloat32x4::Materialize(DeoptContext* deopt_context) { Float32x4Ptr* float32x4_slot = reinterpret_cast(slot()); Float32x4Ptr raw_float32x4 = Float32x4::New(value()); *float32x4_slot = raw_float32x4; if (FLAG_trace_deoptimization_verbose) { float x = raw_float32x4->untag()->x(); float y = raw_float32x4->untag()->y(); float z = raw_float32x4->untag()->z(); float w = raw_float32x4->untag()->w(); OS::PrintErr("materializing Float32x4 at %" Px ": %g,%g,%g,%g\n", reinterpret_cast(slot()), x, y, z, w); } } void DeferredFloat64x2::Materialize(DeoptContext* deopt_context) { Float64x2Ptr* float64x2_slot = reinterpret_cast(slot()); Float64x2Ptr raw_float64x2 = Float64x2::New(value()); *float64x2_slot = raw_float64x2; if (FLAG_trace_deoptimization_verbose) { double x = raw_float64x2->untag()->x(); double y = raw_float64x2->untag()->y(); OS::PrintErr("materializing Float64x2 at %" Px ": %g,%g\n", reinterpret_cast(slot()), x, y); } } void DeferredInt32x4::Materialize(DeoptContext* deopt_context) { Int32x4Ptr* int32x4_slot = reinterpret_cast(slot()); Int32x4Ptr raw_int32x4 = Int32x4::New(value()); *int32x4_slot = raw_int32x4; if (FLAG_trace_deoptimization_verbose) { uint32_t x = raw_int32x4->untag()->x(); uint32_t y = raw_int32x4->untag()->y(); uint32_t z = raw_int32x4->untag()->z(); uint32_t w = raw_int32x4->untag()->w(); OS::PrintErr("materializing Int32x4 at %" Px ": %x,%x,%x,%x\n", reinterpret_cast(slot()), x, y, z, w); } } void DeferredObjectRef::Materialize(DeoptContext* deopt_context) { DeferredObject* obj = deopt_context->GetDeferredObject(index()); *slot() = obj->object(); if (FLAG_trace_deoptimization_verbose) { const Class& cls = Class::Handle(IsolateGroup::Current()->class_table()->At( Object::Handle(obj->object()).GetClassId())); OS::PrintErr("writing instance of class %s ref at %" Px ".\n", cls.ToCString(), reinterpret_cast(slot())); } } void DeferredRetAddr::Materialize(DeoptContext* deopt_context) { Thread* thread = deopt_context->thread(); Zone* zone = deopt_context->zone(); Function& function = Function::Handle(zone); function ^= deopt_context->ObjectAt(index_); const Error& error = Error::Handle(zone, Compiler::EnsureUnoptimizedCode(thread, function)); if (!error.IsNull()) { Exceptions::PropagateError(error); } const Code& code = Code::Handle(zone, function.unoptimized_code()); uword continue_at_pc = code.GetPcForDeoptId(deopt_id_, UntaggedPcDescriptors::kDeopt); if (continue_at_pc == 0) { FATAL("Can't locate continuation PC for deoptid %" Pd " within %s\n", deopt_id_, function.ToFullyQualifiedCString()); } uword* dest_addr = reinterpret_cast(slot()); *dest_addr = continue_at_pc; if (FLAG_trace_deoptimization_verbose) { OS::PrintErr("materializing return addr at 0x%" Px ": 0x%" Px "\n", reinterpret_cast(slot()), continue_at_pc); } uword pc = code.GetPcForDeoptId(deopt_id_, UntaggedPcDescriptors::kIcCall); if (pc != 0) { // If the deoptimization happened at an IC call, update the IC data // to avoid repeated deoptimization at the same site next time around. // We cannot use CodePatcher::GetInstanceCallAt because the call site // may have switched to from referencing an ICData to a target Code or // MegamorphicCache. ICData& ic_data = ICData::Handle(zone, function.FindICData(deopt_id_)); ic_data.AddDeoptReason(deopt_context->deopt_reason()); // Propagate the reason to all ICData-s with same deopt_id since // only unoptimized-code ICData (IC calls) are propagated. function.SetDeoptReasonForAll(ic_data.deopt_id(), deopt_context->deopt_reason()); } else { if (deopt_context->HasDeoptFlag(ICData::kHoisted)) { // Prevent excessive deoptimization. function.SetProhibitsInstructionHoisting(true); } if (deopt_context->HasDeoptFlag(ICData::kGeneralized)) { function.SetProhibitsBoundsCheckGeneralization(true); } } } void DeferredPcMarker::Materialize(DeoptContext* deopt_context) { Thread* thread = deopt_context->thread(); Zone* zone = deopt_context->zone(); uword* dest_addr = reinterpret_cast(slot()); Function& function = Function::Handle(zone); function ^= deopt_context->ObjectAt(index_); ASSERT(!function.IsNull()); SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock()); const Error& error = Error::Handle(zone, Compiler::EnsureUnoptimizedCode(thread, function)); if (!error.IsNull()) { Exceptions::PropagateError(error); } const Code& code = Code::Handle(zone, function.unoptimized_code()); ASSERT(!code.IsNull()); ASSERT(function.HasCode()); *reinterpret_cast(dest_addr) = code.ptr(); if (FLAG_trace_deoptimization_verbose) { THR_Print("materializing pc marker at 0x%" Px ": %s, %s\n", reinterpret_cast(slot()), code.ToCString(), function.ToCString()); } // Increment the deoptimization counter. This effectively increments each // function occurring in the optimized frame. if (deopt_context->deoptimizing_code()) { function.set_deoptimization_counter(function.deoptimization_counter() + 1); } if (FLAG_trace_deoptimization || FLAG_trace_deoptimization_verbose) { THR_Print("Deoptimizing '%s' (count %d)\n", function.ToFullyQualifiedCString(), function.deoptimization_counter()); } // Clear invocation counter so that hopefully the function gets reoptimized // only after more feedback has been collected. function.SetUsageCounter(0); if (function.HasOptimizedCode()) { function.SwitchToUnoptimizedCode(); } } void DeferredPp::Materialize(DeoptContext* deopt_context) { Thread* thread = deopt_context->thread(); Zone* zone = deopt_context->zone(); Function& function = Function::Handle(zone); function ^= deopt_context->ObjectAt(index_); ASSERT(!function.IsNull()); const Error& error = Error::Handle(zone, Compiler::EnsureUnoptimizedCode(thread, function)); if (!error.IsNull()) { Exceptions::PropagateError(error); } const Code& code = Code::Handle(zone, function.unoptimized_code()); ASSERT(!code.IsNull()); ASSERT(code.GetObjectPool() != Object::null()); *slot() = code.GetObjectPool(); if (FLAG_trace_deoptimization_verbose) { OS::PrintErr("materializing pp at 0x%" Px ": 0x%" Px "\n", reinterpret_cast(slot()), static_cast(code.GetObjectPool())); } } ObjectPtr DeferredObject::object() { if (object_ == nullptr) { Create(); } return object_->ptr(); } void DeferredObject::Create() { if (object_ != nullptr) { return; } Class& cls = Class::Handle(); cls ^= GetClass(); switch (cls.id()) { case kClosureCid: { const intptr_t length_and_flags = Smi::Cast(Object::Handle(GetLengthOrShape())).Value(); if (FLAG_trace_deoptimization_verbose) { OS::PrintErr("materializing closure with length and flags %" Px " (%" Px ", %" Pd " fields)\n", length_and_flags, reinterpret_cast(args_), field_count_); } object_ = &Closure::ZoneHandle(Closure::New(length_and_flags)); } break; case kContextCid: { const intptr_t num_variables = Smi::Cast(Object::Handle(GetLengthOrShape())).Value(); if (FLAG_trace_deoptimization_verbose) { OS::PrintErr( "materializing context of length %" Pd " (%" Px ", %" Pd " vars)\n", num_variables, reinterpret_cast(args_), field_count_); } object_ = &Context::ZoneHandle(Context::New(num_variables)); } break; case kArrayCid: { const intptr_t num_elements = Smi::Cast(Object::Handle(GetLengthOrShape())).Value(); if (FLAG_trace_deoptimization_verbose) { OS::PrintErr("materializing array of length %" Pd " (%" Px ", %" Pd " elements)\n", num_elements, reinterpret_cast(args_), field_count_); } object_ = &Array::ZoneHandle(Array::New(num_elements)); } break; case kRecordCid: { const RecordShape shape(Smi::RawCast(GetLengthOrShape())); if (FLAG_trace_deoptimization_verbose) { OS::PrintErr( "materializing record of shape %" Px " (%" Px ", %" Pd " fields)\n", shape.AsInt(), reinterpret_cast(args_), field_count_); } object_ = &Record::ZoneHandle(Record::New(shape)); } break; default: if (IsTypedDataClassId(cls.id())) { const intptr_t num_elements = Smi::Cast(Object::Handle(GetLengthOrShape())).Value(); if (FLAG_trace_deoptimization_verbose) { OS::PrintErr("materializing typed data cid %" Pd " of length %" Pd " (%" Px ", %" Pd " elements)\n", cls.id(), num_elements, reinterpret_cast(args_), field_count_); } object_ = &TypedData::ZoneHandle(TypedData::New(cls.id(), num_elements)); } else { if (FLAG_trace_deoptimization_verbose) { OS::PrintErr( "materializing instance of %s (%" Px ", %" Pd " fields)\n", cls.ToCString(), reinterpret_cast(args_), field_count_); } object_ = &Instance::ZoneHandle(Instance::New(cls)); } } } static intptr_t ToContextIndex(intptr_t offset_in_bytes) { intptr_t result = (offset_in_bytes - Context::variable_offset(0)) / Context::kBytesPerElement; ASSERT(result >= 0); return result; } void DeferredObject::Fill() { Create(); // Ensure instance is created. Class& cls = Class::Handle(); cls ^= GetClass(); switch (cls.id()) { case kClosureCid: { const Closure& closure = Closure::Cast(*object_); Smi& offset = Smi::Handle(); Object& value = Object::Handle(); for (intptr_t i = 0; i < field_count_; i++) { offset ^= GetFieldOffset(i); if (offset.Value() == Closure::function_offset()) { Function& function = Function::Handle(); function ^= GetValue(i); closure.set_function(function); if (FLAG_trace_deoptimization_verbose) { OS::PrintErr(" closure@function (offset %" Pd ") <- %s\n", offset.Value(), function.ToCString()); } } else { ASSERT(offset.Value() >= Closure::element_offset(0)); const intptr_t index = Closure::element_index_at_offset(offset.Value()); value = GetValue(i); closure.SetElementAt(index, value); if (FLAG_trace_deoptimization_verbose) { OS::PrintErr(" closure@%" Pd " (offset %" Pd ") <- %s\n", index, offset.Value(), value.ToCString()); } } } } break; case kContextCid: { const Context& context = Context::Cast(*object_); Smi& offset = Smi::Handle(); Object& value = Object::Handle(); for (intptr_t i = 0; i < field_count_; i++) { offset ^= GetFieldOffset(i); if (offset.Value() == Context::parent_offset()) { // Copy parent. Context& parent = Context::Handle(); parent ^= GetValue(i); context.set_parent(parent); if (FLAG_trace_deoptimization_verbose) { OS::PrintErr(" ctx@parent (offset %" Pd ") <- %s\n", offset.Value(), parent.ToCString()); } } else { intptr_t context_index = ToContextIndex(offset.Value()); value = GetValue(i); context.SetAt(context_index, value); if (FLAG_trace_deoptimization_verbose) { OS::PrintErr(" ctx@%" Pd " (offset %" Pd ") <- %s\n", context_index, offset.Value(), value.ToCString()); } } } } break; case kArrayCid: { const Array& array = Array::Cast(*object_); Smi& offset = Smi::Handle(); Object& value = Object::Handle(); for (intptr_t i = 0; i < field_count_; i++) { offset ^= GetFieldOffset(i); if (offset.Value() == Array::type_arguments_offset()) { TypeArguments& type_args = TypeArguments::Handle(); type_args ^= GetValue(i); array.SetTypeArguments(type_args); if (FLAG_trace_deoptimization_verbose) { OS::PrintErr(" array@type_args (offset %" Pd ") <- %s\n", offset.Value(), type_args.ToCString()); } } else { const intptr_t index = Array::index_at_offset(offset.Value()); value = GetValue(i); array.SetAt(index, value); if (FLAG_trace_deoptimization_verbose) { OS::PrintErr(" array@%" Pd " (offset %" Pd ") <- %s\n", index, offset.Value(), value.ToCString()); } } } } break; case kPointerCid: { auto* const zone = Thread::Current()->zone(); const int kDataIndex = 0; const int kTypeArgIndex = 1; ASSERT(field_count_ == 2); ASSERT( Smi::Cast(Object::Handle(zone, GetFieldOffset(kDataIndex))).Value() == PointerBase::data_offset()); ASSERT(Smi::Cast(Object::Handle(zone, GetFieldOffset(kTypeArgIndex))) .Value() == Pointer::type_arguments_offset()); const auto& pointer = Pointer::Cast(*object_); const size_t address = Integer::Cast(Object::Handle(zone, GetValue(kDataIndex))).Value(); pointer.SetNativeAddress(address); const auto& type_args = TypeArguments::Handle( zone, IsolateGroup::Current()->object_store()->type_argument_never()); pointer.SetTypeArguments(type_args); if (FLAG_trace_deoptimization_verbose) { OS::PrintErr(" pointer@data <- 0x%" Px "\n", address); OS::PrintErr(" pointer@type_args <- %s\n", type_args.ToCString()); } } break; case kRecordCid: { const Record& record = Record::Cast(*object_); Smi& offset = Smi::Handle(); Object& value = Object::Handle(); for (intptr_t i = 0; i < field_count_; i++) { offset ^= GetFieldOffset(i); const intptr_t index = Record::field_index_at_offset(offset.Value()); value = GetValue(i); record.SetFieldAt(index, value); if (FLAG_trace_deoptimization_verbose) { OS::PrintErr(" record@%" Pd " (offset %" Pd ") <- %s\n", index, offset.Value(), value.ToCString()); } } } break; default: if (IsTypedDataClassId(cls.id())) { const TypedData& typed_data = TypedData::Cast(*object_); Smi& offset = Smi::Handle(); Object& value = Object::Handle(); const auto cid = cls.id(); for (intptr_t i = 0; i < field_count_; i++) { offset ^= GetFieldOffset(i); const intptr_t element_offset = offset.Value(); value = GetValue(i); switch (cid) { case kTypedDataInt8ArrayCid: typed_data.SetInt8( element_offset, static_cast(Integer::Cast(value).Value())); break; case kTypedDataUint8ArrayCid: case kTypedDataUint8ClampedArrayCid: typed_data.SetUint8( element_offset, static_cast(Integer::Cast(value).Value())); break; case kTypedDataInt16ArrayCid: typed_data.SetInt16( element_offset, static_cast(Integer::Cast(value).Value())); break; case kTypedDataUint16ArrayCid: typed_data.SetUint16( element_offset, static_cast(Integer::Cast(value).Value())); break; case kTypedDataInt32ArrayCid: typed_data.SetInt32( element_offset, static_cast(Integer::Cast(value).Value())); break; case kTypedDataUint32ArrayCid: typed_data.SetUint32( element_offset, static_cast(Integer::Cast(value).Value())); break; case kTypedDataInt64ArrayCid: typed_data.SetInt64(element_offset, Integer::Cast(value).Value()); break; case kTypedDataUint64ArrayCid: typed_data.SetUint64( element_offset, static_cast(Integer::Cast(value).Value())); break; case kTypedDataFloat32ArrayCid: typed_data.SetFloat32( element_offset, static_cast(Double::Cast(value).value())); break; case kTypedDataFloat64ArrayCid: typed_data.SetFloat64(element_offset, Double::Cast(value).value()); break; case kTypedDataFloat32x4ArrayCid: typed_data.SetFloat32x4(element_offset, Float32x4::Cast(value).value()); break; case kTypedDataInt32x4ArrayCid: typed_data.SetInt32x4(element_offset, Int32x4::Cast(value).value()); break; case kTypedDataFloat64x2ArrayCid: typed_data.SetFloat64x2(element_offset, Float64x2::Cast(value).value()); break; default: UNREACHABLE(); } if (FLAG_trace_deoptimization_verbose) { OS::PrintErr(" typed_data (offset %" Pd ") <- %s\n", element_offset, value.ToCString()); } } } else { const Instance& obj = Instance::Cast(*object_); Smi& offset = Smi::Handle(); Field& field = Field::Handle(); Object& value = Object::Handle(); const Array& offset_map = Array::Handle(cls.OffsetToFieldMap()); for (intptr_t i = 0; i < field_count_; i++) { offset ^= GetFieldOffset(i); field ^= offset_map.At(offset.Value() / kCompressedWordSize); value = GetValue(i); ASSERT((value.ptr() != Object::sentinel().ptr()) || (!field.IsNull() && field.is_late())); if (!field.IsNull() && (value.ptr() != Object::sentinel().ptr())) { obj.SetField(field, value); if (FLAG_trace_deoptimization_verbose) { OS::PrintErr(" %s <- %s\n", String::Handle(field.name()).ToCString(), value.ToCString()); } } else { // In addition to the type arguments vector we can also have lazy // materialization of e.g. _ByteDataView objects which don't have // explicit fields in Dart (all accesses to the fields are done via // recognized native methods). ASSERT(offset.Value() < cls.host_instance_size()); obj.SetFieldAtOffset(offset.Value(), value); if (FLAG_trace_deoptimization_verbose) { OS::PrintErr( " %s @ offset(%" Pd ") <- %s\n", (field.IsNull() ? "null Field" : String::Handle(field.name()).ToCString()), offset.Value(), value.ToCString()); } } } if (obj.IsTypedDataView()) { // The data field does not get materialized for typed data views // because it is not a safe untagged pointer and must be recomputed. TypedDataView::Cast(obj).RecomputeDataField(); } } break; } } } // namespace dart #endif // !defined(DART_PRECOMPILED_RUNTIME)