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