| 1 | // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file | 
|---|
| 2 | // for details. All rights reserved. Use of this source code is governed by a | 
|---|
| 3 | // BSD-style license that can be found in the LICENSE file. | 
|---|
| 4 |  | 
|---|
| 5 | #if !defined(DART_PRECOMPILED_RUNTIME) | 
|---|
| 6 |  | 
|---|
| 7 | #include "vm/deferred_objects.h" | 
|---|
| 8 |  | 
|---|
| 9 | #include "vm/code_patcher.h" | 
|---|
| 10 | #include "vm/compiler/jit/compiler.h" | 
|---|
| 11 | #include "vm/deopt_instructions.h" | 
|---|
| 12 | #include "vm/flags.h" | 
|---|
| 13 | #include "vm/object.h" | 
|---|
| 14 |  | 
|---|
| 15 | namespace dart { | 
|---|
| 16 |  | 
|---|
| 17 | DECLARE_FLAG(bool, trace_deoptimization); | 
|---|
| 18 | DECLARE_FLAG(bool, trace_deoptimization_verbose); | 
|---|
| 19 |  | 
|---|
| 20 | void DeferredDouble::Materialize(DeoptContext* deopt_context) { | 
|---|
| 21 | DoublePtr* double_slot = reinterpret_cast<DoublePtr*>(slot()); | 
|---|
| 22 | *double_slot = Double::New(value()); | 
|---|
| 23 |  | 
|---|
| 24 | if (FLAG_trace_deoptimization_verbose) { | 
|---|
| 25 | OS::PrintErr( "materializing double at %"Px ": %g\n", | 
|---|
| 26 | reinterpret_cast<uword>(slot()), value()); | 
|---|
| 27 | } | 
|---|
| 28 | } | 
|---|
| 29 |  | 
|---|
| 30 | void DeferredMint::Materialize(DeoptContext* deopt_context) { | 
|---|
| 31 | MintPtr* mint_slot = reinterpret_cast<MintPtr*>(slot()); | 
|---|
| 32 | ASSERT(!Smi::IsValid(value())); | 
|---|
| 33 | Mint& mint = Mint::Handle(); | 
|---|
| 34 | mint ^= Integer::New(value()); | 
|---|
| 35 | *mint_slot = mint.raw(); | 
|---|
| 36 |  | 
|---|
| 37 | if (FLAG_trace_deoptimization_verbose) { | 
|---|
| 38 | OS::PrintErr( "materializing mint at %"Px ": %"Pd64 "\n", | 
|---|
| 39 | reinterpret_cast<uword>(slot()), value()); | 
|---|
| 40 | } | 
|---|
| 41 | } | 
|---|
| 42 |  | 
|---|
| 43 | void DeferredFloat32x4::Materialize(DeoptContext* deopt_context) { | 
|---|
| 44 | Float32x4Ptr* float32x4_slot = reinterpret_cast<Float32x4Ptr*>(slot()); | 
|---|
| 45 | Float32x4Ptr raw_float32x4 = Float32x4::New(value()); | 
|---|
| 46 | *float32x4_slot = raw_float32x4; | 
|---|
| 47 |  | 
|---|
| 48 | if (FLAG_trace_deoptimization_verbose) { | 
|---|
| 49 | float x = raw_float32x4->ptr()->x(); | 
|---|
| 50 | float y = raw_float32x4->ptr()->y(); | 
|---|
| 51 | float z = raw_float32x4->ptr()->z(); | 
|---|
| 52 | float w = raw_float32x4->ptr()->w(); | 
|---|
| 53 | OS::PrintErr( "materializing Float32x4 at %"Px ": %g,%g,%g,%g\n", | 
|---|
| 54 | reinterpret_cast<uword>(slot()), x, y, z, w); | 
|---|
| 55 | } | 
|---|
| 56 | } | 
|---|
| 57 |  | 
|---|
| 58 | void DeferredFloat64x2::Materialize(DeoptContext* deopt_context) { | 
|---|
| 59 | Float64x2Ptr* float64x2_slot = reinterpret_cast<Float64x2Ptr*>(slot()); | 
|---|
| 60 | Float64x2Ptr raw_float64x2 = Float64x2::New(value()); | 
|---|
| 61 | *float64x2_slot = raw_float64x2; | 
|---|
| 62 |  | 
|---|
| 63 | if (FLAG_trace_deoptimization_verbose) { | 
|---|
| 64 | double x = raw_float64x2->ptr()->x(); | 
|---|
| 65 | double y = raw_float64x2->ptr()->y(); | 
|---|
| 66 | OS::PrintErr( "materializing Float64x2 at %"Px ": %g,%g\n", | 
|---|
| 67 | reinterpret_cast<uword>(slot()), x, y); | 
|---|
| 68 | } | 
|---|
| 69 | } | 
|---|
| 70 |  | 
|---|
| 71 | void DeferredInt32x4::Materialize(DeoptContext* deopt_context) { | 
|---|
| 72 | Int32x4Ptr* int32x4_slot = reinterpret_cast<Int32x4Ptr*>(slot()); | 
|---|
| 73 | Int32x4Ptr raw_int32x4 = Int32x4::New(value()); | 
|---|
| 74 | *int32x4_slot = raw_int32x4; | 
|---|
| 75 |  | 
|---|
| 76 | if (FLAG_trace_deoptimization_verbose) { | 
|---|
| 77 | uint32_t x = raw_int32x4->ptr()->x(); | 
|---|
| 78 | uint32_t y = raw_int32x4->ptr()->y(); | 
|---|
| 79 | uint32_t z = raw_int32x4->ptr()->z(); | 
|---|
| 80 | uint32_t w = raw_int32x4->ptr()->w(); | 
|---|
| 81 | OS::PrintErr( "materializing Int32x4 at %"Px ": %x,%x,%x,%x\n", | 
|---|
| 82 | reinterpret_cast<uword>(slot()), x, y, z, w); | 
|---|
| 83 | } | 
|---|
| 84 | } | 
|---|
| 85 |  | 
|---|
| 86 | void DeferredObjectRef::Materialize(DeoptContext* deopt_context) { | 
|---|
| 87 | DeferredObject* obj = deopt_context->GetDeferredObject(index()); | 
|---|
| 88 | *slot() = obj->object(); | 
|---|
| 89 | if (FLAG_trace_deoptimization_verbose) { | 
|---|
| 90 | const Class& cls = Class::Handle(Isolate::Current()->class_table()->At( | 
|---|
| 91 | Object::Handle(obj->object()).GetClassId())); | 
|---|
| 92 | OS::PrintErr( "writing instance of class %s ref at %"Px ".\n", | 
|---|
| 93 | cls.ToCString(), reinterpret_cast<uword>(slot())); | 
|---|
| 94 | } | 
|---|
| 95 | } | 
|---|
| 96 |  | 
|---|
| 97 | void DeferredRetAddr::Materialize(DeoptContext* deopt_context) { | 
|---|
| 98 | Thread* thread = deopt_context->thread(); | 
|---|
| 99 | Zone* zone = deopt_context->zone(); | 
|---|
| 100 | Function& function = Function::Handle(zone); | 
|---|
| 101 | function ^= deopt_context->ObjectAt(index_); | 
|---|
| 102 | const Error& error = | 
|---|
| 103 | Error::Handle(zone, Compiler::EnsureUnoptimizedCode(thread, function)); | 
|---|
| 104 | if (!error.IsNull()) { | 
|---|
| 105 | Exceptions::PropagateError(error); | 
|---|
| 106 | } | 
|---|
| 107 | const Code& code = Code::Handle(zone, function.unoptimized_code()); | 
|---|
| 108 |  | 
|---|
| 109 | uword continue_at_pc = | 
|---|
| 110 | code.GetPcForDeoptId(deopt_id_, PcDescriptorsLayout::kDeopt); | 
|---|
| 111 | if (continue_at_pc == 0) { | 
|---|
| 112 | FATAL2( "Can't locate continuation PC for deoptid %"Pd " within %s\n", | 
|---|
| 113 | deopt_id_, function.ToFullyQualifiedCString()); | 
|---|
| 114 | } | 
|---|
| 115 | uword* dest_addr = reinterpret_cast<uword*>(slot()); | 
|---|
| 116 | *dest_addr = continue_at_pc; | 
|---|
| 117 |  | 
|---|
| 118 | if (FLAG_trace_deoptimization_verbose) { | 
|---|
| 119 | OS::PrintErr( "materializing return addr at 0x%"Px ": 0x%"Px "\n", | 
|---|
| 120 | reinterpret_cast<uword>(slot()), continue_at_pc); | 
|---|
| 121 | } | 
|---|
| 122 |  | 
|---|
| 123 | uword pc = code.GetPcForDeoptId(deopt_id_, PcDescriptorsLayout::kIcCall); | 
|---|
| 124 | if (pc != 0) { | 
|---|
| 125 | // If the deoptimization happened at an IC call, update the IC data | 
|---|
| 126 | // to avoid repeated deoptimization at the same site next time around. | 
|---|
| 127 | // We cannot use CodePatcher::GetInstanceCallAt because the call site | 
|---|
| 128 | // may have switched to from referencing an ICData to a target Code or | 
|---|
| 129 | // MegamorphicCache. | 
|---|
| 130 | ICData& ic_data = ICData::Handle(zone, function.FindICData(deopt_id_)); | 
|---|
| 131 | ic_data.AddDeoptReason(deopt_context->deopt_reason()); | 
|---|
| 132 | // Propagate the reason to all ICData-s with same deopt_id since | 
|---|
| 133 | // only unoptimized-code ICData (IC calls) are propagated. | 
|---|
| 134 | function.SetDeoptReasonForAll(ic_data.deopt_id(), | 
|---|
| 135 | deopt_context->deopt_reason()); | 
|---|
| 136 | } else { | 
|---|
| 137 | if (deopt_context->HasDeoptFlag(ICData::kHoisted)) { | 
|---|
| 138 | // Prevent excessive deoptimization. | 
|---|
| 139 | function.SetProhibitsHoistingCheckClass(true); | 
|---|
| 140 | } | 
|---|
| 141 |  | 
|---|
| 142 | if (deopt_context->HasDeoptFlag(ICData::kGeneralized)) { | 
|---|
| 143 | function.SetProhibitsBoundsCheckGeneralization(true); | 
|---|
| 144 | } | 
|---|
| 145 | } | 
|---|
| 146 | } | 
|---|
| 147 |  | 
|---|
| 148 | void DeferredPcMarker::Materialize(DeoptContext* deopt_context) { | 
|---|
| 149 | Thread* thread = deopt_context->thread(); | 
|---|
| 150 | Zone* zone = deopt_context->zone(); | 
|---|
| 151 | uword* dest_addr = reinterpret_cast<uword*>(slot()); | 
|---|
| 152 | Function& function = Function::Handle(zone); | 
|---|
| 153 | function ^= deopt_context->ObjectAt(index_); | 
|---|
| 154 | ASSERT(!function.IsNull()); | 
|---|
| 155 | const Error& error = | 
|---|
| 156 | Error::Handle(zone, Compiler::EnsureUnoptimizedCode(thread, function)); | 
|---|
| 157 | if (!error.IsNull()) { | 
|---|
| 158 | Exceptions::PropagateError(error); | 
|---|
| 159 | } | 
|---|
| 160 | const Code& code = Code::Handle(zone, function.unoptimized_code()); | 
|---|
| 161 | ASSERT(!code.IsNull()); | 
|---|
| 162 | ASSERT(function.HasCode()); | 
|---|
| 163 | *reinterpret_cast<ObjectPtr*>(dest_addr) = code.raw(); | 
|---|
| 164 |  | 
|---|
| 165 | if (FLAG_trace_deoptimization_verbose) { | 
|---|
| 166 | THR_Print( "materializing pc marker at 0x%"Px ": %s, %s\n", | 
|---|
| 167 | reinterpret_cast<uword>(slot()), code.ToCString(), | 
|---|
| 168 | function.ToCString()); | 
|---|
| 169 | } | 
|---|
| 170 |  | 
|---|
| 171 | // Increment the deoptimization counter. This effectively increments each | 
|---|
| 172 | // function occurring in the optimized frame. | 
|---|
| 173 | if (deopt_context->deoptimizing_code()) { | 
|---|
| 174 | function.set_deoptimization_counter(function.deoptimization_counter() + 1); | 
|---|
| 175 | } | 
|---|
| 176 | if (FLAG_trace_deoptimization || FLAG_trace_deoptimization_verbose) { | 
|---|
| 177 | THR_Print( "Deoptimizing '%s' (count %d)\n", | 
|---|
| 178 | function.ToFullyQualifiedCString(), | 
|---|
| 179 | function.deoptimization_counter()); | 
|---|
| 180 | } | 
|---|
| 181 | // Clear invocation counter so that hopefully the function gets reoptimized | 
|---|
| 182 | // only after more feedback has been collected. | 
|---|
| 183 | function.SetUsageCounter(0); | 
|---|
| 184 | if (function.HasOptimizedCode()) { | 
|---|
| 185 | function.SwitchToUnoptimizedCode(); | 
|---|
| 186 | } | 
|---|
| 187 | } | 
|---|
| 188 |  | 
|---|
| 189 | void DeferredPp::Materialize(DeoptContext* deopt_context) { | 
|---|
| 190 | Thread* thread = deopt_context->thread(); | 
|---|
| 191 | Zone* zone = deopt_context->zone(); | 
|---|
| 192 | Function& function = Function::Handle(zone); | 
|---|
| 193 | function ^= deopt_context->ObjectAt(index_); | 
|---|
| 194 | ASSERT(!function.IsNull()); | 
|---|
| 195 | const Error& error = | 
|---|
| 196 | Error::Handle(zone, Compiler::EnsureUnoptimizedCode(thread, function)); | 
|---|
| 197 | if (!error.IsNull()) { | 
|---|
| 198 | Exceptions::PropagateError(error); | 
|---|
| 199 | } | 
|---|
| 200 | const Code& code = Code::Handle(zone, function.unoptimized_code()); | 
|---|
| 201 | ASSERT(!code.IsNull()); | 
|---|
| 202 | ASSERT(code.GetObjectPool() != Object::null()); | 
|---|
| 203 | *slot() = code.GetObjectPool(); | 
|---|
| 204 |  | 
|---|
| 205 | if (FLAG_trace_deoptimization_verbose) { | 
|---|
| 206 | OS::PrintErr( "materializing pp at 0x%"Px ": 0x%"Px "\n", | 
|---|
| 207 | reinterpret_cast<uword>(slot()), | 
|---|
| 208 | static_cast<uword>(code.GetObjectPool())); | 
|---|
| 209 | } | 
|---|
| 210 | } | 
|---|
| 211 |  | 
|---|
| 212 | ObjectPtr DeferredObject::object() { | 
|---|
| 213 | if (object_ == NULL) { | 
|---|
| 214 | Create(); | 
|---|
| 215 | } | 
|---|
| 216 | return object_->raw(); | 
|---|
| 217 | } | 
|---|
| 218 |  | 
|---|
| 219 | void DeferredObject::Create() { | 
|---|
| 220 | if (object_ != NULL) { | 
|---|
| 221 | return; | 
|---|
| 222 | } | 
|---|
| 223 |  | 
|---|
| 224 | Class& cls = Class::Handle(); | 
|---|
| 225 | cls ^= GetClass(); | 
|---|
| 226 |  | 
|---|
| 227 | if (cls.raw() == Object::context_class()) { | 
|---|
| 228 | intptr_t num_variables = Smi::Cast(Object::Handle(GetLength())).Value(); | 
|---|
| 229 | if (FLAG_trace_deoptimization_verbose) { | 
|---|
| 230 | OS::PrintErr( "materializing context of length %"Pd " (%"Px ", %"Pd | 
|---|
| 231 | " vars)\n", | 
|---|
| 232 | num_variables, reinterpret_cast<uword>(args_), field_count_); | 
|---|
| 233 | } | 
|---|
| 234 | object_ = &Context::ZoneHandle(Context::New(num_variables)); | 
|---|
| 235 |  | 
|---|
| 236 | } else { | 
|---|
| 237 | if (FLAG_trace_deoptimization_verbose) { | 
|---|
| 238 | OS::PrintErr( "materializing instance of %s (%"Px ", %"Pd " fields)\n", | 
|---|
| 239 | cls.ToCString(), reinterpret_cast<uword>(args_), | 
|---|
| 240 | field_count_); | 
|---|
| 241 | } | 
|---|
| 242 |  | 
|---|
| 243 | object_ = &Instance::ZoneHandle(Instance::New(cls)); | 
|---|
| 244 | } | 
|---|
| 245 | } | 
|---|
| 246 |  | 
|---|
| 247 | static intptr_t ToContextIndex(intptr_t offset_in_bytes) { | 
|---|
| 248 | intptr_t result = (offset_in_bytes - Context::variable_offset(0)) / kWordSize; | 
|---|
| 249 | ASSERT(result >= 0); | 
|---|
| 250 | return result; | 
|---|
| 251 | } | 
|---|
| 252 |  | 
|---|
| 253 | void DeferredObject::Fill() { | 
|---|
| 254 | Create();  // Ensure instance is created. | 
|---|
| 255 |  | 
|---|
| 256 | Class& cls = Class::Handle(); | 
|---|
| 257 | cls ^= GetClass(); | 
|---|
| 258 |  | 
|---|
| 259 | if (cls.raw() == Object::context_class()) { | 
|---|
| 260 | const Context& context = Context::Cast(*object_); | 
|---|
| 261 |  | 
|---|
| 262 | Smi& offset = Smi::Handle(); | 
|---|
| 263 | Object& value = Object::Handle(); | 
|---|
| 264 |  | 
|---|
| 265 | for (intptr_t i = 0; i < field_count_; i++) { | 
|---|
| 266 | offset ^= GetFieldOffset(i); | 
|---|
| 267 | if (offset.Value() == Context::parent_offset()) { | 
|---|
| 268 | // Copy parent. | 
|---|
| 269 | Context& parent = Context::Handle(); | 
|---|
| 270 | parent ^= GetValue(i); | 
|---|
| 271 | context.set_parent(parent); | 
|---|
| 272 | if (FLAG_trace_deoptimization_verbose) { | 
|---|
| 273 | OS::PrintErr( "    ctx@parent (offset %"Pd ") <- %s\n", | 
|---|
| 274 | offset.Value(), value.ToCString()); | 
|---|
| 275 | } | 
|---|
| 276 | } else { | 
|---|
| 277 | intptr_t context_index = ToContextIndex(offset.Value()); | 
|---|
| 278 | value = GetValue(i); | 
|---|
| 279 | context.SetAt(context_index, value); | 
|---|
| 280 | if (FLAG_trace_deoptimization_verbose) { | 
|---|
| 281 | OS::PrintErr( "    ctx@%"Pd " (offset %"Pd ") <- %s\n", | 
|---|
| 282 | context_index, offset.Value(), value.ToCString()); | 
|---|
| 283 | } | 
|---|
| 284 | } | 
|---|
| 285 | } | 
|---|
| 286 | } else { | 
|---|
| 287 | const Instance& obj = Instance::Cast(*object_); | 
|---|
| 288 |  | 
|---|
| 289 | Smi& offset = Smi::Handle(); | 
|---|
| 290 | Field& field = Field::Handle(); | 
|---|
| 291 | Object& value = Object::Handle(); | 
|---|
| 292 | const Array& offset_map = Array::Handle(cls.OffsetToFieldMap()); | 
|---|
| 293 |  | 
|---|
| 294 | for (intptr_t i = 0; i < field_count_; i++) { | 
|---|
| 295 | offset ^= GetFieldOffset(i); | 
|---|
| 296 | field ^= offset_map.At(offset.Value() / kWordSize); | 
|---|
| 297 | value = GetValue(i); | 
|---|
| 298 | if (!field.IsNull()) { | 
|---|
| 299 | obj.SetField(field, value); | 
|---|
| 300 | if (FLAG_trace_deoptimization_verbose) { | 
|---|
| 301 | OS::PrintErr( "    %s <- %s\n", | 
|---|
| 302 | String::Handle(field.name()).ToCString(), | 
|---|
| 303 | value.ToCString()); | 
|---|
| 304 | } | 
|---|
| 305 | } else { | 
|---|
| 306 | // In addition to the type arguments vector we can also have lazy | 
|---|
| 307 | // materialization of e.g. _ByteDataView objects which don't have | 
|---|
| 308 | // explicit fields in Dart (all accesses to the fields are done via | 
|---|
| 309 | // recognized native methods). | 
|---|
| 310 | ASSERT(offset.Value() < cls.host_instance_size()); | 
|---|
| 311 | obj.SetFieldAtOffset(offset.Value(), value); | 
|---|
| 312 | if (FLAG_trace_deoptimization_verbose) { | 
|---|
| 313 | OS::PrintErr( "    null Field @ offset(%"Pd ") <- %s\n", | 
|---|
| 314 | offset.Value(), value.ToCString()); | 
|---|
| 315 | } | 
|---|
| 316 | } | 
|---|
| 317 | } | 
|---|
| 318 | } | 
|---|
| 319 | } | 
|---|
| 320 |  | 
|---|
| 321 | }  // namespace dart | 
|---|
| 322 |  | 
|---|
| 323 | #endif  // !defined(DART_PRECOMPILED_RUNTIME) | 
|---|
| 324 |  | 
|---|