OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011, 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 #include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32. | |
6 #if defined(TARGET_ARCH_IA32) | |
7 | |
8 #include "vm/ic_stubs.h" | |
9 | |
10 #include "vm/assembler.h" | |
11 #include "vm/code_index_table.h" | |
12 #include "vm/disassembler.h" | |
13 #include "vm/flags.h" | |
14 #include "vm/instructions.h" | |
15 #include "vm/object.h" | |
16 #include "vm/object_store.h" | |
17 #include "vm/stub_code.h" | |
18 | |
19 namespace dart { | |
20 | |
21 DECLARE_FLAG(bool, disassemble_stubs); | |
22 DEFINE_FLAG(bool, enable_polymorphic_ic, true, | |
23 "Enable polymorphic inline caching"); | |
24 DEFINE_FLAG(bool, trace_icstub_generation, false, | |
25 "Print every generated IC stub"); | |
26 | |
27 | |
28 // TODO(srdjan): Move FindInCode and AppendICStubToTargets into the shared file. | |
29 | |
30 // Add 'classes' and 'ic_stub' to all 'targets'. Each target's code | |
31 // (class RawCode) has an array of (classes-array, ic_stub) pairs. | |
32 void ICStubs::AppendICStubToTargets( | |
33 const GrowableArray<const Function*>& targets, | |
34 const GrowableArray<const Class*>& classes, | |
35 const Code& ic_stub) { | |
36 if (FLAG_trace_icstub_generation) { | |
37 OS::Print("Appending ICstub 0x%x to targets:\n", ic_stub.EntryPoint()); | |
38 } | |
39 Code& target = Code::Handle(); | |
40 Array& class_ic_stubs_array = Array::Handle(); | |
41 Code& test_ic_stub = Code::Handle(); | |
42 for (intptr_t i = 0; i < targets.length(); i++) { | |
43 target = Code::Handle(targets[i]->code()).raw(); | |
44 if (FLAG_trace_icstub_generation) { | |
45 OS::Print(" * code 0x%x\n", target.EntryPoint()); | |
46 } | |
47 // Do not add twice: two different classes may have the same target. | |
48 test_ic_stub = ICStubs::FindInCode(target, classes); | |
49 if (test_ic_stub.IsNull()) { | |
50 // Append one class/ic-stub pair entry. | |
51 // Grow the array by two (one pair). | |
52 class_ic_stubs_array = target.class_ic_stubs(); | |
53 intptr_t new_length = class_ic_stubs_array.Length() + 2; | |
54 class_ic_stubs_array = Array::Grow(class_ic_stubs_array, new_length); | |
55 target.set_class_ic_stubs(class_ic_stubs_array); | |
56 // Create classes array out of GrowableArray classes. | |
57 Array& a = Array::Handle(Array::New(classes.length())); | |
58 for (intptr_t i = 0; i < classes.length(); i++) { | |
59 a.SetAt(i, *classes[i]); | |
60 } | |
61 class_ic_stubs_array.SetAt(new_length - 2, a); | |
62 class_ic_stubs_array.SetAt(new_length - 1, ic_stub); | |
63 if (FLAG_trace_icstub_generation) { | |
64 OS::Print(" + icstub 0x%x\n", ic_stub.EntryPoint()); | |
65 } | |
66 } else { | |
67 if (FLAG_trace_icstub_generation) { | |
68 OS::Print(" . icstub 0x%x\n", test_ic_stub.EntryPoint()); | |
69 } | |
70 } | |
71 } | |
72 } | |
73 | |
74 | |
75 // Return true if class 'test' is contained in array 'classes'. | |
76 static bool IsClassInArray(const Class& test, | |
77 const GrowableArray<const Class*>& classes) { | |
78 for (intptr_t i = 0; i < classes.length(); i++) { | |
79 if (classes[i]->raw() == test.raw()) { | |
80 return true; | |
81 } | |
82 } | |
83 return false; | |
84 } | |
85 | |
86 | |
87 // Linear search for the ic stub with given 'classes'. RawCode::class_ic_stubs() | |
88 // returns an array of (classes-array, ic-stub-code) pairs. Returns | |
89 // RawCode::null() if no stub is found. | |
90 RawCode* ICStubs::FindInCode(const Code& target, | |
91 const GrowableArray<const Class*>& classes) { | |
92 Code& result = Code::Handle(); | |
93 if (classes.is_empty()) { | |
94 return result.raw(); // RawCode::null(). | |
95 } | |
96 Array& class_ic_stubs = Array::Handle(target.class_ic_stubs()); | |
97 const intptr_t len = class_ic_stubs.Length(); | |
98 Array& array = Array::Handle(); | |
99 Class& cls = Class::Handle(); | |
100 // Iterate over all stored IC stubs/array classes pairs until match found. | |
101 for (intptr_t i = 0; i < len; i += 2) { | |
102 // i: array of classes, i + 1: ic stub code. | |
103 array ^= class_ic_stubs.At(i); | |
104 if (array.Length() == classes.length()) { | |
105 bool classes_match = true; | |
106 for (intptr_t k = 0; k < array.Length(); k++) { | |
107 cls ^= array.At(k); | |
108 if (!IsClassInArray(cls, classes)) { | |
109 classes_match = false; | |
110 break; | |
111 } | |
112 } | |
113 if (classes_match) { | |
114 // Found matching stub. | |
115 result ^= class_ic_stubs.At(i + 1); | |
116 break; | |
117 } | |
118 } | |
119 } | |
120 // If no matching stub is found, result.raw() returns null. | |
121 return result.raw(); | |
122 } | |
123 | |
124 | |
125 int ICStubs::IndexOfClass(const GrowableArray<const Class*>& classes, | |
126 const Class& cls) { | |
127 for (intptr_t i = 0; i < classes.length(); i++) { | |
128 if (classes[i]->raw() == cls.raw()) { | |
129 return i; | |
130 } | |
131 } | |
132 return -1; | |
133 } | |
134 | |
135 | |
136 // An IC Stub starts with a Smi test, optionally followed by a null test | |
137 // and zero or more class tests. The "StubCode::CallInstanceFunction" | |
138 // corresponds to an IC stub without any classes or targets. | |
139 bool ICStubs::RecognizeICStub(uword ic_entry_point, | |
140 GrowableArray<const Class*>* classes, | |
141 GrowableArray<const Function*>* targets) { | |
142 if (ic_entry_point == StubCode::CallInstanceFunctionLabel().address()) { | |
143 // Unresolved instance call, no classes collected yet. | |
144 return true; | |
145 } | |
146 if (ic_entry_point == StubCode::MegamorphicLookupEntryPoint()) { | |
147 // NoSuchMethod call, no classes collected. | |
148 return true; | |
149 } | |
150 return ParseICStub(ic_entry_point, classes, targets, 0, 0); | |
151 } | |
152 | |
153 | |
154 void ICStubs::PatchTargets(uword ic_entry_point, uword from, uword to) { | |
155 bool is_ok = ParseICStub(ic_entry_point, NULL, NULL, from, to); | |
156 ASSERT(is_ok); | |
157 } | |
158 | |
159 | |
160 // Parse IC stub, collect 'classes' and 'targets' and patches | |
161 // all 'from' targets with 'to' targets. No collection occurs | |
162 // if 'classes' and 'targets' are NULL, no patching occurs if | |
163 // 'from' or 'to' is 0. | |
164 // The IC structure is defined in IcStubs::GetIcStub. | |
165 bool ICStubs::ParseICStub(uword ic_entry_point, | |
166 GrowableArray<const Class*>* classes, | |
167 GrowableArray<const Function*>* targets, | |
168 uword from, | |
169 uword to) { | |
170 uword instruction_address = ic_entry_point; | |
171 bool patch_code = (from != 0) && (to != 0); | |
172 if (classes != NULL) { | |
173 classes->Clear(); | |
174 } | |
175 if (targets != NULL) { | |
176 targets->Clear(); | |
177 } | |
178 | |
179 // Part A: Load receiver, test if Smi, jump to IC miss or hit. | |
180 ICLoadReceiver load_receiver(instruction_address); | |
181 if (!load_receiver.IsValid()) { | |
182 return false; // Not an an IC stub. | |
183 } | |
184 instruction_address += load_receiver.pattern_length_in_bytes(); | |
185 | |
186 TestEaxIsSmi test_smi(instruction_address); | |
187 // The target of the Smi test determines if the test should cause | |
188 // IC miss if successful or jump to target (IC hit). Target of IC miss is the | |
189 // stub code, target of IC success is a code object. | |
190 CodeIndexTable* ci_table = Isolate::Current()->code_index_table(); | |
191 ASSERT(ci_table != NULL); | |
192 | |
193 Instructions& inst = Instructions::Handle( | |
194 Instructions::FromEntryPoint(ic_entry_point)); | |
195 ASSERT(!inst.IsNull()); | |
196 | |
197 if (!StubCode::InCallInstanceFunctionStubCode(test_smi.TargetAddress())) { | |
198 // Jump is an IC success. | |
199 if (patch_code && (test_smi.TargetAddress() == from)) { | |
200 test_smi.SetTargetAddress(to); | |
201 } | |
202 const Class& smi_class = | |
203 Class::ZoneHandle(Isolate::Current()->object_store()->smi_class()); | |
204 const Code& smi_code = | |
205 Code::Handle(ci_table->LookupCode(test_smi.TargetAddress())); | |
206 ASSERT(!smi_class.IsNullClass()); | |
207 ASSERT(!smi_code.IsNull()); | |
208 if (classes != NULL) { | |
209 classes->Add(&smi_class); | |
210 } | |
211 if (targets != NULL) { | |
212 targets->Add(&Function::ZoneHandle(smi_code.function())); | |
213 } | |
214 } | |
215 instruction_address += test_smi.pattern_length_in_bytes(); | |
216 | |
217 // TODO(srdjan): Add checks that the IC stub ends with | |
218 // a null check and a jmp. | |
219 // Part B: Load receiver's class, compare with all known classes. | |
220 LoadObjectClass load_object_class(instruction_address); | |
221 if (!load_object_class.IsValid()) { | |
222 return false; | |
223 } | |
224 instruction_address += load_object_class.pattern_length_in_bytes(); | |
225 while (true) { | |
226 ICCheckReceiverClass check_class(instruction_address); | |
227 if (!check_class.IsValid()) { | |
228 // Done parsing. | |
229 return true; | |
230 } | |
231 if (patch_code && (check_class.TargetAddress() == from)) { | |
232 check_class.SetTargetAddress(to); | |
233 } | |
234 const Class& cls = Class::ZoneHandle(check_class.TestClass()); | |
235 const Code& code = Code::ZoneHandle( | |
236 ci_table->LookupCode(check_class.TargetAddress())); | |
237 ASSERT(!cls.IsNullClass()); | |
238 ASSERT(!code.IsNull()); | |
239 if (classes != NULL) { | |
240 classes->Add(&cls); | |
241 } | |
242 if (targets != NULL) { | |
243 targets->Add(&Function::ZoneHandle(code.function())); | |
244 } | |
245 instruction_address += check_class.pattern_length_in_bytes(); | |
246 } | |
247 } | |
248 | |
249 | |
250 // Generate inline cache stub for given targets and classes. | |
251 // EDX: arguments descriptor array (preserved). | |
252 // ECX: function name (unused, preserved). | |
253 // TOS: return address. | |
254 // Jump to target if the receiver's class matches the 'receiver_class'. | |
255 // Otherwise jump to megamorphic lookup. TODO(srdjan): Patch call site to go to | |
256 // megamorphic instead of going via the IC stub. | |
257 // IC stub structure: | |
258 // A: Get receiver, test if Smi, jump to IC miss or hit. | |
259 // B: Get receiver's class, compare with all known classes. | |
260 RawCode* ICStubs::GetICStub(const GrowableArray<const Class*>& classes, | |
261 const GrowableArray<const Function*>& targets) { | |
262 // Check if a matching IC stub already exists. | |
263 Code& ic_stub_code = Code::Handle(); | |
264 for (intptr_t i = 0; i < targets.length(); i++) { | |
265 ic_stub_code = | |
266 ICStubs::FindInCode(Code::Handle(targets[i]->code()), classes); | |
267 if (!ic_stub_code.IsNull()) { | |
268 return ic_stub_code.raw(); // Reusing the previously created IC stub. | |
269 } | |
270 } | |
271 | |
272 // Call IC miss handling only if polymorphic inline caching is on, otherwise | |
273 // continue in megamorphic lookup. | |
274 const ExternalLabel* ic_miss_label = | |
275 FLAG_enable_polymorphic_ic | |
276 ? &StubCode::CallInstanceFunctionLabel() | |
277 : &StubCode::MegamorphicLookupLabel(); | |
278 | |
279 #define __ assembler. | |
280 Assembler assembler; | |
281 // Part A: Get receiver, test if Smi. | |
282 // Total number of args is the first Smi in args descriptor array (EDX). | |
283 __ movl(EAX, FieldAddress(EDX, Array::data_offset())); | |
284 __ movl(EAX, Address(ESP, EAX, TIMES_2, 0)); // Get receiver. EAX is a Smi. | |
285 __ testl(EAX, Immediate(kSmiTagMask)); | |
286 | |
287 const Class& smi_class = Class::Handle( | |
288 Isolate::Current()->object_store()->smi_class()); | |
289 const int smi_class_index = IndexOfClass(classes, smi_class); | |
290 if (smi_class_index >= 0) { | |
291 // Smi is not IC miss. | |
292 const Code& target = Code::Handle(targets[smi_class_index]->code()); | |
293 ExternalLabel target_label("ICtoTargetSmi", target.EntryPoint()); | |
294 // Always check for Smi first and either go to target or call ic-miss. | |
295 __ j(ZERO, &target_label); | |
296 } else { | |
297 // Smi is IC miss. | |
298 __ j(ZERO, ic_miss_label); | |
299 } | |
300 | |
301 // Part B: Load receiver's class, compare with all known classes. | |
302 __ movl(EBX, FieldAddress(EAX, Object::class_offset())); | |
303 for (int cli = 0; cli < classes.length(); cli++) { | |
304 const Class* test_class = classes[cli]; | |
305 ASSERT(!test_class->IsNullClass()); | |
306 if (test_class->raw() != smi_class.raw()) { | |
307 const Code& target = Code::Handle(targets[cli]->code()); | |
308 ExternalLabel target_label("ICtoTargetClass", target.EntryPoint()); | |
309 __ CompareObject(EBX, *test_class); | |
310 __ j(EQUAL, &target_label); | |
311 } | |
312 } | |
313 // IC miss. If null jump to megamorphic (don't trash IC), otherwise to IC | |
314 // miss in order to update the IC. | |
315 const Immediate raw_null = | |
316 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
317 __ cmpl(EAX, raw_null); | |
318 __ j(EQUAL, &StubCode::MegamorphicLookupLabel()); | |
319 | |
320 __ jmp(ic_miss_label); | |
321 ic_stub_code = Code::FinalizeCode("inline cache stub", &assembler); | |
322 ICStubs::AppendICStubToTargets(targets, classes, ic_stub_code); | |
323 | |
324 if (FLAG_trace_icstub_generation) { | |
325 OS::Print("IC Stub code generated at 0x%x: targets: %d, classes: %d\n", | |
326 ic_stub_code.EntryPoint(), targets.length(), classes.length()); | |
327 for (intptr_t i = 0; i < targets.length(); i++) { | |
328 OS::Print(" target: 0x%x class: %s\n", | |
329 Code::Handle(targets[i]->code()).EntryPoint(), | |
330 classes[i]->ToCString()); | |
331 } | |
332 } | |
333 | |
334 if (FLAG_disassemble_stubs) { | |
335 for (intptr_t i = 0; i < classes.length(); i++) { | |
336 ASSERT(classes[i]->raw() != Object::null_class()); | |
337 const String& class_name = String::Handle(classes[i]->Name()); | |
338 CodeIndexTable* code_index_table = Isolate::Current()->code_index_table(); | |
339 const Code& target = Code::Handle(targets[i]->code()); | |
340 const Function& function = | |
341 Function::Handle( | |
342 code_index_table->LookupFunction(target.EntryPoint())); | |
343 OS::Print("%d: Code for inline cache for class '%s' function '%s': {\n", | |
344 i, class_name.ToCString(), function.ToFullyQualifiedCString()); | |
345 } | |
346 Disassembler::Disassemble(ic_stub_code.EntryPoint(), | |
347 ic_stub_code.EntryPoint() + assembler.CodeSize()); | |
348 OS::Print("}\n"); | |
349 } | |
350 | |
351 return ic_stub_code.raw(); | |
352 #undef __ | |
353 } | |
354 | |
355 | |
356 } // namespace dart | |
357 | |
358 #endif // defined TARGET_ARCH_IA32 | |
OLD | NEW |