OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "vm/deopt_instructions.h" | 5 #include "vm/deopt_instructions.h" |
6 | 6 |
7 #include "vm/assembler.h" | 7 #include "vm/assembler.h" |
8 #include "vm/code_patcher.h" | 8 #include "vm/code_patcher.h" |
9 #include "vm/intermediate_language.h" | 9 #include "vm/intermediate_language.h" |
10 #include "vm/locations.h" | 10 #include "vm/locations.h" |
11 #include "vm/parser.h" | 11 #include "vm/parser.h" |
12 #include "vm/stack_frame.h" | 12 #include "vm/stack_frame.h" |
13 | 13 |
14 namespace dart { | 14 namespace dart { |
15 | 15 |
16 DEFINE_FLAG(bool, compress_deopt_info, true, | 16 DEFINE_FLAG(bool, compress_deopt_info, true, |
17 "Compress the size of the deoptimization info for optimized code."); | 17 "Compress the size of the deoptimization info for optimized code."); |
18 DECLARE_FLAG(bool, trace_deoptimization); | 18 DECLARE_FLAG(bool, trace_deoptimization); |
19 DECLARE_FLAG(bool, trace_deoptimization_verbose); | 19 DECLARE_FLAG(bool, trace_deoptimization_verbose); |
20 | 20 |
21 DeoptContext::DeoptContext(const Array& object_table, | 21 |
22 intptr_t num_args, | 22 DeoptContext::DeoptContext(const StackFrame* frame, |
23 DeoptReasonId deopt_reason) | 23 const Code& code, |
24 : object_table_(object_table.raw()), | 24 DestFrameOptions dest_options, |
| 25 fpu_register_t* fpu_registers, |
| 26 intptr_t* cpu_registers) |
| 27 : code_(code.raw()), |
| 28 object_table_(Array::null()), |
| 29 deopt_info_(DeoptInfo::null()), |
| 30 dest_frame_is_allocated_(false), |
25 dest_frame_(NULL), | 31 dest_frame_(NULL), |
26 dest_frame_size_(0), | 32 dest_frame_size_(0), |
27 source_frame_is_copy_(false), | 33 source_frame_is_allocated_(false), |
28 source_frame_(NULL), | 34 source_frame_(NULL), |
29 source_frame_size_(0), | 35 source_frame_size_(0), |
30 cpu_registers_(NULL), | 36 cpu_registers_(cpu_registers), |
31 fpu_registers_(NULL), | 37 fpu_registers_(fpu_registers), |
32 num_args_(num_args), | 38 num_args_(0), |
33 deopt_reason_(deopt_reason), | 39 deopt_reason_(kDeoptUnknown), |
34 isolate_(Isolate::Current()), | 40 isolate_(Isolate::Current()), |
35 deferred_boxes_(NULL), | 41 deferred_boxes_(NULL), |
36 deferred_object_refs_(NULL), | 42 deferred_object_refs_(NULL), |
37 deferred_objects_count_(0), | 43 deferred_objects_count_(0), |
38 deferred_objects_(NULL) { | 44 deferred_objects_(NULL) { |
| 45 object_table_ = code.object_table(); |
| 46 |
| 47 intptr_t deopt_reason = kDeoptUnknown; |
| 48 const DeoptInfo& deopt_info = |
| 49 DeoptInfo::Handle(code.GetDeoptInfoAtPc(frame->pc(), &deopt_reason)); |
| 50 ASSERT(!deopt_info.IsNull()); |
| 51 deopt_info_ = deopt_info.raw(); |
| 52 deopt_reason_ = static_cast<DeoptReasonId>(deopt_reason); |
| 53 |
| 54 const Function& function = Function::Handle(code.function()); |
| 55 |
| 56 // Do not include incoming arguments if there are optional arguments |
| 57 // (they are copied into local space at method entry). |
| 58 num_args_ = |
| 59 function.HasOptionalParameters() ? 0 : function.num_fixed_parameters(); |
| 60 |
| 61 // The fixed size section of the (fake) Dart frame called via a stub by the |
| 62 // optimized function contains FP, PP (ARM and MIPS only), PC-marker and |
| 63 // return-address. This section is copied as well, so that its contained |
| 64 // values can be updated before returning to the deoptimized function. |
| 65 source_frame_size_ = |
| 66 + kDartFrameFixedSize // For saved values below sp. |
| 67 + ((frame->fp() - frame->sp()) / kWordSize) // For frame size incl. sp. |
| 68 + 1 // For fp. |
| 69 + kParamEndSlotFromFp // For saved values above fp. |
| 70 + num_args_; // For arguments. |
| 71 source_frame_ = reinterpret_cast<intptr_t*>( |
| 72 frame->sp() - (kDartFrameFixedSize * kWordSize)); |
| 73 |
| 74 if (dest_options == kDestIsOriginalFrame) { |
| 75 // Work from a copy of the source frame. |
| 76 intptr_t* original_frame = source_frame_; |
| 77 source_frame_ = new intptr_t[source_frame_size_]; |
| 78 ASSERT(source_frame_ != NULL); |
| 79 for (intptr_t i = 0; i < source_frame_size_; i++) { |
| 80 source_frame_[i] = original_frame[i]; |
| 81 } |
| 82 source_frame_is_allocated_ = true; |
| 83 } |
| 84 caller_fp_ = GetSourceFp(); |
| 85 |
| 86 dest_frame_size_ = deopt_info.FrameSize(); |
| 87 |
| 88 if (dest_options == kDestIsAllocated) { |
| 89 dest_frame_ = new intptr_t[dest_frame_size_]; |
| 90 ASSERT(source_frame_ != NULL); |
| 91 for (intptr_t i = 0; i < dest_frame_size_; i++) { |
| 92 dest_frame_[i] = 0; |
| 93 } |
| 94 dest_frame_is_allocated_ = true; |
| 95 } |
| 96 |
| 97 if (FLAG_trace_deoptimization || FLAG_trace_deoptimization_verbose) { |
| 98 OS::PrintErr( |
| 99 "Deoptimizing (reason %" Pd " '%s') at pc %#" Px " '%s' (count %d)\n", |
| 100 deopt_reason, |
| 101 DeoptReasonToText(deopt_reason_), |
| 102 frame->pc(), |
| 103 function.ToFullyQualifiedCString(), |
| 104 function.deoptimization_counter()); |
| 105 } |
39 } | 106 } |
40 | 107 |
41 | 108 |
42 DeoptContext::~DeoptContext() { | 109 DeoptContext::~DeoptContext() { |
43 // Delete memory for source frame and registers. | 110 // Delete memory for source frame and registers. |
44 if (source_frame_is_copy_) { | 111 if (source_frame_is_allocated_) { |
45 delete[] source_frame_; | 112 delete[] source_frame_; |
46 } | 113 } |
47 source_frame_ = NULL; | 114 source_frame_ = NULL; |
48 delete[] fpu_registers_; | 115 delete[] fpu_registers_; |
49 delete[] cpu_registers_; | 116 delete[] cpu_registers_; |
50 fpu_registers_ = NULL; | 117 fpu_registers_ = NULL; |
51 cpu_registers_ = NULL; | 118 cpu_registers_ = NULL; |
| 119 if (dest_frame_is_allocated_) { |
| 120 delete[] dest_frame_; |
| 121 } |
| 122 dest_frame_ = NULL; |
52 | 123 |
53 // Delete all deferred objects. | 124 // Delete all deferred objects. |
54 for (intptr_t i = 0; i < deferred_objects_count_; i++) { | 125 for (intptr_t i = 0; i < deferred_objects_count_; i++) { |
55 delete deferred_objects_[i]; | 126 delete deferred_objects_[i]; |
56 } | 127 } |
57 delete[] deferred_objects_; | 128 delete[] deferred_objects_; |
58 deferred_objects_ = NULL; | 129 deferred_objects_ = NULL; |
59 deferred_objects_count_ = 0; | 130 deferred_objects_count_ = 0; |
60 } | 131 } |
61 | 132 |
62 | 133 |
63 void DeoptContext::SetSourceArgs(intptr_t* frame_start, | |
64 intptr_t frame_size, | |
65 fpu_register_t* fpu_registers, | |
66 intptr_t* cpu_registers, | |
67 bool source_frame_is_copy) { | |
68 ASSERT(frame_start != NULL); | |
69 ASSERT(frame_size >= 0); | |
70 ASSERT(source_frame_ == NULL); | |
71 ASSERT(cpu_registers_ == NULL && fpu_registers_ == NULL); | |
72 source_frame_ = frame_start; | |
73 source_frame_size_ = frame_size; | |
74 caller_fp_ = GetSourceFp(); | |
75 cpu_registers_ = cpu_registers; | |
76 fpu_registers_ = fpu_registers; | |
77 source_frame_is_copy_ = source_frame_is_copy; | |
78 } | |
79 | |
80 | |
81 void DeoptContext::SetDestArgs(intptr_t* frame_start, | |
82 intptr_t frame_size) { | |
83 ASSERT(frame_start != NULL); | |
84 ASSERT(frame_size >= 0); | |
85 ASSERT(dest_frame_ == NULL); | |
86 dest_frame_ = frame_start; | |
87 dest_frame_size_ = frame_size; | |
88 } | |
89 | |
90 | |
91 void DeoptContext::VisitObjectPointers(ObjectPointerVisitor* visitor) { | 134 void DeoptContext::VisitObjectPointers(ObjectPointerVisitor* visitor) { |
92 visitor->VisitPointer(reinterpret_cast<RawObject**>(&object_table_)); | 135 visitor->VisitPointer(reinterpret_cast<RawObject**>(&object_table_)); |
| 136 visitor->VisitPointer(reinterpret_cast<RawObject**>(&deopt_info_)); |
| 137 |
| 138 // Visit any object pointers on the destination stack. |
| 139 if (dest_frame_is_allocated_) { |
| 140 for (intptr_t i = 0; i < dest_frame_size_; i++) { |
| 141 if (dest_frame_[i] != 0) { |
| 142 visitor->VisitPointer(reinterpret_cast<RawObject**>(&dest_frame_[i])); |
| 143 } |
| 144 } |
| 145 } |
93 } | 146 } |
94 | 147 |
95 | 148 |
| 149 intptr_t DeoptContext::DestStackAdjustment() const { |
| 150 return (dest_frame_size_ |
| 151 - kDartFrameFixedSize |
| 152 - num_args_ |
| 153 - kParamEndSlotFromFp |
| 154 - 1); // For fp. |
| 155 } |
| 156 |
| 157 |
96 intptr_t DeoptContext::GetSourceFp() const { | 158 intptr_t DeoptContext::GetSourceFp() const { |
97 return source_frame_[source_frame_size_ - 1 - num_args_ - | 159 return source_frame_[source_frame_size_ - 1 - num_args_ - |
98 kParamEndSlotFromFp]; | 160 kParamEndSlotFromFp]; |
99 } | 161 } |
100 | 162 |
101 | 163 |
102 intptr_t DeoptContext::GetSourcePp() const { | 164 intptr_t DeoptContext::GetSourcePp() const { |
103 return source_frame_[source_frame_size_ - 1 - num_args_ - | 165 return source_frame_[source_frame_size_ - 1 - num_args_ - |
104 kParamEndSlotFromFp + | 166 kParamEndSlotFromFp + |
105 StackFrame::SavedCallerPpSlotFromFp()]; | 167 StackFrame::SavedCallerPpSlotFromFp()]; |
106 } | 168 } |
107 | 169 |
108 | 170 |
109 intptr_t DeoptContext::GetSourcePc() const { | 171 intptr_t DeoptContext::GetSourcePc() const { |
110 return source_frame_[source_frame_size_ - num_args_ + kSavedPcSlotFromSp]; | 172 return source_frame_[source_frame_size_ - num_args_ + kSavedPcSlotFromSp]; |
111 } | 173 } |
112 | 174 |
113 | 175 |
114 intptr_t DeoptContext::GetCallerFp() const { | 176 intptr_t DeoptContext::GetCallerFp() const { |
115 return caller_fp_; | 177 return caller_fp_; |
116 } | 178 } |
117 | 179 |
118 | 180 |
119 void DeoptContext::SetCallerFp(intptr_t caller_fp) { | 181 void DeoptContext::SetCallerFp(intptr_t caller_fp) { |
120 caller_fp_ = caller_fp; | 182 caller_fp_ = caller_fp; |
121 } | 183 } |
122 | 184 |
123 | 185 |
| 186 static bool IsObjectInstruction(DeoptInstr::Kind kind) { |
| 187 switch (kind) { |
| 188 case DeoptInstr::kConstant: |
| 189 case DeoptInstr::kStackSlot: |
| 190 case DeoptInstr::kDoubleStackSlot: |
| 191 case DeoptInstr::kInt64StackSlot: |
| 192 case DeoptInstr::kFloat32x4StackSlot: |
| 193 case DeoptInstr::kUint32x4StackSlot: |
| 194 case DeoptInstr::kPp: |
| 195 case DeoptInstr::kCallerPp: |
| 196 case DeoptInstr::kMaterializedObjectRef: |
| 197 return true; |
| 198 |
| 199 case DeoptInstr::kRegister: |
| 200 case DeoptInstr::kFpuRegister: |
| 201 case DeoptInstr::kInt64FpuRegister: |
| 202 case DeoptInstr::kFloat32x4FpuRegister: |
| 203 case DeoptInstr::kUint32x4FpuRegister: |
| 204 // TODO(turnidge): Sometimes we encounter a deopt instruction |
| 205 // with a register source while deoptimizing frames during |
| 206 // debugging but we haven't saved our register set. This |
| 207 // happens specifically when using the VMService to inspect the |
| 208 // stack. In that case, the register values will have been |
| 209 // saved before the StackOverflow runtime call but we do not |
| 210 // actually keep track of which registers were saved and |
| 211 // restored. |
| 212 // |
| 213 // It is possible to save this information at the point of the |
| 214 // StackOverflow runtime call but would require a bit of magic |
| 215 // to either make sure that the registers are pushed on the |
| 216 // stack in a predictable fashion or that we save enough |
| 217 // information to recover them after the fact. |
| 218 // |
| 219 // For now, we punt on these instructions. |
| 220 return false; |
| 221 |
| 222 case DeoptInstr::kRetAddress: |
| 223 case DeoptInstr::kPcMarker: |
| 224 case DeoptInstr::kCallerFp: |
| 225 case DeoptInstr::kCallerPc: |
| 226 return false; |
| 227 |
| 228 case DeoptInstr::kSuffix: |
| 229 case DeoptInstr::kMaterializeObject: |
| 230 // We should not encounter these instructions when filling stack slots. |
| 231 UNIMPLEMENTED(); |
| 232 return false; |
| 233 } |
| 234 } |
| 235 |
| 236 |
| 237 void DeoptContext::FillDestFrame() { |
| 238 const Code& code = Code::Handle(code_); |
| 239 const DeoptInfo& deopt_info = DeoptInfo::Handle(deopt_info_); |
| 240 |
| 241 const intptr_t len = deopt_info.TranslationLength(); |
| 242 GrowableArray<DeoptInstr*> deopt_instructions(len); |
| 243 const Array& deopt_table = Array::Handle(code.deopt_info_array()); |
| 244 ASSERT(!deopt_table.IsNull()); |
| 245 deopt_info.ToInstructions(deopt_table, &deopt_instructions); |
| 246 |
| 247 const intptr_t frame_size = deopt_info.FrameSize(); |
| 248 |
| 249 // For now, we never place non-objects in the deoptimized frame if |
| 250 // the destination frame is a copy. This allows us to copy the |
| 251 // deoptimized frame into an Array. |
| 252 const bool objects_only = dest_frame_is_allocated_; |
| 253 |
| 254 // All kMaterializeObject instructions are emitted before the instructions |
| 255 // that describe stack frames. Skip them and defer materialization of |
| 256 // objects until the frame is fully reconstructed and it is safe to perform |
| 257 // GC. |
| 258 // Arguments (class of the instance to allocate and field-value pairs) are |
| 259 // described as part of the expression stack for the bottom-most deoptimized |
| 260 // frame. They will be used during materialization and removed from the stack |
| 261 // right before control switches to the unoptimized code. |
| 262 const intptr_t num_materializations = len - frame_size; |
| 263 PrepareForDeferredMaterialization(num_materializations); |
| 264 for (intptr_t from_index = 0, to_index = kDartFrameFixedSize; |
| 265 from_index < num_materializations; |
| 266 from_index++) { |
| 267 const intptr_t field_count = |
| 268 DeoptInstr::GetFieldCount(deopt_instructions[from_index]); |
| 269 intptr_t* args = GetDestFrameAddressAt(to_index); |
| 270 DeferredObject* obj = new DeferredObject(field_count, args); |
| 271 SetDeferredObjectAt(from_index, obj); |
| 272 to_index += obj->ArgumentCount(); |
| 273 } |
| 274 |
| 275 // Populate stack frames. |
| 276 for (intptr_t to_index = frame_size - 1, from_index = len - 1; |
| 277 to_index >= 0; |
| 278 to_index--, from_index--) { |
| 279 intptr_t* to_addr = GetDestFrameAddressAt(to_index); |
| 280 DeoptInstr* instr = deopt_instructions[from_index]; |
| 281 if (!objects_only || IsObjectInstruction(instr->kind())) { |
| 282 instr->Execute(this, to_addr); |
| 283 } else { |
| 284 *reinterpret_cast<RawObject**>(to_addr) = Object::null(); |
| 285 } |
| 286 } |
| 287 |
| 288 if (FLAG_trace_deoptimization_verbose) { |
| 289 intptr_t* start = dest_frame_; |
| 290 for (intptr_t i = 0; i < frame_size; i++) { |
| 291 OS::PrintErr("*%" Pd ". [%" Px "] %#014" Px " [%s]\n", |
| 292 i, |
| 293 reinterpret_cast<uword>(&start[i]), |
| 294 start[i], |
| 295 deopt_instructions[i + (len - frame_size)]->ToCString()); |
| 296 } |
| 297 } |
| 298 } |
| 299 |
| 300 |
124 static void FillDeferredSlots(DeferredSlot** slot_list) { | 301 static void FillDeferredSlots(DeferredSlot** slot_list) { |
125 DeferredSlot* slot = *slot_list; | 302 DeferredSlot* slot = *slot_list; |
126 *slot_list = NULL; | 303 *slot_list = NULL; |
127 | 304 |
128 while (slot != NULL) { | 305 while (slot != NULL) { |
129 DeferredSlot* current = slot; | 306 DeferredSlot* current = slot; |
130 slot = slot->next(); | 307 slot = slot->next(); |
131 | 308 |
132 current->Materialize(); | 309 current->Materialize(); |
133 | 310 |
134 delete current; | 311 delete current; |
135 } | 312 } |
136 } | 313 } |
137 | 314 |
138 | 315 |
139 // Materializes all deferred objects. Returns the total number of | 316 // Materializes all deferred objects. Returns the total number of |
140 // artificial arguments used during deoptimization. | 317 // artificial arguments used during deoptimization. |
141 intptr_t DeoptContext::MaterializeDeferredObjects() { | 318 intptr_t DeoptContext::MaterializeDeferredObjects() { |
142 // First materialize all unboxed "primitive" values (doubles, mints, simd) | 319 // First materialize all unboxed "primitive" values (doubles, mints, simd) |
143 // then materialize objects. The order is important: objects might be | 320 // then materialize objects. The order is important: objects might be |
144 // referencing boxes allocated on the first step. At the same time | 321 // referencing boxes allocated on the first step. At the same time |
145 // objects can't be referencing other deferred objects because storing | 322 // objects can't be referencing other deferred objects because storing |
146 // an object into a field is always conservatively treated as escaping by | 323 // an object into a field is always conservatively treated as escaping by |
147 // allocation sinking and load forwarding. | 324 // allocation sinking and load forwarding. |
148 FillDeferredSlots(&deferred_boxes_); | 325 FillDeferredSlots(&deferred_boxes_); |
149 FillDeferredSlots(&deferred_object_refs_); | 326 FillDeferredSlots(&deferred_object_refs_); |
150 | 327 |
151 // Compute total number of artificial arguments used during deoptimization. | 328 // Compute total number of artificial arguments used during deoptimization. |
152 intptr_t deopt_arguments = 0; | 329 intptr_t deopt_arg_count = 0; |
153 for (intptr_t i = 0; i < DeferredObjectsCount(); i++) { | 330 for (intptr_t i = 0; i < DeferredObjectsCount(); i++) { |
154 deopt_arguments += GetDeferredObject(i)->ArgumentCount(); | 331 deopt_arg_count += GetDeferredObject(i)->ArgumentCount(); |
155 } | 332 } |
156 return deopt_arguments; | 333 |
| 334 // Since this is the only step where GC can occur during deoptimization, |
| 335 // use it to report the source line where deoptimization occured. |
| 336 if (FLAG_trace_deoptimization || FLAG_trace_deoptimization_verbose) { |
| 337 DartFrameIterator iterator; |
| 338 StackFrame* top_frame = iterator.NextFrame(); |
| 339 ASSERT(top_frame != NULL); |
| 340 const Code& code = Code::Handle(top_frame->LookupDartCode()); |
| 341 const Function& top_function = Function::Handle(code.function()); |
| 342 const Script& script = Script::Handle(top_function.script()); |
| 343 const intptr_t token_pos = code.GetTokenIndexOfPC(top_frame->pc()); |
| 344 intptr_t line, column; |
| 345 script.GetTokenLocation(token_pos, &line, &column); |
| 346 String& line_string = String::Handle(script.GetLine(line)); |
| 347 OS::PrintErr(" Function: %s\n", top_function.ToFullyQualifiedCString()); |
| 348 OS::PrintErr(" Line %" Pd ": '%s'\n", line, line_string.ToCString()); |
| 349 OS::PrintErr(" Deopt args: %" Pd "\n", deopt_arg_count); |
| 350 } |
| 351 |
| 352 return deopt_arg_count; |
| 353 } |
| 354 |
| 355 |
| 356 RawArray* DeoptContext::DestFrameAsArray() { |
| 357 ASSERT(dest_frame_ != NULL && dest_frame_is_allocated_); |
| 358 const Array& dest_array = |
| 359 Array::Handle(Array::New(dest_frame_size_)); |
| 360 Instance& obj = Instance::Handle(); |
| 361 for (intptr_t i = 0; i < dest_frame_size_; i++) { |
| 362 obj ^= reinterpret_cast<RawObject*>(dest_frame_[i]); |
| 363 dest_array.SetAt(i, obj); |
| 364 } |
| 365 return dest_array.raw(); |
157 } | 366 } |
158 | 367 |
159 | 368 |
160 // Deoptimization instruction moving value from optimized frame at | 369 // Deoptimization instruction moving value from optimized frame at |
161 // 'source_index' to specified slots in the unoptimized frame. | 370 // 'source_index' to specified slots in the unoptimized frame. |
162 // 'source_index' represents the slot index of the frame (0 being | 371 // 'source_index' represents the slot index of the frame (0 being |
163 // first argument) and accounts for saved return address, frame | 372 // first argument) and accounts for saved return address, frame |
164 // pointer, pool pointer and pc marker. | 373 // pointer, pool pointer and pc marker. |
165 class DeoptStackSlotInstr : public DeoptInstr { | 374 class DeoptStackSlotInstr : public DeoptInstr { |
166 public: | 375 public: |
(...skipping 1027 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1194 Smi* offset, | 1403 Smi* offset, |
1195 DeoptInfo* info, | 1404 DeoptInfo* info, |
1196 Smi* reason) { | 1405 Smi* reason) { |
1197 intptr_t i = index * kEntrySize; | 1406 intptr_t i = index * kEntrySize; |
1198 *offset ^= table.At(i); | 1407 *offset ^= table.At(i); |
1199 *info ^= table.At(i + 1); | 1408 *info ^= table.At(i + 1); |
1200 *reason ^= table.At(i + 2); | 1409 *reason ^= table.At(i + 2); |
1201 } | 1410 } |
1202 | 1411 |
1203 } // namespace dart | 1412 } // namespace dart |
OLD | NEW |