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

Side by Side Diff: runtime/vm/flow_graph_compiler_ia32.cc

Issue 10636038: Minimize differences between ia32 and x64 sources to facilitate maintenance. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 5 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 | « runtime/vm/flow_graph_compiler_ia32.h ('k') | runtime/vm/flow_graph_compiler_x64.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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/globals.h" // Needed here to get TARGET_ARCH_IA32. 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32.
6 #if defined(TARGET_ARCH_IA32) 6 #if defined(TARGET_ARCH_IA32)
7 7
8 #include "vm/flow_graph_compiler.h" 8 #include "vm/flow_graph_compiler.h"
9 9
10 #include "lib/error.h" 10 #include "lib/error.h"
11 #include "vm/ast_printer.h" 11 #include "vm/ast_printer.h"
12 #include "vm/compiler_stats.h"
13 #include "vm/il_printer.h" 12 #include "vm/il_printer.h"
14 #include "vm/locations.h" 13 #include "vm/locations.h"
15 #include "vm/object_store.h" 14 #include "vm/object_store.h"
15 #include "vm/parser.h"
16 #include "vm/stub_code.h" 16 #include "vm/stub_code.h"
17 17
18 namespace dart { 18 namespace dart {
19 19
20 DECLARE_FLAG(bool, compiler_stats); 20 DEFINE_FLAG(bool, print_scopes, false, "Print scopes of local variables.");
21 DEFINE_FLAG(bool, trace_functions, false, "Trace entry of each function.");
srdjan 2012/06/25 22:18:17 How about moving them into flow_graph_compiler.cc
regis 2012/06/25 22:22:59 Done.
21 DECLARE_FLAG(bool, enable_type_checks); 22 DECLARE_FLAG(bool, enable_type_checks);
22 DECLARE_FLAG(bool, print_ast); 23 DECLARE_FLAG(bool, print_ast);
23 DECLARE_FLAG(bool, print_scopes);
24 DECLARE_FLAG(bool, trace_functions);
25 24
26 25
27 void DeoptimizationStub::GenerateCode(FlowGraphCompiler* compiler) { 26 void DeoptimizationStub::GenerateCode(FlowGraphCompiler* compiler) {
28 Assembler* assem = compiler->assembler(); 27 Assembler* assem = compiler->assembler();
29 #define __ assem-> 28 #define __ assem->
30 __ Comment("Deopt stub for id %d", deopt_id_); 29 __ Comment("Deopt stub for id %d", deopt_id_);
31 __ Bind(entry_label()); 30 __ Bind(entry_label());
32 for (intptr_t i = 0; i < registers_.length(); i++) { 31 for (intptr_t i = 0; i < registers_.length(); i++) {
33 if (registers_[i] != kNoRegister) { 32 if (registers_[i] != kNoRegister) {
34 __ pushl(registers_[i]); 33 __ pushl(registers_[i]);
35 } 34 }
36 } 35 }
37 __ movl(EAX, Immediate(Smi::RawValue(reason_))); 36 __ movl(EAX, Immediate(Smi::RawValue(reason_)));
38 __ call(&StubCode::DeoptimizeLabel()); 37 __ call(&StubCode::DeoptimizeLabel());
39 compiler->AddCurrentDescriptor(PcDescriptors::kOther, 38 compiler->AddCurrentDescriptor(PcDescriptors::kOther,
40 deopt_id_, 39 deopt_id_,
41 deopt_token_pos_, 40 deopt_token_pos_,
42 try_index_); 41 try_index_);
43 #undef __ 42 #undef __
44 } 43 }
45 44
46 45
47
48 #define __ assembler()-> 46 #define __ assembler()->
49 47
50 void FlowGraphCompiler::GenerateInlinedGetter(intptr_t offset) {
51 // TOS: return address.
52 // +1 : receiver.
53 // Sequence node has one return node, its input is load field node.
54 __ movl(EAX, Address(ESP, 1 * kWordSize));
55 __ movl(EAX, FieldAddress(EAX, offset));
56 __ ret();
57 }
58
59
60 void FlowGraphCompiler::GenerateInlinedSetter(intptr_t offset) {
61 // TOS: return address.
62 // +1 : value
63 // +2 : receiver.
64 __ movl(EAX, Address(ESP, 2 * kWordSize)); // Receiver.
65 __ movl(EBX, Address(ESP, 1 * kWordSize)); // Value.
66 __ StoreIntoObject(EAX, FieldAddress(EAX, offset), EBX);
67 const Immediate raw_null =
68 Immediate(reinterpret_cast<intptr_t>(Object::null()));
69 __ movl(EAX, raw_null);
70 __ ret();
71 }
72
73
74 void FlowGraphCompiler::GenerateInlinedMathSqrt(Label* done) {
75 Label smi_to_double, double_op, call_method;
76 __ movl(EAX, Address(ESP, 0));
77 __ testl(EAX, Immediate(kSmiTagMask));
78 __ j(ZERO, &smi_to_double);
79 __ CompareClassId(EAX, kDouble, EBX);
80 __ j(NOT_EQUAL, &call_method);
81 __ movsd(XMM1, FieldAddress(EAX, Double::value_offset()));
82 __ Bind(&double_op);
83 __ sqrtsd(XMM0, XMM1);
84 AssemblerMacros::TryAllocate(assembler_,
85 double_class_,
86 &call_method,
87 EAX); // Result register.
88 __ movsd(FieldAddress(EAX, Double::value_offset()), XMM0);
89 __ Drop(1);
90 __ jmp(done);
91 __ Bind(&smi_to_double);
92 __ SmiUntag(EAX);
93 __ cvtsi2sd(XMM1, EAX);
94 __ jmp(&double_op);
95 __ Bind(&call_method);
96 }
97
98
99 void FlowGraphCompiler::GenerateCall(intptr_t token_pos,
100 intptr_t try_index,
101 const ExternalLabel* label,
102 PcDescriptors::Kind kind) {
103 ASSERT(frame_register_allocator()->IsSpilled());
104 __ call(label);
105 AddCurrentDescriptor(kind, AstNode::kNoId, token_pos, try_index);
106 }
107
108
109 void FlowGraphCompiler::GenerateCallRuntime(intptr_t cid,
110 intptr_t token_pos,
111 intptr_t try_index,
112 const RuntimeEntry& entry) {
113 ASSERT(frame_register_allocator()->IsSpilled());
114 __ CallRuntime(entry);
115 AddCurrentDescriptor(PcDescriptors::kOther, cid, token_pos, try_index);
116 }
117
118
119 void FlowGraphCompiler::CopyParameters() {
120 const Function& function = parsed_function().function();
121 const bool is_native_instance_closure =
122 function.is_native() && function.IsImplicitInstanceClosureFunction();
123 LocalScope* scope = parsed_function().node_sequence()->scope();
124 const int num_fixed_params = function.num_fixed_parameters();
125 const int num_opt_params = function.num_optional_parameters();
126 int implicit_this_param_pos = is_native_instance_closure ? -1 : 0;
127 ASSERT(parsed_function().first_parameter_index() ==
128 ParsedFunction::kFirstLocalSlotIndex + implicit_this_param_pos);
129 // Copy positional arguments.
130 // Check that no fewer than num_fixed_params positional arguments are passed
131 // in and that no more than num_params arguments are passed in.
132 // Passed argument i at fp[1 + argc - i]
133 // copied to fp[ParsedFunction::kFirstLocalSlotIndex - i].
134 const int num_params = num_fixed_params + num_opt_params;
135
136 // Total number of args is the first Smi in args descriptor array (EDX).
137 __ movl(EBX, FieldAddress(EDX, Array::data_offset()));
138 // Check that num_args <= num_params.
139 Label wrong_num_arguments;
140 __ cmpl(EBX, Immediate(Smi::RawValue(num_params)));
141 __ j(GREATER, &wrong_num_arguments);
142 // Number of positional args is the second Smi in descriptor array (EDX).
143 __ movl(ECX, FieldAddress(EDX, Array::data_offset() + (1 * kWordSize)));
144 // Check that num_pos_args >= num_fixed_params.
145 __ cmpl(ECX, Immediate(Smi::RawValue(num_fixed_params)));
146 __ j(LESS, &wrong_num_arguments);
147 // Since EBX and ECX are Smi, use TIMES_2 instead of TIMES_4.
148 // Let EBX point to the last passed positional argument, i.e. to
149 // fp[1 + num_args - (num_pos_args - 1)].
150 __ subl(EBX, ECX);
151 __ leal(EBX, Address(EBP, EBX, TIMES_2, 2 * kWordSize));
152
153 // Let EDI point to the last copied positional argument, i.e. to
154 // fp[ParsedFunction::kFirstLocalSlotIndex - (num_pos_args - 1)].
155 const int index =
156 ParsedFunction::kFirstLocalSlotIndex + 1 + implicit_this_param_pos;
157 // First copy captured receiver if function is an implicit native closure.
158 if (is_native_instance_closure) {
159 __ movl(EAX, FieldAddress(CTX, Context::variable_offset(0)));
160 __ movl(Address(EBP, (index * kWordSize)), EAX);
161 }
162 __ leal(EDI, Address(EBP, (index * kWordSize)));
163 __ subl(EDI, ECX); // ECX is a Smi, subtract twice for TIMES_4 scaling.
164 __ subl(EDI, ECX);
165 __ SmiUntag(ECX);
166 Label loop, loop_condition;
167 __ jmp(&loop_condition, Assembler::kNearJump);
168 // We do not use the final allocation index of the variable here, i.e.
169 // scope->VariableAt(i)->index(), because captured variables still need
170 // to be copied to the context that is not yet allocated.
171 const Address argument_addr(EBX, ECX, TIMES_4, 0);
172 const Address copy_addr(EDI, ECX, TIMES_4, 0);
173 __ Bind(&loop);
174 __ movl(EAX, argument_addr);
175 __ movl(copy_addr, EAX);
176 __ Bind(&loop_condition);
177 __ decl(ECX);
178 __ j(POSITIVE, &loop, Assembler::kNearJump);
179
180 // Copy or initialize optional named arguments.
181 Label all_arguments_processed;
182 const Immediate raw_null =
183 Immediate(reinterpret_cast<intptr_t>(Object::null()));
184 if (num_opt_params > 0) {
185 // Start by alphabetically sorting the names of the optional parameters.
186 LocalVariable** opt_param = new LocalVariable*[num_opt_params];
187 int* opt_param_position = new int[num_opt_params];
188 for (int pos = num_fixed_params; pos < num_params; pos++) {
189 LocalVariable* parameter = scope->VariableAt(pos);
190 const String& opt_param_name = parameter->name();
191 int i = pos - num_fixed_params;
192 while (--i >= 0) {
193 LocalVariable* param_i = opt_param[i];
194 const intptr_t result = opt_param_name.CompareTo(param_i->name());
195 ASSERT(result != 0);
196 if (result > 0) break;
197 opt_param[i + 1] = opt_param[i];
198 opt_param_position[i + 1] = opt_param_position[i];
199 }
200 opt_param[i + 1] = parameter;
201 opt_param_position[i + 1] = pos;
202 }
203 // Generate code handling each optional parameter in alphabetical order.
204 // Total number of args is the first Smi in args descriptor array (EDX).
205 __ movl(EBX, FieldAddress(EDX, Array::data_offset()));
206 // Number of positional args is the second Smi in descriptor array (EDX).
207 __ movl(ECX, FieldAddress(EDX, Array::data_offset() + (1 * kWordSize)));
208 __ SmiUntag(ECX);
209 // Let EBX point to the first passed argument, i.e. to fp[1 + argc - 0].
210 __ leal(EBX, Address(EBP, EBX, TIMES_2, kWordSize)); // EBX is Smi.
211 // Let EDI point to the name/pos pair of the first named argument.
212 __ leal(EDI, FieldAddress(EDX, Array::data_offset() + (2 * kWordSize)));
213 for (int i = 0; i < num_opt_params; i++) {
214 // Handle this optional parameter only if k or fewer positional arguments
215 // have been passed, where k is the position of this optional parameter in
216 // the formal parameter list.
217 Label load_default_value, assign_optional_parameter, next_parameter;
218 const int param_pos = opt_param_position[i];
219 __ cmpl(ECX, Immediate(param_pos));
220 __ j(GREATER, &next_parameter, Assembler::kNearJump);
221 // Check if this named parameter was passed in.
222 __ movl(EAX, Address(EDI, 0)); // Load EAX with the name of the argument.
223 __ CompareObject(EAX, opt_param[i]->name());
224 __ j(NOT_EQUAL, &load_default_value, Assembler::kNearJump);
225 // Load EAX with passed-in argument at provided arg_pos, i.e. at
226 // fp[1 + argc - arg_pos].
227 __ movl(EAX, Address(EDI, kWordSize)); // EAX is arg_pos as Smi.
228 __ addl(EDI, Immediate(2 * kWordSize)); // Point to next name/pos pair.
229 __ negl(EAX);
230 Address argument_addr(EBX, EAX, TIMES_2, 0); // EAX is a negative Smi.
231 __ movl(EAX, argument_addr);
232 __ jmp(&assign_optional_parameter, Assembler::kNearJump);
233 __ Bind(&load_default_value);
234 // Load EAX with default argument at pos.
235 const Object& value = Object::ZoneHandle(
236 parsed_function().default_parameter_values().At(
237 param_pos - num_fixed_params));
238 __ LoadObject(EAX, value);
239 __ Bind(&assign_optional_parameter);
240 // Assign EAX to fp[ParsedFunction::kFirstLocalSlotIndex - param_pos].
241 // We do not use the final allocation index of the variable here, i.e.
242 // scope->VariableAt(i)->index(), because captured variables still need
243 // to be copied to the context that is not yet allocated.
244 intptr_t computed_param_pos = (ParsedFunction::kFirstLocalSlotIndex -
245 param_pos + implicit_this_param_pos);
246 const Address param_addr(EBP, (computed_param_pos * kWordSize));
247 __ movl(param_addr, EAX);
248 __ Bind(&next_parameter);
249 }
250 delete[] opt_param;
251 delete[] opt_param_position;
252 // Check that EDI now points to the null terminator in the array descriptor.
253 __ cmpl(Address(EDI, 0), raw_null);
254 __ j(EQUAL, &all_arguments_processed, Assembler::kNearJump);
255 } else {
256 ASSERT(is_native_instance_closure);
257 __ jmp(&all_arguments_processed, Assembler::kNearJump);
258 }
259
260 __ Bind(&wrong_num_arguments);
261 if (StackSize() != 0) {
262 // We need to unwind the space we reserved for locals and copied parameters.
263 // The NoSuchMethodFunction stub does not expect to see that area on the
264 // stack.
265 __ addl(ESP, Immediate(StackSize() * kWordSize));
266 }
267 if (function.IsClosureFunction()) {
268 GenerateCallRuntime(AstNode::kNoId,
269 0,
270 CatchClauseNode::kInvalidTryIndex,
271 kClosureArgumentMismatchRuntimeEntry);
272 } else {
273 // Invoke noSuchMethod function.
274 const int kNumArgsChecked = 1;
275 ICData& ic_data = ICData::ZoneHandle();
276 ic_data = ICData::New(function,
277 String::Handle(function.name()),
278 AstNode::kNoId,
279 kNumArgsChecked);
280 __ LoadObject(ECX, ic_data);
281 // EBP - 4 : PC marker, allows easy identification of RawInstruction obj.
282 // EBP : points to previous frame pointer.
283 // EBP + 4 : points to return address.
284 // EBP + 8 : address of last argument (arg n-1).
285 // ESP + 8 + 4*(n-1) : address of first argument (arg 0).
286 // ECX : ic-data.
287 // EDX : arguments descriptor array.
288 __ call(&StubCode::CallNoSuchMethodFunctionLabel());
289 }
290
291 if (FLAG_trace_functions) {
292 __ pushl(EAX); // Preserve result.
293 __ PushObject(Function::ZoneHandle(function.raw()));
294 GenerateCallRuntime(AstNode::kNoId,
295 0,
296 CatchClauseNode::kInvalidTryIndex,
297 kTraceFunctionExitRuntimeEntry);
298 __ popl(EAX); // Remove argument.
299 __ popl(EAX); // Restore result.
300 }
301 __ LeaveFrame();
302 __ ret();
303
304 __ Bind(&all_arguments_processed);
305 // Nullify originally passed arguments only after they have been copied and
306 // checked, otherwise noSuchMethod would not see their original values.
307 // This step can be skipped in case we decide that formal parameters are
308 // implicitly final, since garbage collecting the unmodified value is not
309 // an issue anymore.
310
311 // EDX : arguments descriptor array.
312 // Total number of args is the first Smi in args descriptor array (EDX).
313 __ movl(ECX, FieldAddress(EDX, Array::data_offset()));
314 __ SmiUntag(ECX);
315 Label null_args_loop, null_args_loop_condition;
316 __ jmp(&null_args_loop_condition, Assembler::kNearJump);
317 const Address original_argument_addr(EBP, ECX, TIMES_4, 2 * kWordSize);
318 __ Bind(&null_args_loop);
319 __ movl(original_argument_addr, raw_null);
320 __ Bind(&null_args_loop_condition);
321 __ decl(ECX);
322 __ j(POSITIVE, &null_args_loop, Assembler::kNearJump);
323 }
324
325
326 void FlowGraphCompiler::CompileGraph() {
327 InitCompiler();
328 if (TryIntrinsify()) {
329 __ int3();
330 __ jmp(&StubCode::FixCallersTargetLabel());
331 return;
332 }
333 // Specialized version of entry code from CodeGenerator::GenerateEntryCode.
334 const Function& function = parsed_function().function();
335
336 const int parameter_count = function.num_fixed_parameters();
337 const int num_copied_params = parsed_function().copied_parameter_count();
338 const int local_count = parsed_function().stack_local_count();
339 AssemblerMacros::EnterDartFrame(assembler(), (StackSize() * kWordSize));
340 // We check the number of passed arguments when we have to copy them due to
341 // the presence of optional named parameters.
342 // No such checking code is generated if only fixed parameters are declared,
343 // unless we are debug mode or unless we are compiling a closure.
344 if (num_copied_params == 0) {
345 #ifdef DEBUG
346 const bool check_arguments = true;
347 #else
348 const bool check_arguments = function.IsClosureFunction();
349 #endif
350 if (check_arguments) {
351 // Check that num_fixed <= argc <= num_params.
352 Label argc_in_range;
353 // Total number of args is the first Smi in args descriptor array (EDX).
354 __ movl(EAX, FieldAddress(EDX, Array::data_offset()));
355 __ cmpl(EAX, Immediate(Smi::RawValue(parameter_count)));
356 __ j(EQUAL, &argc_in_range, Assembler::kNearJump);
357 if (function.IsClosureFunction()) {
358 GenerateCallRuntime(AstNode::kNoId,
359 function.token_pos(),
360 CatchClauseNode::kInvalidTryIndex,
361 kClosureArgumentMismatchRuntimeEntry);
362 } else {
363 __ Stop("Wrong number of arguments");
364 }
365 __ Bind(&argc_in_range);
366 }
367 } else {
368 CopyParameters();
369 }
370 // Initialize (non-argument) stack allocated locals to null.
371 if (local_count > 0) {
372 const Immediate raw_null =
373 Immediate(reinterpret_cast<intptr_t>(Object::null()));
374 __ movl(EAX, raw_null);
375 const int base = parsed_function().first_stack_local_index();
376 for (int i = 0; i < local_count; ++i) {
377 // Subtract index i (locals lie at lower addresses than EBP).
378 __ movl(Address(EBP, (base - i) * kWordSize), EAX);
379 }
380 }
381
382 // Generate stack overflow check.
383 __ cmpl(ESP,
384 Address::Absolute(Isolate::Current()->stack_limit_address()));
385 Label no_stack_overflow;
386 __ j(ABOVE, &no_stack_overflow, Assembler::kNearJump);
387 GenerateCallRuntime(AstNode::kNoId,
388 function.token_pos(),
389 CatchClauseNode::kInvalidTryIndex,
390 kStackOverflowRuntimeEntry);
391 __ Bind(&no_stack_overflow);
392
393 if (FLAG_print_scopes) {
394 // Print the function scope (again) after generating the prologue in order
395 // to see annotations such as allocation indices of locals.
396 if (FLAG_print_ast) {
397 // Second printing.
398 OS::Print("Annotated ");
399 }
400 AstPrinter::PrintFunctionScope(parsed_function());
401 }
402
403 VisitBlocks();
404
405 __ int3();
406 GenerateDeferredCode();
407 // Emit function patching code. This will be swapped with the first 5 bytes
408 // at entry point.
409 pc_descriptors_list()->AddDescriptor(PcDescriptors::kPatchCode,
410 assembler()->CodeSize(),
411 AstNode::kNoId,
412 0,
413 -1);
414 __ jmp(&StubCode::FixCallersTargetLabel());
415 }
416
417
418 intptr_t FlowGraphCompiler::EmitInstanceCall(ExternalLabel* target_label,
419 const ICData& ic_data,
420 const Array& arguments_descriptor,
421 intptr_t argument_count) {
422 __ LoadObject(ECX, ic_data);
423 __ LoadObject(EDX, arguments_descriptor);
424
425 __ call(target_label);
426 const intptr_t descr_offset = assembler()->CodeSize();
427 __ Drop(argument_count);
428 return descr_offset;
429 }
430
431
432 intptr_t FlowGraphCompiler::EmitStaticCall(const Function& function,
433 const Array& arguments_descriptor,
434 intptr_t argument_count) {
435 __ LoadObject(ECX, function);
436 __ LoadObject(EDX, arguments_descriptor);
437 __ call(&StubCode::CallStaticFunctionLabel());
438 const intptr_t descr_offset = assembler()->CodeSize();
439 __ Drop(argument_count);
440 return descr_offset;
441 }
442
443 48
444 // Fall through if bool_register contains null. 49 // Fall through if bool_register contains null.
445 void FlowGraphCompiler::GenerateBoolToJump(Register bool_register, 50 void FlowGraphCompiler::GenerateBoolToJump(Register bool_register,
446 Label* is_true, 51 Label* is_true,
447 Label* is_false) { 52 Label* is_false) {
448 const Immediate raw_null = 53 const Immediate raw_null =
449 Immediate(reinterpret_cast<intptr_t>(Object::null())); 54 Immediate(reinterpret_cast<intptr_t>(Object::null()));
450 Label fall_through; 55 Label fall_through;
451 __ cmpl(bool_register, raw_null); 56 __ cmpl(bool_register, raw_null);
452 __ j(EQUAL, &fall_through, Assembler::kNearJump); 57 __ j(EQUAL, &fall_through, Assembler::kNearJump);
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
493 __ popl(instance_reg); // Restore receiver. 98 __ popl(instance_reg); // Restore receiver.
494 __ popl(temp_reg); // Discard. 99 __ popl(temp_reg); // Discard.
495 GenerateBoolToJump(ECX, is_instance_lbl, is_not_instance_lbl); 100 GenerateBoolToJump(ECX, is_instance_lbl, is_not_instance_lbl);
496 return type_test_cache.raw(); 101 return type_test_cache.raw();
497 } 102 }
498 103
499 104
500 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if 105 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if
501 // type test is conclusive, otherwise fallthrough if a type test could not 106 // type test is conclusive, otherwise fallthrough if a type test could not
502 // be completed. 107 // be completed.
503 // EAX: instance (must survive), clobbers ECX, EDI 108 // EAX: instance (must survive).
109 // Clobbers ECX, EDI.
504 RawSubtypeTestCache* 110 RawSubtypeTestCache*
505 FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest( 111 FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest(
506 intptr_t cid, 112 intptr_t cid,
507 intptr_t token_pos, 113 intptr_t token_pos,
508 const AbstractType& type, 114 const AbstractType& type,
509 Label* is_instance_lbl, 115 Label* is_instance_lbl,
510 Label* is_not_instance_lbl) { 116 Label* is_not_instance_lbl) {
511 ASSERT(type.IsInstantiated()); 117 ASSERT(type.IsInstantiated());
512 const Class& type_class = Class::ZoneHandle(type.type_class()); 118 const Class& type_class = Class::ZoneHandle(type.type_class());
513 ASSERT(type_class.HasTypeArguments()); 119 ASSERT(type_class.HasTypeArguments());
514 const Register kInstanceReg = EAX; 120 const Register kInstanceReg = EAX;
515 // A Smi object cannot be the instance of a parameterized class. 121 // A Smi object cannot be the instance of a parameterized class.
516 __ testl(kInstanceReg, Immediate(kSmiTagMask)); 122 __ testl(kInstanceReg, Immediate(kSmiTagMask));
517 __ j(ZERO, is_not_instance_lbl); 123 __ j(ZERO, is_not_instance_lbl);
518 const AbstractTypeArguments& type_arguments = 124 const AbstractTypeArguments& type_arguments =
519 AbstractTypeArguments::ZoneHandle(type.arguments()); 125 AbstractTypeArguments::ZoneHandle(type.arguments());
520 const bool is_raw_type = type_arguments.IsNull() || 126 const bool is_raw_type = type_arguments.IsNull() ||
521 type_arguments.IsRaw(type_arguments.Length()); 127 type_arguments.IsRaw(type_arguments.Length());
522 if (is_raw_type) { 128 if (is_raw_type) {
523 const Register kClassIdReg = ECX; 129 const Register kClassIdReg = ECX;
524 // Dynamic type argument, check only classes. 130 // Dynamic type argument, check only classes.
131 // List is a very common case.
525 __ LoadClassId(kClassIdReg, kInstanceReg); 132 __ LoadClassId(kClassIdReg, kInstanceReg);
526 if (!type_class.is_interface()) { 133 if (!type_class.is_interface()) {
527 __ cmpl(kClassIdReg, Immediate(type_class.id())); 134 __ cmpl(kClassIdReg, Immediate(type_class.id()));
528 __ j(EQUAL, is_instance_lbl); 135 __ j(EQUAL, is_instance_lbl);
529 } 136 }
530 if (type.IsListInterface()) { 137 if (type.IsListInterface()) {
531 GenerateListTypeCheck(kClassIdReg, is_instance_lbl); 138 GenerateListTypeCheck(kClassIdReg, is_instance_lbl);
532 } 139 }
533 return GenerateSubtype1TestCacheLookup( 140 return GenerateSubtype1TestCacheLookup(
534 cid, token_pos, type_class, is_instance_lbl, is_not_instance_lbl); 141 cid, token_pos, type_class, is_instance_lbl, is_not_instance_lbl);
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
569 for (intptr_t i = 0; i < class_ids.length(); i++) { 176 for (intptr_t i = 0; i < class_ids.length(); i++) {
570 __ cmpl(class_id_reg, Immediate(class_ids[i])); 177 __ cmpl(class_id_reg, Immediate(class_ids[i]));
571 __ j(EQUAL, is_equal_lbl); 178 __ j(EQUAL, is_equal_lbl);
572 } 179 }
573 __ jmp(is_not_equal_lbl); 180 __ jmp(is_not_equal_lbl);
574 } 181 }
575 182
576 183
577 // Testing against an instantiated type with no arguments, without 184 // Testing against an instantiated type with no arguments, without
578 // SubtypeTestCache. 185 // SubtypeTestCache.
579 // EAX: instance to test against (preserved). Clobbers ECX, EDI. 186 // EAX: instance to test against (preserved).
187 // Clobbers ECX, EDI.
580 void FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest( 188 void FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest(
581 intptr_t cid, 189 intptr_t cid,
582 intptr_t token_pos, 190 intptr_t token_pos,
583 const AbstractType& type, 191 const AbstractType& type,
584 Label* is_instance_lbl, 192 Label* is_instance_lbl,
585 Label* is_not_instance_lbl) { 193 Label* is_not_instance_lbl) {
586 ASSERT(type.IsInstantiated()); 194 ASSERT(type.IsInstantiated());
587 const Class& type_class = Class::Handle(type.type_class()); 195 const Class& type_class = Class::Handle(type.type_class());
588 ASSERT(!type_class.HasTypeArguments()); 196 ASSERT(!type_class.HasTypeArguments());
589 197
(...skipping 15 matching lines...) Expand all
605 } 213 }
606 // Compare if the classes are equal. 214 // Compare if the classes are equal.
607 __ Bind(&compare_classes); 215 __ Bind(&compare_classes);
608 const Register kClassIdReg = ECX; 216 const Register kClassIdReg = ECX;
609 __ LoadClassId(kClassIdReg, kInstanceReg); 217 __ LoadClassId(kClassIdReg, kInstanceReg);
610 // If type is an interface, we can skip the class equality check. 218 // If type is an interface, we can skip the class equality check.
611 if (!type_class.is_interface()) { 219 if (!type_class.is_interface()) {
612 __ cmpl(kClassIdReg, Immediate(type_class.id())); 220 __ cmpl(kClassIdReg, Immediate(type_class.id()));
613 __ j(EQUAL, is_instance_lbl); 221 __ j(EQUAL, is_instance_lbl);
614 } 222 }
223 // Bool interface can be implemented only by core class Bool.
615 // (see ClassFinalizer::ResolveInterfaces for list of restricted interfaces). 224 // (see ClassFinalizer::ResolveInterfaces for list of restricted interfaces).
616 // Bool interface can be implemented only by core class Bool.
617 if (type.IsBoolInterface()) { 225 if (type.IsBoolInterface()) {
618 __ cmpl(kClassIdReg, Immediate(kBool)); 226 __ cmpl(kClassIdReg, Immediate(kBool));
619 __ j(EQUAL, is_instance_lbl); 227 __ j(EQUAL, is_instance_lbl);
620 __ jmp(is_not_instance_lbl); 228 __ jmp(is_not_instance_lbl);
621 return; 229 return;
622 } 230 }
623 if (type.IsFunctionInterface()) { 231 if (type.IsFunctionInterface()) {
624 // Check if instance is a closure. 232 // Check if instance is a closure.
625 const Immediate raw_null = 233 const Immediate raw_null =
626 Immediate(reinterpret_cast<intptr_t>(Object::null())); 234 Immediate(reinterpret_cast<intptr_t>(Object::null()));
627 __ LoadClassById(EDI, kClassIdReg); 235 __ LoadClassById(EDI, kClassIdReg);
628 __ movl(EDI, FieldAddress(EDI, Class::signature_function_offset())); 236 __ movl(EDI, FieldAddress(EDI, Class::signature_function_offset()));
629 __ cmpl(EDI, raw_null); 237 __ cmpl(EDI, raw_null);
630 __ j(NOT_EQUAL, is_instance_lbl); 238 __ j(NOT_EQUAL, is_instance_lbl);
631 __ jmp(is_not_instance_lbl); 239 __ jmp(is_not_instance_lbl);
632 return; 240 return;
633 } 241 }
634 // Custom checking for numbers (Smi, Mint, Bigint and Double). 242 // Custom checking for numbers (Smi, Mint, Bigint and Double).
635 // Note that instance is not Smi(checked above). 243 // Note that instance is not Smi(checked above).
636 if (type.IsSubtypeOf( 244 if (type.IsSubtypeOf(
637 Type::Handle(Type::NumberInterface()), &malformed_error)) { 245 Type::Handle(Type::NumberInterface()), &malformed_error)) {
638 GenerateNumberTypeCheck( 246 GenerateNumberTypeCheck(
639 kClassIdReg, type, is_instance_lbl, is_not_instance_lbl); 247 kClassIdReg, type, is_instance_lbl, is_not_instance_lbl);
640 return; 248 return;
641 } 249 }
642 if (type.IsStringInterface()) { 250 if (type.IsStringInterface()) {
643 GenerateStringTypeCheck(kClassIdReg, is_instance_lbl, is_not_instance_lbl); 251 GenerateStringTypeCheck(kClassIdReg, is_instance_lbl, is_not_instance_lbl);
644 return; 252 return;
645 } 253 }
254 // Otherwise fallthrough.
255 }
256
257
258 // Uses SubtypeTestCache to store instance class and result.
259 // EAX: instance to test.
260 // Clobbers EDI, ECX.
261 // Immediate class test already done.
262 // TODO(srdjan): Implement a quicker subtype check, as type test
263 // arrays can grow too high, but they may be useful when optimizing
264 // code (type-feedback).
265 RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup(
266 intptr_t cid,
267 intptr_t token_pos,
268 const Class& type_class,
269 Label* is_instance_lbl,
270 Label* is_not_instance_lbl) {
271 const Register kInstanceReg = EAX;
272 __ LoadClass(ECX, kInstanceReg, EDI);
273 // ECX: instance class.
274 // Check immediate superclass equality.
275 __ movl(EDI, FieldAddress(ECX, Class::super_type_offset()));
276 __ movl(EDI, FieldAddress(EDI, Type::type_class_offset()));
277 __ CompareObject(EDI, type_class);
278 __ j(EQUAL, is_instance_lbl);
279
280 const Register kTypeArgumentsReg = kNoRegister;
281 const Register kTempReg = EDI;
282 return GenerateCallSubtypeTestStub(kTestTypeOneArg,
283 kInstanceReg,
284 kTypeArgumentsReg,
285 kTempReg,
286 is_instance_lbl,
287 is_not_instance_lbl);
646 } 288 }
647 289
648 290
649 // Generates inlined check if 'type' is a type parameter or type itsef 291 // Generates inlined check if 'type' is a type parameter or type itsef
650 // EAX: instance (preserved). Clobbers EDX, EDI, ECX. 292 // EAX: instance (preserved).
293 // Clobbers EDX, EDI, ECX.
651 RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( 294 RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest(
652 intptr_t cid, 295 intptr_t cid,
653 intptr_t token_pos, 296 intptr_t token_pos,
654 const AbstractType& type, 297 const AbstractType& type,
655 Label* is_instance_lbl, 298 Label* is_instance_lbl,
656 Label* is_not_instance_lbl) { 299 Label* is_not_instance_lbl) {
657 ASSERT(!type.IsInstantiated()); 300 ASSERT(!type.IsInstantiated());
658 // Skip check if destination is a dynamic type. 301 // Skip check if destination is a dynamic type.
659 const Immediate raw_null = 302 const Immediate raw_null =
660 Immediate(reinterpret_cast<intptr_t>(Object::null())); 303 Immediate(reinterpret_cast<intptr_t>(Object::null()));
661 if (type.IsTypeParameter()) { 304 if (type.IsTypeParameter()) {
662 // Load instantiator (or null) and instantiator type arguments on stack. 305 // Load instantiator (or null) and instantiator type arguments on stack.
663 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. 306 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments.
664 // EDX: instantiator type arguments. 307 // EDX: instantiator type arguments.
665 // Check if type argument is Dynamic. 308 // Check if type argument is Dynamic.
666 __ cmpl(EDX, raw_null); 309 __ cmpl(EDX, raw_null);
667 __ j(EQUAL, is_instance_lbl); 310 __ j(EQUAL, is_instance_lbl);
668 // Can handle only type arguments that are instances of TypeArguments. 311 // Can handle only type arguments that are instances of TypeArguments.
669 // (runtime checks canonicalize type arguments). 312 // (runtime checks canonicalize type arguments).
670 Label fall_through; 313 Label fall_through;
671 __ CompareClassId(EDX, kTypeArguments, EDI); 314 __ CompareClassId(EDX, kTypeArguments, EDI);
672 __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump); 315 __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump);
673
674 __ movl(EDI, 316 __ movl(EDI,
675 FieldAddress(EDX, TypeArguments::type_at_offset(type.Index()))); 317 FieldAddress(EDX, TypeArguments::type_at_offset(type.Index())));
676 // EDI: concrete type of type. 318 // EDI: concrete type of type.
677 // Check if type argument is dynamic. 319 // Check if type argument is dynamic.
678 __ CompareObject(EDI, Type::ZoneHandle(Type::DynamicType())); 320 __ CompareObject(EDI, Type::ZoneHandle(Type::DynamicType()));
679 __ j(EQUAL, is_instance_lbl); 321 __ j(EQUAL, is_instance_lbl);
680 __ cmpl(EDI, raw_null); 322 __ cmpl(EDI, raw_null);
681 __ j(EQUAL, is_instance_lbl); 323 __ j(EQUAL, is_instance_lbl);
682 const Type& object_type = Type::ZoneHandle(Type::ObjectType()); 324 const Type& object_type = Type::ZoneHandle(Type::ObjectType());
683 __ CompareObject(EDI, object_type); 325 __ CompareObject(EDI, object_type);
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
724 kInstanceReg, 366 kInstanceReg,
725 kTypeArgumentsReg, 367 kTypeArgumentsReg,
726 kTempReg, 368 kTempReg,
727 is_instance_lbl, 369 is_instance_lbl,
728 is_not_instance_lbl); 370 is_not_instance_lbl);
729 } 371 }
730 return SubtypeTestCache::null(); 372 return SubtypeTestCache::null();
731 } 373 }
732 374
733 375
734 // Uses SubtypeTestCache to store instance class and result.
735 // EAX: instance to test. Clobbers EDI, ECX.
736 // Immediate class test already done.
737 // TODO(srdjan): Implement a quicker subtype check, as type test
738 // arrays can grow too high, but they may be useful when optimizing
739 // code (type-feedback).
740 RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup(
741 intptr_t cid,
742 intptr_t token_pos,
743 const Class& type_class,
744 Label* is_instance_lbl,
745 Label* is_not_instance_lbl) {
746 const Register kInstanceReg = EAX;
747 __ LoadClass(ECX, kInstanceReg, EDI);
748 // ECX: instance class.
749 // Check immediate superclass equality.
750 __ movl(EDI, FieldAddress(ECX, Class::super_type_offset()));
751 __ movl(EDI, FieldAddress(EDI, Type::type_class_offset()));
752 __ CompareObject(EDI, type_class);
753 __ j(EQUAL, is_instance_lbl);
754
755 const Register kTypeArgumentsReg = kNoRegister;
756 const Register kTempReg = EDI;
757 return GenerateCallSubtypeTestStub(kTestTypeOneArg,
758 kInstanceReg,
759 kTypeArgumentsReg,
760 kTempReg,
761 is_instance_lbl,
762 is_not_instance_lbl);
763 }
764
765
766 // Inputs: 376 // Inputs:
767 // - EAX: instance to test against (preserved). 377 // - EAX: instance to test against (preserved).
768 // - EDX: optional instantiator type arguments (preserved). 378 // - EDX: optional instantiator type arguments (preserved).
379 // Clobbers ECX, EDI.
769 // Returns: 380 // Returns:
770 // - preserved instance in EAX and optional instantiator type arguments in EDX. 381 // - preserved instance in EAX and optional instantiator type arguments in EDX.
771 // Note that this inlined code must be followed by the runtime_call code, as it 382 // Note that this inlined code must be followed by the runtime_call code, as it
772 // may fall through to it. Otherwise, this inline code will jump to the label 383 // may fall through to it. Otherwise, this inline code will jump to the label
773 // is_instance or to the label is_not_instance. 384 // is_instance or to the label is_not_instance.
774 RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( 385 RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof(
775 intptr_t cid, 386 intptr_t cid,
776 intptr_t token_pos, 387 intptr_t token_pos,
777 const AbstractType& type, 388 const AbstractType& type,
778 Label* is_instance_lbl, 389 Label* is_instance_lbl,
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
814 425
815 // If instanceof type test cannot be performed successfully at compile time and 426 // If instanceof type test cannot be performed successfully at compile time and
816 // therefore eliminated, optimize it by adding inlined tests for: 427 // therefore eliminated, optimize it by adding inlined tests for:
817 // - NULL -> return false. 428 // - NULL -> return false.
818 // - Smi -> compile time subtype check (only if dst class is not parameterized). 429 // - Smi -> compile time subtype check (only if dst class is not parameterized).
819 // - Class equality (only if class is not parameterized). 430 // - Class equality (only if class is not parameterized).
820 // Inputs: 431 // Inputs:
821 // - EAX: object. 432 // - EAX: object.
822 // - EDX: instantiator type arguments or raw_null. 433 // - EDX: instantiator type arguments or raw_null.
823 // - ECX: instantiator or raw_null. 434 // - ECX: instantiator or raw_null.
435 // Clobbers ECX and EDX.
824 // Returns: 436 // Returns:
825 // - true or false in EAX. 437 // - true or false in EAX.
826 void FlowGraphCompiler::GenerateInstanceOf(intptr_t cid, 438 void FlowGraphCompiler::GenerateInstanceOf(intptr_t cid,
827 intptr_t token_pos, 439 intptr_t token_pos,
828 intptr_t try_index, 440 intptr_t try_index,
829 const AbstractType& type, 441 const AbstractType& type,
830 bool negate_result) { 442 bool negate_result) {
831 ASSERT(type.IsFinalized() && !type.IsMalformed()); 443 ASSERT(type.IsFinalized() && !type.IsMalformed());
832 444
833 const Immediate raw_null = 445 const Immediate raw_null =
834 Immediate(reinterpret_cast<intptr_t>(Object::null())); 446 Immediate(reinterpret_cast<intptr_t>(Object::null()));
835 Label is_instance, is_not_instance; 447 Label is_instance, is_not_instance;
836 __ pushl(ECX); // Store instantiator on stack. 448 __ pushl(ECX); // Store instantiator on stack.
837 __ pushl(EDX); // Store instantiator type arguments. 449 __ pushl(EDX); // Store instantiator type arguments.
838 // If type is instantiated and non-parameterized, we can inline code 450 // If type is instantiated and non-parameterized, we can inline code
839 // checking whether the tested instance is a Smi. 451 // checking whether the tested instance is a Smi.
840 if (type.IsInstantiated()) { 452 if (type.IsInstantiated()) {
841 // A null object is only an instance of Object and Dynamic, which has 453 // A null object is only an instance of Object and Dynamic, which has
842 // already been checked above (if the type is instantiated). So we can 454 // already been checked above (if the type is instantiated). So we can
843 // return false here if the instance is null (and if the type is 455 // return false here if the instance is null (and if the type is
844 // instantiated). 456 // instantiated).
845 // We can only inline this null check if the type is instantiated at compile 457 // We can only inline this null check if the type is instantiated at compile
846 // time, since an uninstantiated type at compile time could be Object or 458 // time, since an uninstantiated type at compile time could be Object or
847 // Dynamic at run time. 459 // Dynamic at run time.
848 __ cmpl(EAX, raw_null); 460 __ cmpl(EAX, raw_null);
849 __ j(EQUAL, &is_not_instance); 461 __ j(EQUAL, &is_not_instance);
850 } 462 }
851 // TODO(srdjan): Enable inlined checks. 463
852 // Generate inline instanceof test. 464 // Generate inline instanceof test.
853 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); 465 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle();
854 test_cache = GenerateInlineInstanceof(cid, token_pos, type, 466 test_cache = GenerateInlineInstanceof(cid, token_pos, type,
855 &is_instance, &is_not_instance); 467 &is_instance, &is_not_instance);
856 468
857 // Generate runtime call. 469 // Generate runtime call.
858 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. 470 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments.
859 __ movl(ECX, Address(ESP, kWordSize)); // Get instantiator. 471 __ movl(ECX, Address(ESP, kWordSize)); // Get instantiator.
860 __ PushObject(Object::ZoneHandle()); // Make room for the result. 472 __ PushObject(Object::ZoneHandle()); // Make room for the result.
861 __ pushl(Immediate(Smi::RawValue(token_pos))); // Source location. 473 __ pushl(Immediate(Smi::RawValue(token_pos))); // Source location.
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
941 token_pos, 553 token_pos,
942 try_index, 554 try_index,
943 kMalformedTypeErrorRuntimeEntry); 555 kMalformedTypeErrorRuntimeEntry);
944 // We should never return here. 556 // We should never return here.
945 __ int3(); 557 __ int3();
946 558
947 __ Bind(&is_assignable); // For a null object. 559 __ Bind(&is_assignable); // For a null object.
948 return; 560 return;
949 } 561 }
950 562
951 // TODO(srdjan): Enable subtype test cache.
952 // Generate inline type check, linking to runtime call if not assignable. 563 // Generate inline type check, linking to runtime call if not assignable.
953 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); 564 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle();
954 test_cache = GenerateInlineInstanceof(cid, token_pos, dst_type, 565 test_cache = GenerateInlineInstanceof(cid, token_pos, dst_type,
955 &is_assignable, &runtime_call); 566 &is_assignable, &runtime_call);
956 567
957 __ Bind(&runtime_call); 568 __ Bind(&runtime_call);
958 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. 569 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments.
959 __ movl(ECX, Address(ESP, kWordSize)); // Get instantiator. 570 __ movl(ECX, Address(ESP, kWordSize)); // Get instantiator.
960 __ PushObject(Object::ZoneHandle()); // Make room for the result. 571 __ PushObject(Object::ZoneHandle()); // Make room for the result.
961 __ pushl(Immediate(Smi::RawValue(token_pos))); // Source location. 572 __ pushl(Immediate(Smi::RawValue(token_pos))); // Source location.
(...skipping 26 matching lines...) Expand all
988 599
989 frame_register_allocator()->AllocateRegisters(instr); 600 frame_register_allocator()->AllocateRegisters(instr);
990 601
991 // TODO(vegorov): adjust assertion when we start removing comparison from the 602 // TODO(vegorov): adjust assertion when we start removing comparison from the
992 // graph when it is merged with a branch. 603 // graph when it is merged with a branch.
993 ASSERT(locs->is_call() || 604 ASSERT(locs->is_call() ||
994 (instr->IsBranch() && instr->AsBranch()->is_fused_with_comparison()) || 605 (instr->IsBranch() && instr->AsBranch()->is_fused_with_comparison()) ||
995 (locs->input_count() == instr->InputCount())); 606 (locs->input_count() == instr->InputCount()));
996 } 607 }
997 608
609
610 void FlowGraphCompiler::CopyParameters() {
611 const Function& function = parsed_function().function();
612 const bool is_native_instance_closure =
613 function.is_native() && function.IsImplicitInstanceClosureFunction();
614 LocalScope* scope = parsed_function().node_sequence()->scope();
615 const int num_fixed_params = function.num_fixed_parameters();
616 const int num_opt_params = function.num_optional_parameters();
617 int implicit_this_param_pos = is_native_instance_closure ? -1 : 0;
618 ASSERT(parsed_function().first_parameter_index() ==
619 ParsedFunction::kFirstLocalSlotIndex + implicit_this_param_pos);
620 // Copy positional arguments.
621 // Check that no fewer than num_fixed_params positional arguments are passed
622 // in and that no more than num_params arguments are passed in.
623 // Passed argument i at fp[1 + argc - i]
624 // copied to fp[ParsedFunction::kFirstLocalSlotIndex - i].
625 const int num_params = num_fixed_params + num_opt_params;
626
627 // Total number of args is the first Smi in args descriptor array (EDX).
628 __ movl(EBX, FieldAddress(EDX, Array::data_offset()));
629 // Check that num_args <= num_params.
630 Label wrong_num_arguments;
631 __ cmpl(EBX, Immediate(Smi::RawValue(num_params)));
632 __ j(GREATER, &wrong_num_arguments);
633 // Number of positional args is the second Smi in descriptor array (EDX).
634 __ movl(ECX, FieldAddress(EDX, Array::data_offset() + (1 * kWordSize)));
635 // Check that num_pos_args >= num_fixed_params.
636 __ cmpl(ECX, Immediate(Smi::RawValue(num_fixed_params)));
637 __ j(LESS, &wrong_num_arguments);
638
639 // Since EBX and ECX are Smi, use TIMES_2 instead of TIMES_4.
640 // Let EBX point to the last passed positional argument, i.e. to
641 // fp[1 + num_args - (num_pos_args - 1)].
642 __ subl(EBX, ECX);
643 __ leal(EBX, Address(EBP, EBX, TIMES_2, 2 * kWordSize));
644
645 // Let EDI point to the last copied positional argument, i.e. to
646 // fp[ParsedFunction::kFirstLocalSlotIndex - (num_pos_args - 1)].
647 const int index =
648 ParsedFunction::kFirstLocalSlotIndex + 1 + implicit_this_param_pos;
649 // First copy captured receiver if function is an implicit native closure.
650 if (is_native_instance_closure) {
651 __ movl(EAX, FieldAddress(CTX, Context::variable_offset(0)));
652 __ movl(Address(EBP, (index * kWordSize)), EAX);
653 }
654 __ leal(EDI, Address(EBP, (index * kWordSize)));
655 __ subl(EDI, ECX); // ECX is a Smi, subtract twice for TIMES_4 scaling.
656 __ subl(EDI, ECX);
657 __ SmiUntag(ECX);
658 Label loop, loop_condition;
659 __ jmp(&loop_condition, Assembler::kNearJump);
660 // We do not use the final allocation index of the variable here, i.e.
661 // scope->VariableAt(i)->index(), because captured variables still need
662 // to be copied to the context that is not yet allocated.
663 const Address argument_addr(EBX, ECX, TIMES_4, 0);
664 const Address copy_addr(EDI, ECX, TIMES_4, 0);
665 __ Bind(&loop);
666 __ movl(EAX, argument_addr);
667 __ movl(copy_addr, EAX);
668 __ Bind(&loop_condition);
669 __ decl(ECX);
670 __ j(POSITIVE, &loop, Assembler::kNearJump);
671
672 // Copy or initialize optional named arguments.
673 const Immediate raw_null =
674 Immediate(reinterpret_cast<intptr_t>(Object::null()));
675 Label all_arguments_processed;
676 if (num_opt_params > 0) {
677 // Start by alphabetically sorting the names of the optional parameters.
678 LocalVariable** opt_param = new LocalVariable*[num_opt_params];
679 int* opt_param_position = new int[num_opt_params];
680 for (int pos = num_fixed_params; pos < num_params; pos++) {
681 LocalVariable* parameter = scope->VariableAt(pos);
682 const String& opt_param_name = parameter->name();
683 int i = pos - num_fixed_params;
684 while (--i >= 0) {
685 LocalVariable* param_i = opt_param[i];
686 const intptr_t result = opt_param_name.CompareTo(param_i->name());
687 ASSERT(result != 0);
688 if (result > 0) break;
689 opt_param[i + 1] = opt_param[i];
690 opt_param_position[i + 1] = opt_param_position[i];
691 }
692 opt_param[i + 1] = parameter;
693 opt_param_position[i + 1] = pos;
694 }
695 // Generate code handling each optional parameter in alphabetical order.
696 // Total number of args is the first Smi in args descriptor array (EDX).
697 __ movl(EBX, FieldAddress(EDX, Array::data_offset()));
698 // Number of positional args is the second Smi in descriptor array (EDX).
699 __ movl(ECX, FieldAddress(EDX, Array::data_offset() + (1 * kWordSize)));
700 __ SmiUntag(ECX);
701 // Let EBX point to the first passed argument, i.e. to fp[1 + argc - 0].
702 __ leal(EBX, Address(EBP, EBX, TIMES_2, kWordSize)); // EBX is Smi.
703 // Let EDI point to the name/pos pair of the first named argument.
704 __ leal(EDI, FieldAddress(EDX, Array::data_offset() + (2 * kWordSize)));
705 for (int i = 0; i < num_opt_params; i++) {
706 // Handle this optional parameter only if k or fewer positional arguments
707 // have been passed, where k is the position of this optional parameter in
708 // the formal parameter list.
709 Label load_default_value, assign_optional_parameter, next_parameter;
710 const int param_pos = opt_param_position[i];
711 __ cmpl(ECX, Immediate(param_pos));
712 __ j(GREATER, &next_parameter, Assembler::kNearJump);
713 // Check if this named parameter was passed in.
714 __ movl(EAX, Address(EDI, 0)); // Load EAX with the name of the argument.
715 __ CompareObject(EAX, opt_param[i]->name());
716 __ j(NOT_EQUAL, &load_default_value, Assembler::kNearJump);
717 // Load EAX with passed-in argument at provided arg_pos, i.e. at
718 // fp[1 + argc - arg_pos].
719 __ movl(EAX, Address(EDI, kWordSize)); // EAX is arg_pos as Smi.
720 __ addl(EDI, Immediate(2 * kWordSize)); // Point to next name/pos pair.
721 __ negl(EAX);
722 Address argument_addr(EBX, EAX, TIMES_2, 0); // EAX is a negative Smi.
723 __ movl(EAX, argument_addr);
724 __ jmp(&assign_optional_parameter, Assembler::kNearJump);
725 __ Bind(&load_default_value);
726 // Load EAX with default argument at pos.
727 const Object& value = Object::ZoneHandle(
728 parsed_function().default_parameter_values().At(
729 param_pos - num_fixed_params));
730 __ LoadObject(EAX, value);
731 __ Bind(&assign_optional_parameter);
732 // Assign EAX to fp[ParsedFunction::kFirstLocalSlotIndex - param_pos].
733 // We do not use the final allocation index of the variable here, i.e.
734 // scope->VariableAt(i)->index(), because captured variables still need
735 // to be copied to the context that is not yet allocated.
736 intptr_t computed_param_pos = (ParsedFunction::kFirstLocalSlotIndex -
737 param_pos + implicit_this_param_pos);
738 const Address param_addr(EBP, (computed_param_pos * kWordSize));
739 __ movl(param_addr, EAX);
740 __ Bind(&next_parameter);
741 }
742 delete[] opt_param;
743 delete[] opt_param_position;
744 // Check that EDI now points to the null terminator in the array descriptor.
745 __ cmpl(Address(EDI, 0), raw_null);
746 __ j(EQUAL, &all_arguments_processed, Assembler::kNearJump);
747 } else {
748 ASSERT(is_native_instance_closure);
749 __ jmp(&all_arguments_processed, Assembler::kNearJump);
750 }
751
752 __ Bind(&wrong_num_arguments);
753 if (StackSize() != 0) {
754 // We need to unwind the space we reserved for locals and copied parameters.
755 // The NoSuchMethodFunction stub does not expect to see that area on the
756 // stack.
757 __ addl(ESP, Immediate(StackSize() * kWordSize));
758 }
759 if (function.IsClosureFunction()) {
760 GenerateCallRuntime(AstNode::kNoId,
761 0,
762 CatchClauseNode::kInvalidTryIndex,
763 kClosureArgumentMismatchRuntimeEntry);
764 } else {
765 // Invoke noSuchMethod function.
766 const int kNumArgsChecked = 1;
767 ICData& ic_data = ICData::ZoneHandle();
768 ic_data = ICData::New(function,
769 String::Handle(function.name()),
770 AstNode::kNoId,
771 kNumArgsChecked);
772 __ LoadObject(ECX, ic_data);
773 // EBP - 4 : PC marker, allows easy identification of RawInstruction obj.
774 // EBP : points to previous frame pointer.
775 // EBP + 4 : points to return address.
776 // EBP + 8 : address of last argument (arg n-1).
777 // ESP + 8 + 4*(n-1) : address of first argument (arg 0).
778 // ECX : ic-data.
779 // EDX : arguments descriptor array.
780 __ call(&StubCode::CallNoSuchMethodFunctionLabel());
781 }
782
783 if (FLAG_trace_functions) {
784 __ pushl(EAX); // Preserve result.
785 __ PushObject(Function::ZoneHandle(function.raw()));
786 GenerateCallRuntime(AstNode::kNoId,
787 0,
788 CatchClauseNode::kInvalidTryIndex,
789 kTraceFunctionExitRuntimeEntry);
790 __ popl(EAX); // Remove argument.
791 __ popl(EAX); // Restore result.
792 }
793 __ LeaveFrame();
794 __ ret();
795
796 __ Bind(&all_arguments_processed);
797 // Nullify originally passed arguments only after they have been copied and
798 // checked, otherwise noSuchMethod would not see their original values.
799 // This step can be skipped in case we decide that formal parameters are
800 // implicitly final, since garbage collecting the unmodified value is not
801 // an issue anymore.
802
803 // EDX : arguments descriptor array.
804 // Total number of args is the first Smi in args descriptor array (EDX).
805 __ movl(ECX, FieldAddress(EDX, Array::data_offset()));
806 __ SmiUntag(ECX);
807 Label null_args_loop, null_args_loop_condition;
808 __ jmp(&null_args_loop_condition, Assembler::kNearJump);
809 const Address original_argument_addr(EBP, ECX, TIMES_4, 2 * kWordSize);
810 __ Bind(&null_args_loop);
811 __ movl(original_argument_addr, raw_null);
812 __ Bind(&null_args_loop_condition);
813 __ decl(ECX);
814 __ j(POSITIVE, &null_args_loop, Assembler::kNearJump);
815 }
816
817
818 void FlowGraphCompiler::GenerateInlinedGetter(intptr_t offset) {
819 // TOS: return address.
820 // +1 : receiver.
821 // Sequence node has one return node, its input is load field node.
822 __ movl(EAX, Address(ESP, 1 * kWordSize));
823 __ movl(EAX, FieldAddress(EAX, offset));
824 __ ret();
825 }
826
827
828 void FlowGraphCompiler::GenerateInlinedSetter(intptr_t offset) {
829 // TOS: return address.
830 // +1 : value
831 // +2 : receiver.
832 // Sequence node has one store node and one return NULL node.
833 __ movl(EAX, Address(ESP, 2 * kWordSize)); // Receiver.
834 __ movl(EBX, Address(ESP, 1 * kWordSize)); // Value.
835 __ StoreIntoObject(EAX, FieldAddress(EAX, offset), EBX);
836 const Immediate raw_null =
837 Immediate(reinterpret_cast<intptr_t>(Object::null()));
838 __ movl(EAX, raw_null);
839 __ ret();
840 }
841
842
843 void FlowGraphCompiler::GenerateInlinedMathSqrt(Label* done) {
844 Label smi_to_double, double_op, call_method;
845 __ movl(EAX, Address(ESP, 0));
846 __ testl(EAX, Immediate(kSmiTagMask));
847 __ j(ZERO, &smi_to_double);
848 __ CompareClassId(EAX, kDouble, EBX);
849 __ j(NOT_EQUAL, &call_method);
850 __ movsd(XMM1, FieldAddress(EAX, Double::value_offset()));
851 __ Bind(&double_op);
852 __ sqrtsd(XMM0, XMM1);
853 AssemblerMacros::TryAllocate(assembler_,
854 double_class_,
855 &call_method,
856 EAX); // Result register.
857 __ movsd(FieldAddress(EAX, Double::value_offset()), XMM0);
858 __ Drop(1);
859 __ jmp(done);
860 __ Bind(&smi_to_double);
861 __ SmiUntag(EAX);
862 __ cvtsi2sd(XMM1, EAX);
863 __ jmp(&double_op);
864 __ Bind(&call_method);
865 }
866
867
868 void FlowGraphCompiler::CompileGraph() {
869 InitCompiler();
870 if (TryIntrinsify()) {
871 // Although this intrinsified code will never be patched, it must satisfy
872 // CodePatcher::CodeIsPatchable, which verifies that this code has a minimum
873 // code size.
874 __ int3();
875 __ jmp(&StubCode::FixCallersTargetLabel());
876 return;
877 }
878 // Specialized version of entry code from CodeGenerator::GenerateEntryCode.
879 const Function& function = parsed_function().function();
880
881 const int parameter_count = function.num_fixed_parameters();
882 const int num_copied_params = parsed_function().copied_parameter_count();
883 const int local_count = parsed_function().stack_local_count();
884 AssemblerMacros::EnterDartFrame(assembler(), (StackSize() * kWordSize));
885 // We check the number of passed arguments when we have to copy them due to
886 // the presence of optional named parameters.
887 // No such checking code is generated if only fixed parameters are declared,
888 // unless we are debug mode or unless we are compiling a closure.
889 if (num_copied_params == 0) {
890 #ifdef DEBUG
891 const bool check_arguments = true;
892 #else
893 const bool check_arguments = function.IsClosureFunction();
894 #endif
895 if (check_arguments) {
896 // Check that num_fixed <= argc <= num_params.
897 Label argc_in_range;
898 // Total number of args is the first Smi in args descriptor array (EDX).
899 __ movl(EAX, FieldAddress(EDX, Array::data_offset()));
900 __ cmpl(EAX, Immediate(Smi::RawValue(parameter_count)));
901 __ j(EQUAL, &argc_in_range, Assembler::kNearJump);
902 if (function.IsClosureFunction()) {
903 GenerateCallRuntime(AstNode::kNoId,
904 function.token_pos(),
905 CatchClauseNode::kInvalidTryIndex,
906 kClosureArgumentMismatchRuntimeEntry);
907 } else {
908 __ Stop("Wrong number of arguments");
909 }
910 __ Bind(&argc_in_range);
911 }
912 } else {
913 CopyParameters();
914 }
915 // Initialize (non-argument) stack allocated locals to null.
916 if (local_count > 0) {
917 const Immediate raw_null =
918 Immediate(reinterpret_cast<intptr_t>(Object::null()));
919 __ movl(EAX, raw_null);
920 const int base = parsed_function().first_stack_local_index();
921 for (int i = 0; i < local_count; ++i) {
922 // Subtract index i (locals lie at lower addresses than EBP).
923 __ movl(Address(EBP, (base - i) * kWordSize), EAX);
924 }
925 }
926
927 // Generate stack overflow check.
928 __ cmpl(ESP,
929 Address::Absolute(Isolate::Current()->stack_limit_address()));
930 Label no_stack_overflow;
931 __ j(ABOVE, &no_stack_overflow, Assembler::kNearJump);
932 GenerateCallRuntime(AstNode::kNoId,
933 function.token_pos(),
934 CatchClauseNode::kInvalidTryIndex,
935 kStackOverflowRuntimeEntry);
936 __ Bind(&no_stack_overflow);
937
938 if (FLAG_print_scopes) {
939 // Print the function scope (again) after generating the prologue in order
940 // to see annotations such as allocation indices of locals.
941 if (FLAG_print_ast) {
942 // Second printing.
943 OS::Print("Annotated ");
944 }
945 AstPrinter::PrintFunctionScope(parsed_function());
946 }
947
948 VisitBlocks();
949
950 __ int3();
951 GenerateDeferredCode();
952 // Emit function patching code. This will be swapped with the first 5 bytes
953 // at entry point.
954 pc_descriptors_list()->AddDescriptor(PcDescriptors::kPatchCode,
955 assembler()->CodeSize(),
956 AstNode::kNoId,
957 0,
958 -1);
959 __ jmp(&StubCode::FixCallersTargetLabel());
960 }
961
962
963 void FlowGraphCompiler::GenerateCall(intptr_t token_pos,
964 intptr_t try_index,
965 const ExternalLabel* label,
966 PcDescriptors::Kind kind) {
967 ASSERT(frame_register_allocator()->IsSpilled());
968 __ call(label);
969 AddCurrentDescriptor(kind, AstNode::kNoId, token_pos, try_index);
970 }
971
972
973 void FlowGraphCompiler::GenerateCallRuntime(intptr_t cid,
974 intptr_t token_pos,
975 intptr_t try_index,
976 const RuntimeEntry& entry) {
977 ASSERT(frame_register_allocator()->IsSpilled());
978 __ CallRuntime(entry);
979 AddCurrentDescriptor(PcDescriptors::kOther, cid, token_pos, try_index);
980 }
981
982
983 intptr_t FlowGraphCompiler::EmitInstanceCall(ExternalLabel* target_label,
984 const ICData& ic_data,
985 const Array& arguments_descriptor,
986 intptr_t argument_count) {
987 __ LoadObject(ECX, ic_data);
988 __ LoadObject(EDX, arguments_descriptor);
989
990 __ call(target_label);
991 const intptr_t descr_offset = assembler()->CodeSize();
992 __ Drop(argument_count);
993 return descr_offset;
994 }
995
996
997 intptr_t FlowGraphCompiler::EmitStaticCall(const Function& function,
998 const Array& arguments_descriptor,
999 intptr_t argument_count) {
1000 __ LoadObject(ECX, function);
1001 __ LoadObject(EDX, arguments_descriptor);
1002 __ call(&StubCode::CallStaticFunctionLabel());
1003 const intptr_t descr_offset = assembler()->CodeSize();
1004 __ Drop(argument_count);
1005 return descr_offset;
1006 }
1007
1008
998 // Checks class id of instance against all 'class_ids'. Jump to 'deopt' label 1009 // Checks class id of instance against all 'class_ids'. Jump to 'deopt' label
999 // if no match or instance is Smi. 1010 // if no match or instance is Smi.
1000 void FlowGraphCompiler::EmitClassChecksNoSmi(const ICData& ic_data, 1011 void FlowGraphCompiler::EmitClassChecksNoSmi(const ICData& ic_data,
1001 Register instance_reg, 1012 Register instance_reg,
1002 Register temp_reg, 1013 Register temp_reg,
1003 Label* deopt) { 1014 Label* deopt) {
1004 Label ok; 1015 Label ok;
1005 ASSERT(ic_data.GetReceiverClassIdAt(0) != kSmi); 1016 ASSERT(ic_data.GetReceiverClassIdAt(0) != kSmi);
1006 __ testl(instance_reg, Immediate(kSmiTagMask)); 1017 __ testl(instance_reg, Immediate(kSmiTagMask));
1007 __ j(ZERO, deopt); 1018 __ j(ZERO, deopt);
(...skipping 17 matching lines...) Expand all
1025 } 1036 }
1026 1037
1027 1038
1028 void FlowGraphCompiler::LoadDoubleOrSmiToXmm(XmmRegister result, 1039 void FlowGraphCompiler::LoadDoubleOrSmiToXmm(XmmRegister result,
1029 Register reg, 1040 Register reg,
1030 Register temp, 1041 Register temp,
1031 Label* not_double_or_smi) { 1042 Label* not_double_or_smi) {
1032 Label is_smi, done; 1043 Label is_smi, done;
1033 __ testl(reg, Immediate(kSmiTagMask)); 1044 __ testl(reg, Immediate(kSmiTagMask));
1034 __ j(ZERO, &is_smi); 1045 __ j(ZERO, &is_smi);
1035 __ LoadClassId(temp, reg); 1046 __ CompareClassId(reg, kDouble, temp);
1036 __ cmpl(temp, Immediate(kDouble));
1037 __ j(NOT_EQUAL, not_double_or_smi); 1047 __ j(NOT_EQUAL, not_double_or_smi);
1038 __ movsd(result, FieldAddress(reg, Double::value_offset())); 1048 __ movsd(result, FieldAddress(reg, Double::value_offset()));
1039 __ jmp(&done); 1049 __ jmp(&done);
1040 __ Bind(&is_smi); 1050 __ Bind(&is_smi);
1041 __ movl(temp, reg); 1051 __ movl(temp, reg);
1042 __ SmiUntag(temp); 1052 __ SmiUntag(temp);
1043 __ cvtsi2sd(result, temp); 1053 __ cvtsi2sd(result, temp);
1044 __ Bind(&done); 1054 __ Bind(&done);
1045 } 1055 }
1046 1056
1047 1057
1048 #undef __ 1058 #undef __
1049 1059
1050 } // namespace dart 1060 } // namespace dart
1051 1061
1052 #endif // defined TARGET_ARCH_IA32 1062 #endif // defined TARGET_ARCH_IA32
OLDNEW
« no previous file with comments | « runtime/vm/flow_graph_compiler_ia32.h ('k') | runtime/vm/flow_graph_compiler_x64.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698