Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(453)

Side by Side Diff: vm/ic_stubs_ia32.cc

Issue 8379013: Implement new inline cache. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/runtime/
Patch Set: '' Created 9 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « vm/ic_stubs_arm.cc ('k') | vm/ic_stubs_ia32_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « vm/ic_stubs_arm.cc ('k') | vm/ic_stubs_ia32_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698