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

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

Issue 10665038: Remove old code generator. (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
OLDNEW
(Empty)
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
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/code_generator.h"
9
10 #include "lib/error.h"
11 #include "vm/assembler_macros.h"
12 #include "vm/ast_printer.h"
13 #include "vm/class_finalizer.h"
14 #include "vm/code_descriptors.h"
15 #include "vm/dart_entry.h"
16 #include "vm/debugger.h"
17 #include "vm/intrinsifier.h"
18 #include "vm/longjump.h"
19 #include "vm/object.h"
20 #include "vm/object_store.h"
21 #include "vm/parser.h"
22 #include "vm/resolver.h"
23 #include "vm/stub_code.h"
24
25 namespace dart {
26
27 DECLARE_FLAG(bool, print_ast);
28 DECLARE_FLAG(bool, print_scopes);
29 DECLARE_FLAG(bool, trace_functions);
30 DEFINE_FLAG(bool, print_ic_in_optimized, false,
31 "Debugging helper to identify potential performance pitfalls.");
32 DECLARE_FLAG(int, optimization_counter_threshold);
33 DECLARE_FLAG(bool, enable_type_checks);
34 DECLARE_FLAG(bool, trace_compiler);
35 DECLARE_FLAG(bool, intrinsify);
36 DECLARE_FLAG(bool, trace_functions);
37
38
39 #define __ assembler_->
40
41
42 CodeGeneratorState::CodeGeneratorState(CodeGenerator* codegen)
43 : StackResource(Isolate::Current()),
44 codegen_(codegen),
45 parent_(codegen->state()) {
46 if (parent_ != NULL) {
47 root_node_ = parent_->root_node_;
48 current_try_index_ = parent_->current_try_index_;
49 } else {
50 root_node_ = NULL;
51 current_try_index_ = CatchClauseNode::kInvalidTryIndex;
52 }
53 codegen_->set_state(this);
54 }
55
56
57 CodeGeneratorState::~CodeGeneratorState() {
58 codegen_->set_state(parent_);
59 }
60
61
62 CodeGenerator::CodeGenerator(Assembler* assembler,
63 const ParsedFunction& parsed_function)
64 : assembler_(assembler),
65 parsed_function_(parsed_function),
66 locals_space_size_(-1),
67 state_(NULL),
68 pc_descriptors_list_(NULL),
69 stackmap_builder_(NULL),
70 exception_handlers_list_(NULL),
71 try_index_(CatchClauseNode::kInvalidTryIndex),
72 context_level_(0) {
73 ASSERT(assembler_ != NULL);
74 ASSERT(parsed_function.node_sequence() != NULL);
75 ASSERT(Isolate::Current()->long_jump_base()->IsSafeToJump());
76 }
77
78
79 void CodeGenerator::InitGenerator() {
80 pc_descriptors_list_ = new DescriptorList();
81 // We do not build any stack maps in the unoptimizing compiler.
82 exception_handlers_list_ = new ExceptionHandlerList();
83 }
84
85
86 void CodeGenerator::IntrinsifyGetter() {
87 // TOS: return address.
88 // +1 : receiver.
89 // Sequence node has one return node, its input is oad field node.
90 const SequenceNode& sequence_node = *parsed_function_.node_sequence();
91 ASSERT(sequence_node.length() == 1);
92 ASSERT(sequence_node.NodeAt(0)->IsReturnNode());
93 const ReturnNode& return_node = *sequence_node.NodeAt(0)->AsReturnNode();
94 ASSERT(return_node.value()->IsLoadInstanceFieldNode());
95 const LoadInstanceFieldNode& load_node =
96 *return_node.value()->AsLoadInstanceFieldNode();
97 __ movl(EAX, Address(ESP, 1 * kWordSize));
98 __ movl(EAX, FieldAddress(EAX, load_node.field().Offset()));
99 __ ret();
100 }
101
102
103 void CodeGenerator::IntrinsifySetter() {
104 // TOS: return address.
105 // +1 : value
106 // +2 : receiver.
107 // Sequence node has one store node and one return NULL node.
108 const SequenceNode& sequence_node = *parsed_function_.node_sequence();
109 ASSERT(sequence_node.length() == 2);
110 ASSERT(sequence_node.NodeAt(0)->IsStoreInstanceFieldNode());
111 ASSERT(sequence_node.NodeAt(1)->IsReturnNode());
112 const StoreInstanceFieldNode& store_node =
113 *sequence_node.NodeAt(0)->AsStoreInstanceFieldNode();
114 __ movl(EAX, Address(ESP, 2 * kWordSize)); // Receiver.
115 __ movl(EBX, Address(ESP, 1 * kWordSize)); // Value.
116 __ StoreIntoObject(EAX, FieldAddress(EAX, store_node.field().Offset()), EBX);
117 const Immediate raw_null =
118 Immediate(reinterpret_cast<intptr_t>(Object::null()));
119 __ movl(EAX, raw_null);
120 __ ret();
121 }
122
123
124
125 bool CodeGenerator::TryIntrinsify() {
126 if (!CanOptimize()) return false;
127 // Intrinsification skips arguments checks, therefore disable if in checked
128 // mode.
129 if (FLAG_intrinsify && !FLAG_trace_functions && !FLAG_enable_type_checks) {
130 if ((parsed_function_.function().kind() == RawFunction::kImplicitGetter)) {
131 IntrinsifyGetter();
132 return true;
133 }
134 if ((parsed_function_.function().kind() == RawFunction::kImplicitSetter)) {
135 IntrinsifySetter();
136 return true;
137 }
138 }
139 // Even if an intrinsified version of the function was successfully
140 // generated, it may fall through to the non-intrinsified method body.
141 if (!FLAG_trace_functions) {
142 return Intrinsifier::Intrinsify(parsed_function().function(), assembler_);
143 }
144 return false;
145 }
146
147
148 bool CodeGenerator::IsResultNeeded(AstNode* node) const {
149 return !state()->IsRootNode(node);
150 }
151
152
153 // NOTE: First 5 bytes of the code may be patched with a jump instruction. Do
154 // not emit any objects in the first 5 bytes.
155 void CodeGenerator::GenerateCode() {
156 InitGenerator();
157 CodeGeneratorState codegen_state(this);
158 if (FLAG_print_scopes && FLAG_print_ast) {
159 // Print the function scope before code generation.
160 AstPrinter::PrintFunctionScope(parsed_function_);
161 }
162 if (FLAG_print_ast) {
163 // Print the function ast before code generation.
164 AstPrinter::PrintFunctionNodes(parsed_function_);
165 }
166 if (FLAG_trace_functions) {
167 // Preserve ECX (ic-data array or object) and EDX (arguments descriptor).
168 __ nop(2); // Make sure the code is patchable.
169 __ pushl(ECX);
170 __ pushl(EDX);
171 const Function& function =
172 Function::ZoneHandle(parsed_function_.function().raw());
173 __ LoadObject(EAX, function);
174 __ pushl(EAX);
175 GenerateCallRuntime(AstNode::kNoId,
176 0,
177 kTraceFunctionEntryRuntimeEntry);
178 __ popl(EAX);
179 __ popl(EDX);
180 __ popl(ECX);
181 }
182
183 const bool code_generation_finished = TryIntrinsify();
184 // In some cases intrinsifier can generate all code and no AST based
185 // code generation is needed. In some cases slow-paths (e.g., overflows) are
186 // implemented by the AST based code generation and 'code_generation_finished'
187 // is false.
188 if (!code_generation_finished) {
189 GenerateEntryCode();
190 if (FLAG_print_scopes) {
191 // Print the function scope (again) after generating the prologue in order
192 // to see annotations such as allocation indices of locals.
193 if (FLAG_print_ast) {
194 // Second printing.
195 OS::Print("Annotated ");
196 }
197 AstPrinter::PrintFunctionScope(parsed_function_);
198 }
199 parsed_function_.node_sequence()->Visit(this);
200 }
201 // End of code.
202 __ int3();
203 GenerateDeferredCode();
204
205 // Emit function patching code. This will be swapped with the first 5 bytes
206 // at entry point.
207 pc_descriptors_list_->AddDescriptor(PcDescriptors::kPatchCode,
208 assembler_->CodeSize(),
209 AstNode::kNoId,
210 0,
211 -1);
212 __ jmp(&StubCode::FixCallersTargetLabel());
213 }
214
215
216 void CodeGenerator::GenerateDeferredCode() {
217 }
218
219
220 void CodeGenerator::FinalizePcDescriptors(const Code& code) {
221 ASSERT(pc_descriptors_list_ != NULL);
222 const PcDescriptors& descriptors = PcDescriptors::Handle(
223 pc_descriptors_list_->FinalizePcDescriptors(code.EntryPoint()));
224 descriptors.Verify(parsed_function_.function().is_optimizable());
225 code.set_pc_descriptors(descriptors);
226 }
227
228
229 void CodeGenerator::FinalizeStackmaps(const Code& code) {
230 if (stackmap_builder_ == NULL) {
231 // The unoptimizing compiler has no stack maps.
232 code.set_stackmaps(Array::Handle());
233 } else {
234 // Finalize the stack map array and add it to the code object.
235 code.set_stackmaps(
236 Array::Handle(stackmap_builder_->FinalizeStackmaps(code)));
237 }
238 }
239
240
241 void CodeGenerator::FinalizeVarDescriptors(const Code& code) {
242 const LocalVarDescriptors& var_descs = LocalVarDescriptors::Handle(
243 parsed_function_.node_sequence()->scope()->GetVarDescriptors(
244 parsed_function_.function()));
245 code.set_var_descriptors(var_descs);
246 }
247
248
249 void CodeGenerator::FinalizeExceptionHandlers(const Code& code) {
250 ASSERT(exception_handlers_list_ != NULL);
251 const ExceptionHandlers& handlers = ExceptionHandlers::Handle(
252 exception_handlers_list_->FinalizeExceptionHandlers(code.EntryPoint()));
253 code.set_exception_handlers(handlers);
254 }
255
256
257 void CodeGenerator::FinalizeComments(const Code& code) {
258 code.set_comments(assembler_->GetCodeComments());
259 }
260
261
262 void CodeGenerator::GenerateLoadVariable(Register dst,
263 const LocalVariable& variable) {
264 if (variable.is_captured()) {
265 // The variable lives in the context.
266 intptr_t delta = context_level() - variable.owner()->context_level();
267 ASSERT(delta >= 0);
268 Register base = CTX;
269 while (delta-- > 0) {
270 __ movl(dst, FieldAddress(base, Context::parent_offset()));
271 base = dst;
272 }
273 __ movl(dst,
274 FieldAddress(base, Context::variable_offset(variable.index())));
275 } else {
276 // The variable lives in the current stack frame.
277 __ movl(dst, Address(EBP, variable.index() * kWordSize));
278 }
279 }
280
281
282 void CodeGenerator::GenerateStoreVariable(const LocalVariable& variable,
283 Register src,
284 Register scratch) {
285 if (variable.is_captured()) {
286 // The variable lives in the context.
287 intptr_t delta = context_level() - variable.owner()->context_level();
288 ASSERT(delta >= 0);
289 Register base = CTX;
290 while (delta-- > 0) {
291 __ movl(scratch, FieldAddress(base, Context::parent_offset()));
292 base = scratch;
293 }
294 __ StoreIntoObject(
295 base,
296 FieldAddress(base, Context::variable_offset(variable.index())),
297 src);
298 } else {
299 // The variable lives in the current stack frame.
300 __ movl(Address(EBP, variable.index() * kWordSize), src);
301 }
302 }
303
304
305 void CodeGenerator::GeneratePushVariable(const LocalVariable& variable,
306 Register scratch) {
307 if (variable.is_captured()) {
308 // The variable lives in the context.
309 intptr_t delta = context_level() - variable.owner()->context_level();
310 ASSERT(delta >= 0);
311 Register base = CTX;
312 while (delta-- > 0) {
313 __ movl(scratch, FieldAddress(base, Context::parent_offset()));
314 base = scratch;
315 }
316 __ pushl(FieldAddress(base, Context::variable_offset(variable.index())));
317 } else {
318 // The variable lives in the current stack frame.
319 __ pushl(Address(EBP, variable.index() * kWordSize));
320 }
321 }
322
323
324 void CodeGenerator::GenerateInstanceCall(
325 intptr_t node_id,
326 intptr_t token_pos,
327 const String& function_name,
328 int num_arguments,
329 const Array& optional_arguments_names,
330 intptr_t num_args_checked) {
331 if (FLAG_print_ic_in_optimized && IsOptimizing()) {
332 OS::Print("Generate IC in optimized code: id %d name: '%s'\n",
333 node_id, function_name.ToCString());
334 }
335 ASSERT(num_args_checked > 0); // At least receiver check is necessary.
336 // Set up the function name and number of arguments (including the receiver)
337 // to the InstanceCall stub which will resolve the correct entrypoint for
338 // the operator and call it.
339 ICData& ic_data = ICData::ZoneHandle();
340 ic_data = ICData::New(parsed_function().function(),
341 function_name,
342 node_id,
343 num_args_checked);
344 __ LoadObject(ECX, ic_data);
345 __ LoadObject(EDX, ArgumentsDescriptor(num_arguments,
346 optional_arguments_names));
347 uword label_address = 0;
348 switch (num_args_checked) {
349 case 1:
350 label_address = StubCode::OneArgCheckInlineCacheEntryPoint();
351 break;
352 case 2:
353 label_address = StubCode::TwoArgsCheckInlineCacheEntryPoint();
354 break;
355 default:
356 UNIMPLEMENTED();
357 }
358 ExternalLabel target_label("InlineCache", label_address);
359
360 __ call(&target_label);
361 AddCurrentDescriptor(PcDescriptors::kIcCall,
362 node_id,
363 token_pos);
364 __ addl(ESP, Immediate(num_arguments * kWordSize));
365 }
366
367
368 // Input parameters:
369 // ESP : points to return address.
370 // ESP + 4 : address of last argument (arg n-1).
371 // ESP + 4*n : address of first argument (arg 0).
372 // EDX : arguments descriptor array.
373 void CodeGenerator::GenerateEntryCode() {
374 const Immediate raw_null =
375 Immediate(reinterpret_cast<intptr_t>(Object::null()));
376 const Function& function = parsed_function_.function();
377 const bool is_instance_native_closure =
378 function.is_native() && function.IsImplicitInstanceClosureFunction();
379
380 // 1. Compute the frame size and enter the frame (reserving local space
381 // for copied incoming and default arguments and stack-allocated local
382 // variables).
383 //
384 // TODO(regis): We may give up reserving space on stack for args/locals
385 // because pushes of initial values may be more effective than moves.
386 LocalScope* scope = parsed_function_.node_sequence()->scope();
387 const int num_fixed_params = function.num_fixed_parameters();
388 const int num_opt_params = function.num_optional_parameters();
389 const int num_copied_params = parsed_function_.copied_parameter_count();
390 const int stack_slot_count =
391 num_copied_params + parsed_function_.stack_local_count();
392 set_locals_space_size(stack_slot_count * kWordSize);
393 AssemblerMacros::EnterDartFrame(assembler_, locals_space_size());
394
395 // 2. Optionally check if the number of arguments matches. We check the
396 // number of passed arguments when we have to copy them due to the
397 // presence of optional named parameters. No such checking code is
398 // generated if only fixed parameters are declared, unless we are in debug
399 // mode or unless we are compiling a closure.
400 if (num_copied_params == 0) {
401 ASSERT(num_opt_params == 0);
402 #if defined(DEBUG)
403 const bool check_arguments = true; // Always check arguments in debug mode.
404 #else
405 // The number of arguments passed to closure functions must always be
406 // checked here, because no resolving stub (normally responsible for the
407 // check) is involved in closure calls.
408 const bool check_arguments = function.IsClosureFunction();
409 #endif
410 if (check_arguments) {
411 // Check that num_fixed <= argc <= num_params.
412 Label argc_in_range;
413 // Total number of args is the first Smi in args descriptor array (EDX).
414 __ movl(EAX, FieldAddress(EDX, Array::data_offset()));
415 __ cmpl(EAX, Immediate(Smi::RawValue(num_fixed_params)));
416 __ j(EQUAL, &argc_in_range, Assembler::kNearJump);
417 if (function.IsClosureFunction()) {
418 GenerateCallRuntime(AstNode::kNoId,
419 0,
420 kClosureArgumentMismatchRuntimeEntry);
421 } else {
422 __ Stop("Wrong number of arguments");
423 }
424 __ Bind(&argc_in_range);
425 }
426 } else {
427 int implicit_this_param_pos = is_instance_native_closure ? -1 : 0;
428 ASSERT(parsed_function_.first_parameter_index() ==
429 (ParsedFunction::kFirstLocalSlotIndex + implicit_this_param_pos));
430 // Copy positional arguments.
431 // Check that no fewer than num_fixed_params positional arguments are passed
432 // in and that no more than num_params arguments are passed in.
433 // Passed argument i at fp[1 + argc - i] copied to
434 // fp[ParsedFunction::kFirstLocalSlotIndex - i].
435 const int num_params = num_fixed_params + num_opt_params;
436
437 // Total number of args is the first Smi in args descriptor array (EDX).
438 __ movl(EBX, FieldAddress(EDX, Array::data_offset()));
439 // Check that num_args <= num_params.
440 Label wrong_num_arguments;
441 __ cmpl(EBX, Immediate(Smi::RawValue(num_params)));
442 __ j(GREATER, &wrong_num_arguments);
443 // Number of positional args is the second Smi in descriptor array (EDX).
444 __ movl(ECX, FieldAddress(EDX, Array::data_offset() + (1 * kWordSize)));
445 // Check that num_pos_args >= num_fixed_params.
446 __ cmpl(ECX, Immediate(Smi::RawValue(num_fixed_params)));
447 __ j(LESS, &wrong_num_arguments);
448
449 // Since EBX and ECX are Smi, use TIMES_2 instead of TIMES_4.
450 // Let EBX point to the last passed positional argument, i.e. to
451 // fp[1 + num_args - (num_pos_args - 1)].
452 __ subl(EBX, ECX);
453 __ leal(EBX, Address(EBP, EBX, TIMES_2, 2 * kWordSize));
454 // Let EDI point to the last copied positional argument, i.e. to
455 // fp[ParsedFunction::kFirstLocalSlotIndex - (num_pos_args - 1)].
456 const int index =
457 ParsedFunction::kFirstLocalSlotIndex + 1 + implicit_this_param_pos;
458 // First copy captured receiver if function is an implicit native closure.
459 if (is_instance_native_closure) {
460 __ movl(EAX, FieldAddress(CTX, Context::variable_offset(0)));
461 __ movl(Address(EBP, (index * kWordSize)), EAX);
462 }
463 __ leal(EDI, Address(EBP, (index * kWordSize)));
464 __ subl(EDI, ECX); // ECX is a Smi, subtract twice for TIMES_4 scaling.
465 __ subl(EDI, ECX);
466 __ SmiUntag(ECX);
467 Label loop, loop_condition;
468 __ jmp(&loop_condition, Assembler::kNearJump);
469 // We do not use the final allocation index of the variable here, i.e.
470 // scope->VariableAt(i)->index(), because captured variables still need
471 // to be copied to the context that is not yet allocated.
472 const Address argument_addr(EBX, ECX, TIMES_4, 0);
473 const Address copy_addr(EDI, ECX, TIMES_4, 0);
474 __ Bind(&loop);
475 __ movl(EAX, argument_addr);
476 __ movl(copy_addr, EAX);
477 __ Bind(&loop_condition);
478 __ decl(ECX);
479 __ j(POSITIVE, &loop, Assembler::kNearJump);
480
481 // Copy or initialize optional named arguments.
482 Label all_arguments_processed;
483 if (num_opt_params > 0) {
484 // Start by alphabetically sorting the names of the optional parameters.
485 LocalVariable** opt_param = new LocalVariable*[num_opt_params];
486 int* opt_param_position = new int[num_opt_params];
487 for (int pos = num_fixed_params; pos < num_params; pos++) {
488 LocalVariable* parameter = scope->VariableAt(pos);
489 const String& opt_param_name = parameter->name();
490 int i = pos - num_fixed_params;
491 while (--i >= 0) {
492 LocalVariable* param_i = opt_param[i];
493 const intptr_t result = opt_param_name.CompareTo(param_i->name());
494 ASSERT(result != 0);
495 if (result > 0) break;
496 opt_param[i + 1] = opt_param[i];
497 opt_param_position[i + 1] = opt_param_position[i];
498 }
499 opt_param[i + 1] = parameter;
500 opt_param_position[i + 1] = pos;
501 }
502 // Generate code handling each optional parameter in alphabetical order.
503 // Total number of args is the first Smi in args descriptor array (EDX).
504 __ movl(EBX, FieldAddress(EDX, Array::data_offset()));
505 // Number of positional args is the second Smi in descriptor array (EDX).
506 __ movl(ECX, FieldAddress(EDX, Array::data_offset() + (1 * kWordSize)));
507 __ SmiUntag(ECX);
508 // Let EBX point to the first passed argument, i.e. to fp[1 + argc - 0].
509 __ leal(EBX, Address(EBP, EBX, TIMES_2, kWordSize));
510 // Let EDI point to the name/pos pair of the first named argument.
511 __ leal(EDI, FieldAddress(EDX, Array::data_offset() + (2 * kWordSize)));
512 for (int i = 0; i < num_opt_params; i++) {
513 // Handle this optional parameter only if k or fewer positional args
514 // have been passed, where k is the position of this optional parameter
515 // in the formal parameter list.
516 Label load_default_value, assign_optional_parameter, next_parameter;
517 const int param_pos = opt_param_position[i];
518 __ cmpl(ECX, Immediate(param_pos));
519 __ j(GREATER, &next_parameter, Assembler::kNearJump);
520 // Check if this named parameter was passed in.
521 __ movl(EAX, Address(EDI, 0)); // Load EAX with the name of the arg.
522 __ CompareObject(EAX, opt_param[i]->name());
523 __ j(NOT_EQUAL, &load_default_value, Assembler::kNearJump);
524 // Load EAX with passed-in argument at provided arg_pos, i.e. at
525 // fp[1 + argc - arg_pos].
526 __ movl(EAX, Address(EDI, kWordSize)); // EAX is arg_pos as Smi.
527 __ addl(EDI, Immediate(2 * kWordSize)); // Point to next name/pos pair.
528 __ negl(EAX);
529 Address argument_addr(EBX, EAX, TIMES_2, 0); // EAX is a negative Smi.
530 __ movl(EAX, argument_addr);
531 __ jmp(&assign_optional_parameter, Assembler::kNearJump);
532 __ Bind(&load_default_value);
533 // Load EAX with default argument at pos.
534 const Object& value = Object::ZoneHandle(
535 parsed_function_.default_parameter_values().At(
536 param_pos - num_fixed_params));
537 __ LoadObject(EAX, value);
538 __ Bind(&assign_optional_parameter);
539 // Assign EAX to fp[ParsedFunction::kFirstLocalSlotIndex - param_pos].
540 // We do not use the final allocation index of the variable here, i.e.
541 // scope->VariableAt(i)->index(), because captured variables still need
542 // to be copied to the context that is not yet allocated.
543 intptr_t computed_param_pos = (ParsedFunction::kFirstLocalSlotIndex -
544 param_pos + implicit_this_param_pos);
545 const Address param_addr(EBP, (computed_param_pos * kWordSize));
546 __ movl(param_addr, EAX);
547 __ Bind(&next_parameter);
548 }
549 delete[] opt_param;
550 delete[] opt_param_position;
551 // Check that EDI now points to null terminator in the array descriptor.
552 __ cmpl(Address(EDI, 0), raw_null);
553 __ j(EQUAL, &all_arguments_processed, Assembler::kNearJump);
554 } else {
555 ASSERT(is_instance_native_closure);
556 __ jmp(&all_arguments_processed, Assembler::kNearJump);
557 }
558
559 __ Bind(&wrong_num_arguments);
560 if (locals_space_size() != 0) {
561 // We need to unwind the space we reserved for locals and copied
562 // parameters. The NoSuchMethodFunction stub does not expect to
563 // see that area on the stack.
564 __ addl(ESP, Immediate(locals_space_size()));
565 }
566 if (function.IsClosureFunction()) {
567 GenerateCallRuntime(AstNode::kNoId,
568 0,
569 kClosureArgumentMismatchRuntimeEntry);
570 } else {
571 // Invoke noSuchMethod function.
572 const int kNumArgsChecked = 1;
573 ICData& ic_data = ICData::ZoneHandle();
574 ic_data = ICData::New(parsed_function().function(),
575 String::Handle(function.name()),
576 AstNode::kNoId,
577 kNumArgsChecked);
578 __ LoadObject(ECX, ic_data);
579 // EBP - 4 : PC marker, allows easy identification of RawInstruction obj.
580 // EBP : points to previous frame pointer.
581 // EBP + 4 : points to return address.
582 // EBP + 8 : address of last argument (arg n-1).
583 // ESP + 8 + 4*(n-1) : address of first argument (arg 0).
584 // ECX : ic-data.
585 // EDX : arguments descriptor array.
586 __ call(&StubCode::CallNoSuchMethodFunctionLabel());
587 }
588
589 if (FLAG_trace_functions) {
590 __ pushl(EAX); // Preserve result.
591 __ PushObject(Function::ZoneHandle(function.raw()));
592 GenerateCallRuntime(AstNode::kNoId,
593 0,
594 kTraceFunctionExitRuntimeEntry);
595 __ popl(EAX); // Remove argument.
596 __ popl(EAX); // Restore result.
597 }
598 __ LeaveFrame();
599 __ ret();
600
601 __ Bind(&all_arguments_processed);
602 // Nullify originally passed arguments only after they have been copied and
603 // checked, otherwise noSuchMethod would not see their original values.
604 // This step can be skipped in case we decide that formal parameters are
605 // implicitly final, since garbage collecting the unmodified value is not
606 // an issue anymore.
607
608 // EDX : arguments descriptor array.
609 // Total number of args is the first Smi in args descriptor array (EDX).
610 __ movl(ECX, FieldAddress(EDX, Array::data_offset()));
611 __ SmiUntag(ECX);
612 Label null_args_loop, null_args_loop_condition;
613 __ jmp(&null_args_loop_condition, Assembler::kNearJump);
614 const Address original_argument_addr(EBP, ECX, TIMES_4, 2 * kWordSize);
615 __ Bind(&null_args_loop);
616 __ movl(original_argument_addr, raw_null);
617 __ Bind(&null_args_loop_condition);
618 __ decl(ECX);
619 __ j(POSITIVE, &null_args_loop, Assembler::kNearJump);
620 }
621
622 // 3. Initialize (non-argument) stack-allocated locals to null.
623 //
624 // TODO(regis): For now, always unroll the init loop. Decide later above
625 // which threshold to implement a loop. Consider emitting pushes instead
626 // of moves.
627 const int base = parsed_function_.first_stack_local_index();
628 for (int index = 0; index < parsed_function_.stack_local_count(); ++index) {
629 if (index == 0) {
630 __ movl(EAX, raw_null);
631 }
632 __ movl(Address(EBP, (base - index) * kWordSize), EAX);
633 }
634
635 // 4. Generate the stack overflow check.
636 __ cmpl(ESP,
637 Address::Absolute(Isolate::Current()->stack_limit_address()));
638 Label no_stack_overflow;
639 __ j(ABOVE, &no_stack_overflow);
640 GenerateCallRuntime(AstNode::kNoId,
641 0,
642 kStackOverflowRuntimeEntry);
643 __ Bind(&no_stack_overflow);
644 }
645
646
647 void CodeGenerator::GenerateReturnEpilog(ReturnNode* node) {
648 // Unchain the context(s) up to context level 0.
649 intptr_t current_context_level = context_level();
650 ASSERT(current_context_level >= 0);
651 if (parsed_function_.saved_context_var() != NULL) {
652 // CTX on entry was saved, but not linked as context parent.
653 GenerateLoadVariable(CTX, *parsed_function_.saved_context_var());
654 } else {
655 while (current_context_level-- > 0) {
656 __ movl(CTX, FieldAddress(CTX, Context::parent_offset()));
657 }
658 }
659 #ifdef DEBUG
660 // Check that the entry stack size matches the exit stack size.
661 __ leal(EDX, Address(EBP, kLocalsOffsetFromFP));
662 __ subl(EDX, ESP);
663 ASSERT(locals_space_size() >= 0);
664 __ cmpl(EDX, Immediate(locals_space_size()));
665 Label wrong_stack;
666 __ j(NOT_EQUAL, &wrong_stack, Assembler::kNearJump);
667 #endif // DEBUG.
668
669 if (!IsOptimizing()) {
670 // Count only in unoptimized code.
671 // TODO(srdjan): Replace the counting code with a type feedback
672 // collection and counting stub.
673 const Function& function =
674 Function::ZoneHandle(parsed_function_.function().raw());
675 __ LoadObject(EBX, function);
676 __ incl(FieldAddress(EBX, Function::usage_counter_offset()));
677 if (CodeGenerator::CanOptimize()) {
678 // Do not optimize if usage count must be reported.
679 __ cmpl(FieldAddress(EBX, Function::usage_counter_offset()),
680 Immediate(FLAG_optimization_counter_threshold));
681 Label not_yet_hot;
682 __ j(LESS_EQUAL, &not_yet_hot, Assembler::kNearJump);
683 __ pushl(EAX); // Preserve result.
684 __ pushl(EBX); // Argument for runtime: function to optimize.
685 __ CallRuntime(kOptimizeInvokedFunctionRuntimeEntry);
686 __ popl(EBX); // Remove argument.
687 __ popl(EAX); // Restore result.
688 __ Bind(&not_yet_hot);
689 }
690 }
691 if (FLAG_trace_functions) {
692 const Function& function =
693 Function::ZoneHandle(parsed_function_.function().raw());
694 __ LoadObject(EBX, function);
695 __ pushl(EAX); // Preserve result.
696 __ pushl(EBX);
697 GenerateCallRuntime(AstNode::kNoId,
698 0,
699 kTraceFunctionExitRuntimeEntry);
700 __ popl(EAX); // Remove argument.
701 __ popl(EAX); // Restore result.
702 }
703 __ LeaveFrame();
704 __ ret();
705 // Add a NOP to make return code pattern 5 bytes long for patching
706 // in breakpoints during debugging.
707 __ nop(1);
708 AddCurrentDescriptor(PcDescriptors::kReturn,
709 node->id(),
710 node->token_pos());
711
712 #ifdef DEBUG
713 __ Bind(&wrong_stack);
714 __ Stop("Exit stack size does not match the entry stack size.");
715 #endif // DEBUG.
716 }
717
718
719 void CodeGenerator::VisitReturnNode(ReturnNode* node) {
720 ASSERT(!IsResultNeeded(node));
721 ASSERT(node->value() != NULL);
722
723 if (!node->value()->IsLiteralNode()) {
724 node->value()->Visit(this);
725 // The result of the return value is now on top of the stack.
726 }
727
728 // Generate inlined code for all finally blocks as we are about to transfer
729 // control out of the 'try' blocks if any.
730 for (intptr_t i = 0; i < node->inlined_finally_list_length(); i++) {
731 node->InlinedFinallyNodeAt(i)->Visit(this);
732 }
733
734 if (node->value()->IsLiteralNode()) {
735 // Load literal value into EAX.
736 const Object& literal = node->value()->AsLiteralNode()->literal();
737 if (literal.IsSmi()) {
738 __ movl(EAX, Immediate(reinterpret_cast<int32_t>(literal.raw())));
739 } else {
740 __ LoadObject(EAX, literal);
741 }
742 } else {
743 // Pop the previously evaluated result value into EAX.
744 __ popl(EAX);
745 }
746
747 // Generate type check.
748 if (FLAG_enable_type_checks) {
749 const RawFunction::Kind kind = parsed_function().function().kind();
750 const bool is_implicit_getter =
751 (kind == RawFunction::kImplicitGetter) ||
752 (kind == RawFunction::kConstImplicitGetter);
753 const bool is_static = parsed_function().function().is_static();
754 // Implicit getters do not need a type check at return, unless they compute
755 // the initial value of a static field.
756 if (is_static || !is_implicit_getter) {
757 GenerateAssertAssignable(
758 node->id(),
759 node->value()->token_pos(),
760 node->value(),
761 AbstractType::ZoneHandle(parsed_function().function().result_type()),
762 String::ZoneHandle(String::NewSymbol("function result")));
763 }
764 }
765 GenerateReturnEpilog(node);
766 }
767
768
769 void CodeGenerator::VisitLiteralNode(LiteralNode* node) {
770 if (!IsResultNeeded(node)) return;
771 __ PushObject(node->literal());
772 }
773
774
775 void CodeGenerator::VisitTypeNode(TypeNode* node) {
776 // Type nodes are handled specially by the code generator.
777 UNREACHABLE();
778 }
779
780
781 void CodeGenerator::VisitAssignableNode(AssignableNode* node) {
782 ASSERT(FLAG_enable_type_checks);
783 node->expr()->Visit(this);
784 __ popl(EAX);
785 GenerateAssertAssignable(node->id(),
786 node->token_pos(),
787 node->expr(),
788 node->type(),
789 node->dst_name());
790 if (IsResultNeeded(node)) {
791 __ pushl(EAX);
792 }
793 }
794
795
796 void CodeGenerator::VisitClosureNode(ClosureNode* node) {
797 const Immediate raw_null =
798 Immediate(reinterpret_cast<intptr_t>(Object::null()));
799 const Function& function = node->function();
800 if (function.IsNonImplicitClosureFunction()) {
801 // The context scope may have already been set by the new non-optimizing
802 // compiler. If it was not, set it here.
803 if (function.context_scope() == ContextScope::null()) {
804 const intptr_t current_context_level = context_level();
805 const ContextScope& context_scope = ContextScope::ZoneHandle(
806 node->scope()->PreserveOuterScope(current_context_level));
807 ASSERT(!function.HasCode());
808 function.set_context_scope(context_scope);
809 }
810 __ pushl(raw_null); // No receiver.
811 } else if (function.IsImplicitInstanceClosureFunction()) {
812 node->receiver()->Visit(this);
813 } else {
814 __ pushl(raw_null); // No receiver.
815 }
816 ASSERT(function.context_scope() != ContextScope::null());
817
818 // The function type of a closure may have type arguments. In that case, pass
819 // the type arguments of the instantiator.
820 const Class& cls = Class::Handle(function.signature_class());
821 ASSERT(!cls.IsNull());
822 const bool requires_type_arguments = cls.HasTypeArguments();
823 if (requires_type_arguments) {
824 ASSERT(!function.IsImplicitStaticClosureFunction());
825 const bool kPushInstantiator = false;
826 GenerateInstantiatorTypeArguments(node->token_pos(), kPushInstantiator);
827 } else {
828 __ pushl(raw_null); // No type arguments.
829 }
830 const Code& stub = Code::Handle(
831 StubCode::GetAllocationStubForClosure(function));
832 const ExternalLabel label(function.ToCString(), stub.EntryPoint());
833 GenerateCall(node->token_pos(), &label, PcDescriptors::kOther);
834 __ popl(ECX); // Pop type arguments.
835 __ popl(ECX); // Pop receiver.
836 if (IsResultNeeded(node)) {
837 __ pushl(EAX);
838 }
839 }
840
841
842 void CodeGenerator::VisitPrimaryNode(PrimaryNode* node) {
843 // PrimaryNodes are temporary during parsing.
844 UNREACHABLE();
845 }
846
847
848 void CodeGenerator::VisitCloneContextNode(CloneContextNode *node) {
849 __ PushObject(Object::ZoneHandle()); // Make room for the result.
850 __ pushl(CTX);
851 GenerateCallRuntime(node->id(),
852 node->token_pos(), kCloneContextRuntimeEntry);
853 __ popl(EAX);
854 __ popl(CTX); // result: cloned context. Set as current context.
855 }
856
857
858 void CodeGenerator::VisitSequenceNode(SequenceNode* node_sequence) {
859 CodeGeneratorState codegen_state(this);
860 LocalScope* scope = node_sequence->scope();
861 const intptr_t num_context_variables =
862 (scope != NULL) ? scope->num_context_variables() : 0;
863 intptr_t previous_context_level = context_level();
864 if (num_context_variables > 0) {
865 // The loop local scope declares variables that are captured.
866 // Allocate and chain a new context.
867 __ movl(EDX, Immediate(num_context_variables));
868 const ExternalLabel label("alloc_context",
869 StubCode::AllocateContextEntryPoint());
870 GenerateCall(node_sequence->token_pos(), &label, PcDescriptors::kOther);
871
872 // If this node_sequence is the body of the function being compiled, and if
873 // this function is not a closure, do not link the current context as the
874 // parent of the newly allocated context, as it is not accessible. Instead,
875 // save it in a pre-allocated variable and restore it on exit.
876 if ((node_sequence == parsed_function_.node_sequence()) &&
877 (parsed_function_.saved_context_var() != NULL)) {
878 GenerateStoreVariable(
879 *parsed_function_.saved_context_var(), CTX, kNoRegister);
880 __ StoreIntoObjectNoBarrier(EAX,
881 FieldAddress(EAX, Context::parent_offset()),
882 Object::ZoneHandle());
883 } else {
884 // Chain the new context in EAX to its parent in CTX.
885 __ StoreIntoObject(EAX, FieldAddress(EAX, Context::parent_offset()), CTX);
886 }
887
888 // Set new context as current context.
889 __ movl(CTX, EAX);
890 set_context_level(scope->context_level());
891
892 // If this node_sequence is the body of the function being compiled, copy
893 // the captured parameters from the frame into the context.
894 if (node_sequence == parsed_function_.node_sequence()) {
895 ASSERT(scope->context_level() == 1);
896 const Immediate raw_null =
897 Immediate(reinterpret_cast<intptr_t>(Object::null()));
898 const Function& function = parsed_function_.function();
899 const int num_params = function.NumberOfParameters();
900 int param_frame_index = (num_params == function.num_fixed_parameters()) ?
901 (1 + num_params) : ParsedFunction::kFirstLocalSlotIndex;
902 for (int pos = 0; pos < num_params; param_frame_index--, pos++) {
903 LocalVariable* parameter = scope->VariableAt(pos);
904 ASSERT(parameter->owner() == scope);
905 if (parameter->is_captured()) {
906 // Copy parameter from local frame to current context.
907 const Address local_addr(EBP, param_frame_index * kWordSize);
908 __ movl(EAX, local_addr);
909 GenerateStoreVariable(*parameter, EAX, EDX);
910 // Write NULL to the source location to detect buggy accesses and
911 // allow GC of passed value if it gets overwritten by a new value in
912 // the function.
913 __ movl(local_addr, raw_null);
914 }
915 }
916 }
917 }
918 // If this node_sequence is the body of the function being compiled, generate
919 // code checking the type of the actual arguments.
920 if (FLAG_enable_type_checks &&
921 (node_sequence == parsed_function_.node_sequence())) {
922 GenerateArgumentTypeChecks();
923 }
924 for (int i = 0; i < node_sequence->length(); i++) {
925 AstNode* child_node = node_sequence->NodeAt(i);
926 state()->set_root_node(child_node);
927 child_node->Visit(this);
928 }
929
930 // Unchain the previously allocated context.
931 if ((node_sequence == parsed_function_.node_sequence()) &&
932 (parsed_function_.saved_context_var() != NULL)) {
933 ASSERT(num_context_variables > 0);
934 GenerateLoadVariable(CTX, *parsed_function_.saved_context_var());
935 } else if (num_context_variables > 0) {
936 __ movl(CTX, FieldAddress(CTX, Context::parent_offset()));
937 }
938
939 // If this node sequence is labeled, a break out of the sequence will have
940 // taken care of unchaining the context.
941 if (node_sequence->label() != NULL) {
942 __ Bind(node_sequence->label()->break_label());
943 // Outermost sequence cannot have a label.
944 ASSERT(node_sequence != parsed_function_.node_sequence());
945 }
946 set_context_level(previous_context_level);
947 }
948
949
950 void CodeGenerator::VisitArgumentListNode(ArgumentListNode* arguments) {
951 for (int i = 0; i < arguments->length(); i++) {
952 AstNode* argument = arguments->NodeAt(i);
953 argument->Visit(this);
954 }
955 }
956
957
958 void CodeGenerator::VisitArrayNode(ArrayNode* node) {
959 // Evaluate the array elements.
960 for (int i = 0; i < node->length(); i++) {
961 AstNode* element = node->ElementAt(i);
962 element->Visit(this);
963 }
964
965 const AbstractTypeArguments& element_type = node->type_arguments();
966 const bool instantiate_type_arguments = true;
967 GenerateTypeArguments(node->id(),
968 node->token_pos(),
969 element_type,
970 instantiate_type_arguments);
971 __ popl(ECX);
972 __ movl(EDX, Immediate(Smi::RawValue(node->length())));
973
974 // Allocate the array.
975 // EDX : Array length as Smi.
976 // ECX : element type for the array.
977 GenerateCall(node->token_pos(),
978 &StubCode::AllocateArrayLabel(),
979 PcDescriptors::kOther);
980
981 // Pop the element values from the stack into the array.
982 __ leal(ECX, FieldAddress(EAX, Array::data_offset()));
983 for (int i = node->length() - 1; i >= 0; i--) {
984 __ popl(Address(ECX, i * kWordSize));
985 }
986
987 if (IsResultNeeded(node)) {
988 __ pushl(EAX);
989 }
990 }
991
992
993 void CodeGenerator::VisitLoadLocalNode(LoadLocalNode* node) {
994 if (node->HasPseudo()) {
995 node->pseudo()->Visit(this);
996 __ popl(EAX); // Discard result.
997 }
998 // Load the value of the local variable and push it onto the expression stack.
999 if (IsResultNeeded(node)) {
1000 GeneratePushVariable(node->local(), EAX);
1001 }
1002 }
1003
1004
1005 void CodeGenerator::VisitStoreLocalNode(StoreLocalNode* node) {
1006 node->value()->Visit(this);
1007 __ popl(EAX);
1008 if (FLAG_enable_type_checks) {
1009 GenerateAssertAssignable(node->id(),
1010 node->value()->token_pos(),
1011 node->value(),
1012 node->local().type(),
1013 node->local().name());
1014 }
1015 GenerateStoreVariable(node->local(), EAX, EDX);
1016 if (IsResultNeeded(node)) {
1017 __ pushl(EAX);
1018 }
1019 }
1020
1021
1022 void CodeGenerator::VisitLoadInstanceFieldNode(LoadInstanceFieldNode* node) {
1023 node->instance()->Visit(this);
1024 MarkDeoptPoint(node->id(), node->token_pos());
1025 __ popl(EAX); // Instance.
1026 __ movl(EAX, FieldAddress(EAX, node->field().Offset()));
1027 if (IsResultNeeded(node)) {
1028 __ pushl(EAX);
1029 }
1030 }
1031
1032
1033 void CodeGenerator::VisitStoreInstanceFieldNode(StoreInstanceFieldNode* node) {
1034 node->instance()->Visit(this);
1035 node->value()->Visit(this);
1036 MarkDeoptPoint(node->id(), node->token_pos());
1037 __ popl(EAX); // Value.
1038 if (FLAG_enable_type_checks) {
1039 GenerateAssertAssignable(node->id(),
1040 node->value()->token_pos(),
1041 node->value(),
1042 AbstractType::ZoneHandle(node->field().type()),
1043 String::ZoneHandle(node->field().name()));
1044 }
1045 __ popl(EDX); // Instance.
1046 __ StoreIntoObject(EDX, FieldAddress(EDX, node->field().Offset()), EAX);
1047 ASSERT(!IsResultNeeded(node));
1048 }
1049
1050
1051 // Expects array and index on stack and returns result in EAX.
1052 void CodeGenerator::GenerateLoadIndexed(intptr_t node_id,
1053 intptr_t token_pos) {
1054 // Invoke the [] operator on the receiver object with the index as argument.
1055 const String& operator_name =
1056 String::ZoneHandle(String::NewSymbol(Token::Str(Token::kINDEX)));
1057 const int kNumArguments = 2; // Receiver and index.
1058 const Array& kNoArgumentNames = Array::Handle();
1059 const int kNumArgumentsChecked = 1;
1060 GenerateInstanceCall(node_id,
1061 token_pos,
1062 operator_name,
1063 kNumArguments,
1064 kNoArgumentNames,
1065 kNumArgumentsChecked);
1066 }
1067
1068
1069 void CodeGenerator::VisitLoadIndexedNode(LoadIndexedNode* node) {
1070 node->array()->Visit(this);
1071 // Now compute the index.
1072 node->index_expr()->Visit(this);
1073 MarkDeoptPoint(node->id(), node->token_pos());
1074 GenerateLoadIndexed(node->id(), node->token_pos());
1075 // Result is in EAX.
1076 if (IsResultNeeded(node)) {
1077 __ pushl(EAX);
1078 }
1079 }
1080
1081
1082 // Expected arguments.
1083 // TOS(0): value.
1084 // TOS(1): index.
1085 // TOS(2): array.
1086 void CodeGenerator::GenerateStoreIndexed(intptr_t node_id,
1087 intptr_t token_pos,
1088 bool preserve_value) {
1089 // It is not necessary to generate a type test of the assigned value here,
1090 // because the []= operator will check the type of its incoming arguments.
1091 if (preserve_value) {
1092 __ popl(EAX);
1093 __ popl(EDX);
1094 __ popl(ECX);
1095 __ pushl(EAX); // Preserve stored value.
1096 __ pushl(ECX); // Restore arguments.
1097 __ pushl(EDX);
1098 __ pushl(EAX);
1099 }
1100 // Invoke the []= operator on the receiver object with index and
1101 // value as arguments.
1102 const String& operator_name =
1103 String::ZoneHandle(String::NewSymbol(Token::Str(Token::kASSIGN_INDEX)));
1104 const int kNumArguments = 3; // Receiver, index and value.
1105 const Array& kNoArgumentNames = Array::Handle();
1106 const int kNumArgumentsChecked = 1;
1107 GenerateInstanceCall(node_id,
1108 token_pos,
1109 operator_name,
1110 kNumArguments,
1111 kNoArgumentNames,
1112 kNumArgumentsChecked);
1113 }
1114
1115
1116 void CodeGenerator::VisitStoreIndexedNode(StoreIndexedNode* node) {
1117 // Compute the receiver object and pass as first argument to call.
1118 node->array()->Visit(this);
1119 // Now compute the index.
1120 node->index_expr()->Visit(this);
1121 // Finally compute the value to assign.
1122 node->value()->Visit(this);
1123 MarkDeoptPoint(node->id(), node->token_pos());
1124 GenerateStoreIndexed(node->id(), node->token_pos(), IsResultNeeded(node));
1125 }
1126
1127
1128 void CodeGenerator::VisitLoadStaticFieldNode(LoadStaticFieldNode* node) {
1129 MarkDeoptPoint(node->id(), node->token_pos());
1130 __ LoadObject(EDX, node->field());
1131 __ movl(EAX, FieldAddress(EDX, Field::value_offset()));
1132 if (IsResultNeeded(node)) {
1133 __ pushl(EAX);
1134 }
1135 }
1136
1137
1138 void CodeGenerator::VisitStoreStaticFieldNode(StoreStaticFieldNode* node) {
1139 node->value()->Visit(this);
1140 MarkDeoptPoint(node->id(), node->token_pos());
1141 __ popl(EAX); // Value.
1142 if (FLAG_enable_type_checks) {
1143 GenerateAssertAssignable(node->id(),
1144 node->value()->token_pos(),
1145 node->value(),
1146 AbstractType::ZoneHandle(node->field().type()),
1147 String::ZoneHandle(node->field().name()));
1148 }
1149 __ LoadObject(EDX, node->field());
1150 __ StoreIntoObject(EDX, FieldAddress(EDX, Field::value_offset()), EAX);
1151 if (IsResultNeeded(node)) {
1152 // The result is the input value.
1153 __ pushl(EAX);
1154 }
1155 }
1156
1157
1158 void CodeGenerator::GenerateLogicalNotOp(UnaryOpNode* node) {
1159 // Generate false if operand is true, otherwise generate true.
1160 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
1161 const Bool& bool_false = Bool::ZoneHandle(Bool::False());
1162 node->operand()->Visit(this);
1163 MarkDeoptPoint(node->id(), node->token_pos());
1164 Label done;
1165 GenerateConditionTypeCheck(node->id(), node->operand()->token_pos());
1166 __ popl(EDX);
1167 __ LoadObject(EAX, bool_true);
1168 __ cmpl(EAX, EDX);
1169 __ j(NOT_EQUAL, &done, Assembler::kNearJump);
1170 __ LoadObject(EAX, bool_false);
1171 __ Bind(&done);
1172 if (IsResultNeeded(node)) {
1173 __ pushl(EAX);
1174 }
1175 }
1176
1177
1178 void CodeGenerator::VisitUnaryOpNode(UnaryOpNode* node) {
1179 if (node->kind() == Token::kNOT) {
1180 // "!" cannot be overloaded, therefore inline it.
1181 GenerateLogicalNotOp(node);
1182 return;
1183 }
1184 node->operand()->Visit(this);
1185 if (node->kind() == Token::kADD) {
1186 // TODO(srdjan): Remove this as it is not part of Dart language any longer.
1187 // Unary operator '+' does not exist, it's a NOP, skip it.
1188 if (!IsResultNeeded(node)) {
1189 __ popl(EAX);
1190 }
1191 return;
1192 }
1193 MarkDeoptPoint(node->id(), node->token_pos());
1194 String& operator_name = String::ZoneHandle();
1195 if (node->kind() == Token::kSUB) {
1196 operator_name = String::NewSymbol(Token::Str(Token::kNEGATE));
1197 } else {
1198 operator_name = String::NewSymbol(node->Name());
1199 }
1200 const int kNumberOfArguments = 1;
1201 const Array& kNoArgumentNames = Array::Handle();
1202 const int kNumArgumentsChecked = 1;
1203 GenerateInstanceCall(node->id(),
1204 node->token_pos(),
1205 operator_name,
1206 kNumberOfArguments,
1207 kNoArgumentNames,
1208 kNumArgumentsChecked);
1209 if (IsResultNeeded(node)) {
1210 __ pushl(EAX);
1211 }
1212 }
1213
1214
1215 // Instance to test is in EAX. Test if its class is in subtype test cache array
1216 // and use the result in the array to jump to one of the labels. Fall through
1217 // if the instance is not in the cache array.
1218 // TODO(srdjan): Implement a quicker subtype check, as type test
1219 // arrays can grow too high, but they may be useful when optimizing
1220 // code (type-feedback).
1221 RawSubtypeTestCache* CodeGenerator::GenerateSubtype1TestCacheLookup(
1222 intptr_t node_id,
1223 intptr_t token_pos,
1224 const Class& type_class,
1225 Label* is_instance_lbl,
1226 Label* is_not_instance_lbl) {
1227 const SubtypeTestCache& type_test_cache =
1228 SubtypeTestCache::ZoneHandle(SubtypeTestCache::New());
1229 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
1230 const Immediate raw_null =
1231 Immediate(reinterpret_cast<intptr_t>(Object::null()));
1232 // Check immediate equality.
1233 __ LoadClass(ECX, EAX, EDI);
1234 // ECX: instance class.
1235 __ CompareObject(ECX, type_class);
1236 __ j(EQUAL, is_instance_lbl);
1237
1238 // Check immediate superclass equality.
1239 __ movl(EDI, FieldAddress(ECX, Class::super_type_offset()));
1240 __ movl(EDI, FieldAddress(EDI, Type::type_class_offset()));
1241 __ CompareObject(EDI, type_class);
1242 __ j(EQUAL, is_instance_lbl);
1243
1244 __ LoadObject(EDX, type_test_cache);
1245 __ pushl(EDX); // Cache array.
1246 __ pushl(EAX); // Instance.
1247 __ pushl(raw_null); // Unused
1248 __ call(&StubCode::Subtype1TestCacheLabel());
1249 __ popl(EAX); // Discard.
1250 __ popl(EAX); // Restore receiver.
1251 __ popl(EDX); // Discard.
1252 // Result is in ECX: null -> not found, otherwise Bool::True or Bool::False.
1253
1254 Label runtime_call;
1255 __ cmpl(ECX, raw_null);
1256 __ j(EQUAL, &runtime_call, Assembler::kNearJump);
1257 __ CompareObject(ECX, bool_true);
1258 __ j(EQUAL, is_instance_lbl);
1259 __ jmp(is_not_instance_lbl);
1260 __ Bind(&runtime_call);
1261 return type_test_cache.raw();
1262 }
1263
1264
1265 // Inline tests according to the 'type' being tested. Jump to labels
1266 // if we can compute the type-test, otherwise fallthrough.
1267 // EAX: instance to be tested, must be preserved.
1268 // Clobbers many registers.
1269 RawSubtypeTestCache* CodeGenerator::GenerateInlineInstanceof(
1270 intptr_t node_id,
1271 intptr_t token_pos,
1272 const AbstractType& type,
1273 Label* is_instance_lbl,
1274 Label* is_not_instance_lbl) {
1275 if (type.IsInstantiated()) {
1276 const Class& type_class = Class::ZoneHandle(type.type_class());
1277 // A Smi object cannot be the instance of a parameterized class.
1278 // A class equality check is only applicable with a dst type of a
1279 // non-parameterized class or with a raw dst type of a parameterized class.
1280 if (type_class.HasTypeArguments()) {
1281 return GenerateInstantiatedTypeWithArgumentsTest(node_id,
1282 token_pos,
1283 type,
1284 is_instance_lbl,
1285 is_not_instance_lbl);
1286 // Fall through to runtime call.
1287 } else {
1288 GenerateInstantiatedTypeNoArgumentsTest(node_id,
1289 token_pos,
1290 type,
1291 is_instance_lbl,
1292 is_not_instance_lbl);
1293 // If test non-conclusive so far, try the inlined type-test cache.
1294 // 'type' is known at compile time.
1295 return GenerateSubtype1TestCacheLookup(
1296 node_id, token_pos, type_class,
1297 is_instance_lbl, is_not_instance_lbl);
1298 }
1299 } else {
1300 return GenerateUninstantiatedTypeTest(type,
1301 node_id,
1302 token_pos,
1303 is_instance_lbl,
1304 is_not_instance_lbl);
1305 }
1306 return SubtypeTestCache::null();
1307 }
1308
1309
1310 // If instanceof type test cannot be performed successfully at compile time and
1311 // therefore eliminated, optimize it by adding inlined tests for:
1312 // - NULL -> return false.
1313 // - Smi -> compile time subtype check (only if dst class is not parameterized).
1314 // - Class equality (only if class is not parameterized).
1315 // Inputs:
1316 // - EAX: object.
1317 // Destroys ECX.
1318 // Returns:
1319 // - true or false on stack.
1320 void CodeGenerator::GenerateInstanceOf(intptr_t node_id,
1321 intptr_t token_pos,
1322 AstNode* value,
1323 const AbstractType& type,
1324 bool negate_result) {
1325 ASSERT(type.IsFinalized() && !type.IsMalformed());
1326 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
1327 const Bool& bool_false = Bool::ZoneHandle(Bool::False());
1328
1329 // All objects are instances of type T if Object type is a subtype of type T.
1330 const Type& object_type =
1331 Type::Handle(Isolate::Current()->object_store()->object_type());
1332 Error& malformed_error = Error::Handle();
1333 if (type.IsInstantiated() &&
1334 object_type.IsSubtypeOf(type, &malformed_error)) {
1335 __ PushObject(negate_result ? bool_false : bool_true);
1336 return;
1337 }
1338
1339 // Eliminate the test if it can be performed successfully at compile time.
1340 if ((value != NULL) && value->IsLiteralNode() && type.IsInstantiated()) {
1341 const Instance& literal_value = value->AsLiteralNode()->literal();
1342 const Class& cls = Class::Handle(literal_value.clazz());
1343 if (cls.IsNullClass()) {
1344 ASSERT(literal_value.IsNull() ||
1345 (literal_value.raw() == Object::sentinel()) ||
1346 (literal_value.raw() == Object::transition_sentinel()));
1347 // A null object is only an instance of Object and Dynamic, which has
1348 // already been checked above (if the type is instantiated). So we can
1349 // return false here if the instance is null (and if the type is
1350 // instantiated).
1351 __ PushObject(negate_result ? bool_true : bool_false);
1352 } else {
1353 Error& malformed_error = Error::Handle();
1354 if (literal_value.IsInstanceOf(type,
1355 TypeArguments::Handle(),
1356 &malformed_error)) {
1357 __ PushObject(negate_result ? bool_false : bool_true);
1358 } else {
1359 ASSERT(malformed_error.IsNull());
1360 __ PushObject(negate_result ? bool_true : bool_false);
1361 }
1362 }
1363 return;
1364 }
1365
1366 Label is_instance_of, is_not_instance_of;
1367 const Immediate raw_null =
1368 Immediate(reinterpret_cast<intptr_t>(Object::null()));
1369 Label done;
1370 // If type is instantiated and non-parameterized, we can inline code
1371 // checking whether the tested instance is a Smi.
1372 if (type.IsInstantiated()) {
1373 // A null object is only an instance of Object and Dynamic, which has
1374 // already been checked above (if the type is instantiated). So we can
1375 // return false here if the instance is null (and if the type is
1376 // instantiated).
1377 // We can only inline this null check if the type is instantiated at compile
1378 // time, since an uninstantiated type at compile time could be Object or
1379 // Dynamic at run time.
1380 Label non_null;
1381 __ cmpl(EAX, raw_null);
1382 __ j(NOT_EQUAL, &non_null, Assembler::kNearJump);
1383 __ PushObject(negate_result ? bool_true : bool_false);
1384 __ jmp(&done);
1385 __ Bind(&non_null);
1386 }
1387 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle();
1388 test_cache = GenerateInlineInstanceof(node_id, token_pos, type,
1389 &is_instance_of, &is_not_instance_of);
1390
1391 __ PushObject(Object::ZoneHandle()); // Make room for the result.
1392 const Immediate location = Immediate(Smi::RawValue(token_pos));
1393 const Immediate node_id_as_smi = Immediate(Smi::RawValue(node_id));
1394 __ pushl(location); // Push the source location.
1395 __ pushl(node_id_as_smi); // node-id.
1396 __ pushl(EAX); // Push the instance.
1397 __ PushObject(type); // Push the type.
1398 if (type.IsInstantiated()) {
1399 __ pushl(raw_null); // Null instantiator.
1400 __ pushl(raw_null); // Null instantiator.
1401 } else {
1402 const bool kPushInstantiator = true;
1403 GenerateInstantiatorTypeArguments(token_pos, kPushInstantiator);
1404 }
1405 __ LoadObject(EAX, test_cache);
1406 __ pushl(EAX);
1407 GenerateCallRuntime(node_id, token_pos, kInstanceofRuntimeEntry);
1408 // Pop the two parameters supplied to the runtime entry. The result of the
1409 // instanceof runtime call will be left as the result of the operation.
1410 __ addl(ESP, Immediate(7 * kWordSize));
1411 if (negate_result) {
1412 __ popl(EDX);
1413 __ CompareObject(EDX, bool_false);
1414 __ j(EQUAL, &is_not_instance_of, Assembler::kNearJump);
1415 // Fall through to is_instance_of.
1416 } else {
1417 __ jmp(&done);
1418 }
1419
1420 __ Bind(&is_instance_of);
1421 __ PushObject(negate_result ? bool_false: bool_true);
1422 __ jmp(&done, Assembler::kNearJump);
1423 __ Bind(&is_not_instance_of);
1424 __ PushObject(negate_result ? bool_true: bool_false);
1425 __ Bind(&done);
1426 }
1427
1428
1429 // EAX: instance to test.
1430 // Clobbers: ECX.
1431 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if
1432 // type test is conclusive, otherwise fallthrough if a type test could not
1433 // be completed.
1434 RawSubtypeTestCache* CodeGenerator::GenerateInstantiatedTypeWithArgumentsTest(
1435 intptr_t node_id,
1436 intptr_t token_pos,
1437 const AbstractType& type,
1438 Label* is_instance_lbl,
1439 Label* is_not_instance_lbl) {
1440 ASSERT(type.IsInstantiated());
1441 const Class& type_class = Class::ZoneHandle(type.type_class());
1442 ASSERT(type_class.HasTypeArguments());
1443 // A Smi object cannot be the instance of a parameterized class.
1444 // A class equality check is only applicable with a dst type of a
1445 // non-parameterized class or with a raw dst type of a parameterized class.
1446 __ testl(EAX, Immediate(kSmiTagMask));
1447 __ j(ZERO, is_not_instance_lbl);
1448 const AbstractTypeArguments& type_arguments =
1449 AbstractTypeArguments::ZoneHandle(type.arguments());
1450 const bool is_raw_type = type_arguments.IsNull() ||
1451 type_arguments.IsRaw(type_arguments.Length());
1452 if (is_raw_type) {
1453 // Dynamic type argument, check only classes.
1454 __ LoadClassId(ECX, EAX);
1455 if (!type_class.is_interface()) {
1456 __ cmpl(ECX, Immediate(type_class.id()));
1457 __ j(EQUAL, is_instance_lbl);
1458 }
1459 if (type.IsListInterface()) {
1460 // TODO(srdjan) also accept List<Object>.
1461 __ cmpl(ECX, Immediate(kArray));
1462 __ j(EQUAL, is_instance_lbl);
1463 __ cmpl(ECX, Immediate(kGrowableObjectArray));
1464 __ j(EQUAL, is_instance_lbl);
1465 }
1466 return
1467 GenerateSubtype1TestCacheLookup(node_id, token_pos, type_class,
1468 is_instance_lbl, is_not_instance_lbl);
1469 }
1470 // If one type argument only, quick check.
1471 if (type_arguments.Length() == 1) {
1472 const AbstractType& tp_argument = AbstractType::ZoneHandle(
1473 type_arguments.TypeAt(0));
1474 if (tp_argument.IsType()) {
1475 // Malformed type has been caught in the caller chain of this function.
1476 ASSERT(tp_argument.HasResolvedTypeClass());
1477 // Check if type argument is dynamic or Object.
1478 const Type& object_type =
1479 Type::Handle(Isolate::Current()->object_store()->object_type());
1480 Error& malformed_error = Error::Handle();
1481 if (object_type.IsSubtypeOf(tp_argument, &malformed_error)) {
1482 // Instance class test only necessary.
1483 return GenerateSubtype1TestCacheLookup(
1484 node_id,
1485 token_pos,
1486 type_class,
1487 is_instance_lbl,
1488 is_not_instance_lbl);
1489 }
1490 }
1491 }
1492 const SubtypeTestCache& type_test_cache =
1493 SubtypeTestCache::ZoneHandle(SubtypeTestCache::New());
1494 Label inlined_check, fall_through;
1495 const Immediate raw_null =
1496 Immediate(reinterpret_cast<intptr_t>(Object::null()));
1497 __ LoadObject(EDX, type_test_cache);
1498 __ pushl(EDX); // Subtype test cache.
1499 __ pushl(EAX); // Instance.
1500 __ pushl(raw_null); // Unused.
1501 __ call(&StubCode::Subtype2TestCacheLabel());
1502 __ popl(EAX); // Discard.
1503 __ popl(EAX); // Restore receiver.
1504 __ popl(EDX); // Discard.
1505 // Result is in ECX: null -> not found, otherwise Bool::True or Bool::False.
1506
1507 __ cmpl(ECX, raw_null);
1508 __ j(EQUAL, &fall_through, Assembler::kNearJump);
1509 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
1510 __ CompareObject(ECX, bool_true);
1511 __ j(EQUAL, is_instance_lbl);
1512 __ jmp(is_not_instance_lbl);
1513 __ Bind(&fall_through);
1514 return type_test_cache.raw();
1515 }
1516
1517
1518 // EAX: instance to test.
1519 // Clobbers: EBX, ECX, EDX.
1520 void CodeGenerator::GenerateInstantiatedTypeNoArgumentsTest(
1521 intptr_t node_id,
1522 intptr_t token_pos,
1523 const AbstractType& type,
1524 Label* is_instance_lbl,
1525 Label* is_not_instance_lbl) {
1526 ASSERT(type.IsInstantiated());
1527 const Class& type_class = Class::Handle(type.type_class());
1528 ASSERT(!type_class.HasTypeArguments());
1529
1530 Label compare_classes;
1531 __ testl(EAX, Immediate(kSmiTagMask));
1532 __ j(NOT_ZERO, &compare_classes, Assembler::kNearJump);
1533 // Instance is Smi, check directly.
1534 const Class& smi_class = Class::Handle(Smi::Class());
1535 // TODO(regis): We should introduce a SmiType.
1536 Error& malformed_error = Error::Handle();
1537 if (smi_class.IsSubtypeOf(TypeArguments::Handle(),
1538 type_class,
1539 TypeArguments::Handle(),
1540 &malformed_error)) {
1541 __ jmp(is_instance_lbl);
1542 } else {
1543 __ jmp(is_not_instance_lbl);
1544 }
1545 // Compare if the classes are equal.
1546 __ Bind(&compare_classes);
1547
1548 // Checking against interface.
1549 // However, for specific core library interfaces, we can check for
1550 // specific core library classes.
1551 if (type.IsBoolInterface()) {
1552 __ CompareClassId(EAX, kBool, ECX);
1553 __ j(EQUAL, is_instance_lbl);
1554 __ jmp(is_not_instance_lbl);
1555 return;
1556 }
1557 // If type is an interface, we can skip the class equality check,
1558 // because instances cannot be of an interface type.
1559 if (!type_class.is_interface()) {
1560 __ CompareClassId(EAX, type_class.id(), ECX);
1561 __ j(EQUAL, is_instance_lbl);
1562 }
1563 if (type.IsSubtypeOf(
1564 Type::Handle(Type::NumberInterface()), &malformed_error)) {
1565 // Custom checking for numbers (Smi, Mint, Bigint and Double)
1566 __ LoadClassId(ECX, EAX);
1567
1568 if (type.IsIntInterface() || type.IsNumberInterface()) {
1569 // We already checked for Smi above.
1570 __ cmpl(ECX, Immediate(kMint));
1571 __ j(EQUAL, is_instance_lbl);
1572 __ cmpl(ECX, Immediate(kBigint));
1573 __ j(EQUAL, is_instance_lbl);
1574 if (type.IsIntInterface()) {
1575 __ jmp(is_not_instance_lbl);
1576 }
1577 }
1578 if (type.IsDoubleInterface() || type.IsNumberInterface()) {
1579 __ cmpl(ECX, Immediate(kDouble));
1580 __ j(EQUAL, is_instance_lbl);
1581 __ jmp(is_not_instance_lbl);
1582 }
1583 } else if (type.IsStringInterface()) {
1584 __ LoadClassId(ECX, EAX);
1585 __ cmpl(ECX, Immediate(kOneByteString));
1586 __ j(EQUAL, is_instance_lbl);
1587 __ cmpl(ECX, Immediate(kTwoByteString));
1588 __ j(EQUAL, is_instance_lbl);
1589 __ cmpl(ECX, Immediate(kFourByteString));
1590 __ j(EQUAL, is_instance_lbl);
1591 } else if (type.IsFunctionInterface()) {
1592 // Check if instance is a closure.
1593 const Immediate raw_null =
1594 Immediate(reinterpret_cast<intptr_t>(Object::null()));
1595 __ LoadClass(ECX, EAX, EBX);
1596 __ movl(ECX, FieldAddress(ECX, Class::signature_function_offset()));
1597 __ cmpl(ECX, raw_null);
1598 __ j(NOT_EQUAL, is_instance_lbl);
1599 }
1600 // Otherwise fallthrough.
1601 }
1602
1603
1604 // EAX: instance to test.
1605 // Clobbers: EBX, EDX, ECX.
1606 RawSubtypeTestCache* CodeGenerator::GenerateUninstantiatedTypeTest(
1607 const AbstractType& type,
1608 intptr_t node_id,
1609 intptr_t token_pos,
1610 Label* is_instance_lbl,
1611 Label* is_not_instance_lbl) {
1612 ASSERT(!type.IsInstantiated());
1613 // Skip check if destination is a dynamic type.
1614 const Immediate raw_null =
1615 Immediate(reinterpret_cast<intptr_t>(Object::null()));
1616 if (type.IsTypeParameter()) {
1617 // EAX must be preserved!
1618 Label fall_through;
1619 const bool kPushInstantiator = false;
1620 GenerateInstantiatorTypeArguments(token_pos, kPushInstantiator);
1621 // Type arguments are on stack.
1622 __ popl(EBX);
1623 // Check if type argument is dynamic.
1624 __ cmpl(EBX, raw_null);
1625 __ j(EQUAL, is_instance_lbl);
1626
1627 // EBX: instantiator type arguments.
1628 // For now handle only TypeArguments and bail out if InstantiatedTypeArgs.
1629 // We expect that frequently checked objects wull have their type arguments
1630 // converted into instance of TypeArguments.
1631 __ CompareClassId(EBX, kTypeArguments, EDX);
1632 __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump);
1633
1634 __ movl(EDX,
1635 FieldAddress(EBX, TypeArguments::type_at_offset(type.Index())));
1636 // EDX: concrete type of type.
1637 // Check if type argument is dynamic.
1638 __ CompareObject(EDX, Type::ZoneHandle(Type::DynamicType()));
1639 __ j(EQUAL, is_instance_lbl);
1640 __ cmpl(EDX, raw_null);
1641 __ j(EQUAL, is_instance_lbl);
1642
1643 // For Smi check quickly against int and num interfaces.
1644 Label not_smi;
1645 __ testl(EAX, Immediate(kSmiTagMask)); // Value is Smi?
1646 __ j(NOT_ZERO, &not_smi, Assembler::kNearJump);
1647 __ CompareObject(EDX, Type::ZoneHandle(Type::IntInterface()));
1648 __ j(EQUAL, is_instance_lbl);
1649 __ CompareObject(EDX, Type::ZoneHandle(Type::NumberInterface()));
1650 __ j(EQUAL, is_instance_lbl);
1651 __ jmp(&fall_through);
1652
1653 __ Bind(&not_smi);
1654 // EBX: instantiator type arguments.
1655 // EAX: instance
1656 // The instantiated type parameter may not be a Type, but could be an
1657 // InstantiatedType. It is therefore necessary to check its class.
1658 const SubtypeTestCache& type_test_cache =
1659 SubtypeTestCache::ZoneHandle(SubtypeTestCache::New());
1660 __ LoadObject(ECX, type_test_cache);
1661 __ pushl(ECX); // Subtype test cache.
1662 __ pushl(EAX); // Instance.
1663 __ pushl(EBX); // Instantator type arguments.
1664 __ call(&StubCode::Subtype3TestCacheLabel());
1665 __ popl(EDX); // Discard type arguments.
1666 __ popl(EAX); // Restore receiver.
1667 __ popl(EDX); // Discard subtype test cache.
1668 // Result is in ECX: null -> not found, otherwise Bool::True or Bool::False.
1669 __ cmpl(ECX, raw_null);
1670 __ j(EQUAL, &fall_through, Assembler::kNearJump);
1671 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
1672 __ CompareObject(ECX, bool_true);
1673 __ j(EQUAL, is_instance_lbl);
1674 __ jmp(is_not_instance_lbl);
1675 __ Bind(&fall_through);
1676 return type_test_cache.raw();
1677 }
1678 if (type.IsType()) {
1679 Label fall_through;
1680 __ testl(EAX, Immediate(kSmiTagMask)); // Is instance Smi?
1681 __ j(ZERO, is_not_instance_lbl);
1682 // Uninstantiated type class is known at compile time, but the type
1683 // arguments are determined at runtime by the instantiator.
1684 const SubtypeTestCache& type_test_cache =
1685 SubtypeTestCache::ZoneHandle(SubtypeTestCache::New());
1686 __ LoadObject(EDX, type_test_cache);
1687 __ pushl(EDX); // Subtype test cache.
1688 __ pushl(EAX); // Instance.
1689 const bool kPushInstantiator = false;
1690 GenerateInstantiatorTypeArguments(token_pos, kPushInstantiator);
1691 __ call(&StubCode::Subtype3TestCacheLabel());
1692 __ popl(EDX); // Discard type arguments.
1693 __ popl(EAX); // Restore receiver.
1694 __ popl(EDX); // Discard subtype test cache.
1695 // Result is in ECX: null -> not found, otherwise Bool::True or Bool::False.
1696 __ cmpl(ECX, raw_null);
1697 __ j(EQUAL, &fall_through, Assembler::kNearJump);
1698 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
1699 __ CompareObject(ECX, bool_true);
1700 __ j(EQUAL, is_instance_lbl);
1701 __ jmp(is_not_instance_lbl);
1702 __ Bind(&fall_through);
1703 return type_test_cache.raw();
1704 }
1705 return SubtypeTestCache::null();
1706 }
1707
1708
1709 // If type check cannot be performed successfully at compile time and therefore
1710 // eliminated, optimize it by adding inlined tests for:
1711 // - NULL -> return NULL.
1712 // - Smi -> compile time subtype check (only if dst class is not parameterized).
1713 // - Class equality (only if class is not parameterized).
1714 // Inputs:
1715 // - EAX: object.
1716 // Destroys ECX and EDX.
1717 // Returns:
1718 // - object in EAX for successful assignable check (or throws TypeError).
1719 // Performance notes: positive checks must be quick, negative checks can be slow
1720 // as they throw an exception.
1721 void CodeGenerator::GenerateAssertAssignable(intptr_t node_id,
1722 intptr_t token_pos,
1723 AstNode* value,
1724 const AbstractType& dst_type,
1725 const String& dst_name) {
1726 ASSERT(FLAG_enable_type_checks);
1727 ASSERT(token_pos >= 0);
1728 ASSERT(!dst_type.IsNull());
1729 ASSERT(dst_type.IsFinalized());
1730
1731 // Any expression is assignable to the Dynamic type and to the Object type.
1732 // Skip the test.
1733 if (!dst_type.IsMalformed() &&
1734 (dst_type.IsDynamicType() || dst_type.IsObjectType())) {
1735 return;
1736 }
1737
1738 // It is a compile-time error to explicitly return a value (including null)
1739 // from a void function. However, functions that do not explicitly return a
1740 // value, implicitly return null. This includes void functions. Therefore, we
1741 // skip the type test here and trust the parser to only return null in void
1742 // function.
1743 if (dst_type.IsVoidType()) {
1744 return;
1745 }
1746
1747 // Eliminate the test if it can be performed successfully at compile time.
1748 if ((value != NULL) && value->IsLiteralNode()) {
1749 const Instance& literal_value = value->AsLiteralNode()->literal();
1750 const Class& cls = Class::Handle(literal_value.clazz());
1751 if (cls.IsNullClass()) {
1752 ASSERT(literal_value.IsNull() ||
1753 (literal_value.raw() == Object::sentinel()) ||
1754 (literal_value.raw() == Object::transition_sentinel()));
1755 return;
1756 }
1757 Error& malformed_error = Error::Handle();
1758 if (!dst_type.IsMalformed() &&
1759 dst_type.IsInstantiated() &&
1760 literal_value.IsInstanceOf(dst_type,
1761 TypeArguments::Handle(),
1762 &malformed_error)) {
1763 return;
1764 }
1765 }
1766
1767 // A null object is always assignable and is returned as result.
1768 const Immediate raw_null =
1769 Immediate(reinterpret_cast<intptr_t>(Object::null()));
1770 Label done, runtime_call;
1771 __ cmpl(EAX, raw_null);
1772 __ j(EQUAL, &done);
1773
1774 // Generate throw new TypeError() if the type is malformed.
1775 if (dst_type.IsMalformed()) {
1776 const Error& error = Error::Handle(dst_type.malformed_error());
1777 const String& error_message = String::ZoneHandle(
1778 String::NewSymbol(error.ToErrorCString()));
1779 __ PushObject(Object::ZoneHandle()); // Make room for the result.
1780 __ pushl(Immediate(Smi::RawValue(token_pos))); // Source location.
1781 __ pushl(EAX); // Push the source object.
1782 __ PushObject(dst_name); // Push the name of the destination.
1783 __ PushObject(error_message);
1784 GenerateCallRuntime(node_id, token_pos, kMalformedTypeErrorRuntimeEntry);
1785 // We should never return here.
1786 __ int3();
1787
1788 __ Bind(&done); // For a null object.
1789 return;
1790 }
1791
1792 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle();
1793 test_cache = GenerateInlineInstanceof(node_id, token_pos, dst_type,
1794 &done, &runtime_call);
1795
1796 __ Bind(&runtime_call);
1797 __ PushObject(Object::ZoneHandle()); // Make room for the result.
1798 __ pushl(Immediate(Smi::RawValue(token_pos))); // Source location.
1799 __ pushl(Immediate(Smi::RawValue(node_id))); // node-id.
1800 __ pushl(EAX); // Push the source object.
1801 __ PushObject(dst_type); // Push the type of the destination.
1802 if (dst_type.IsInstantiated()) {
1803 __ pushl(raw_null); // Null instantiator.
1804 __ pushl(raw_null); // Null instantiator type argument.
1805 } else {
1806 const bool kPushInstantiator = true;
1807 GenerateInstantiatorTypeArguments(token_pos, kPushInstantiator);
1808 }
1809 __ PushObject(dst_name); // Push the name of the destination.
1810 __ LoadObject(EAX, test_cache);
1811 __ pushl(EAX);
1812 GenerateCallRuntime(node_id, token_pos, kTypeCheckRuntimeEntry);
1813 // Pop the parameters supplied to the runtime entry. The result of the
1814 // type check runtime call is the checked value.
1815 __ addl(ESP, Immediate(8 * kWordSize));
1816 __ popl(EAX);
1817
1818 // EAX: value.
1819 __ Bind(&done);
1820 }
1821
1822
1823 void CodeGenerator::GenerateArgumentTypeChecks() {
1824 const Function& function = parsed_function_.function();
1825 const SequenceNode& sequence_node = *parsed_function_.node_sequence();
1826 LocalScope* scope = sequence_node.scope();
1827 const int num_fixed_params = function.num_fixed_parameters();
1828 const int num_opt_params = function.num_optional_parameters();
1829 ASSERT(num_fixed_params + num_opt_params <= scope->num_variables());
1830 for (intptr_t i = 0; i < num_fixed_params + num_opt_params; i++) {
1831 LocalVariable* parameter = scope->VariableAt(i);
1832 GenerateLoadVariable(EAX, *parameter);
1833 GenerateAssertAssignable(sequence_node.ParameterIdAt(i),
1834 parameter->token_pos(),
1835 NULL,
1836 parameter->type(),
1837 parameter->name());
1838 }
1839 }
1840
1841
1842 void CodeGenerator::GenerateConditionTypeCheck(intptr_t node_id,
1843 intptr_t token_pos) {
1844 if (!FLAG_enable_type_checks) {
1845 return;
1846 }
1847
1848 // Check that the type of the object on the stack is allowed in conditional
1849 // context.
1850 // Call the runtime if the object is null or not of type bool.
1851 const Immediate raw_null =
1852 Immediate(reinterpret_cast<intptr_t>(Object::null()));
1853 Label runtime_call, done;
1854 __ movl(EAX, Address(ESP, 0));
1855 __ cmpl(EAX, raw_null);
1856 __ j(EQUAL, &runtime_call, Assembler::kNearJump);
1857 __ testl(EAX, Immediate(kSmiTagMask));
1858 __ j(ZERO, &runtime_call, Assembler::kNearJump); // Call runtime for Smi.
1859 // This check should pass if the receiver's class implements the interface
1860 // 'bool'. Check only class 'Bool' since it is the only legal implementation
1861 // of the interface 'bool'.
1862 __ CompareClassId(EAX, kBool, ECX);
1863 __ j(EQUAL, &done, Assembler::kNearJump);
1864
1865 __ Bind(&runtime_call);
1866 __ pushl(Immediate(Smi::RawValue(token_pos))); // Source location.
1867 __ pushl(EAX); // Push the source object.
1868 GenerateCallRuntime(node_id, token_pos, kConditionTypeErrorRuntimeEntry);
1869 // We should never return here.
1870 __ int3();
1871
1872 __ Bind(&done);
1873 }
1874
1875
1876 void CodeGenerator::VisitComparisonNode(ComparisonNode* node) {
1877 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
1878 const Bool& bool_false = Bool::ZoneHandle(Bool::False());
1879 node->left()->Visit(this);
1880
1881 // The instanceof operator needs special handling.
1882 if (Token::IsTypeTestOperator(node->kind())) {
1883 __ popl(EAX); // Left operand.
1884 ASSERT(node->right()->IsTypeNode());
1885 GenerateInstanceOf(node->id(),
1886 node->token_pos(),
1887 node->left(),
1888 node->right()->AsTypeNode()->type(),
1889 (node->kind() == Token::kISNOT));
1890 if (!IsResultNeeded(node)) {
1891 __ popl(EAX); // Pop the result of the instanceof operation.
1892 }
1893 return;
1894 }
1895
1896 if (Token::IsTypeCastOperator(node->kind())) {
1897 // This code generator is not maintained anymore.
1898 UNIMPLEMENTED();
1899 }
1900
1901 node->right()->Visit(this);
1902 // Both left and right values on stack.
1903
1904 // '===' and '!==' are not overloadable.
1905 if ((node->kind() == Token::kEQ_STRICT) ||
1906 (node->kind() == Token::kNE_STRICT)) {
1907 __ popl(EDX); // Right operand.
1908 __ popl(EAX); // Left operand.
1909 if (!IsResultNeeded(node)) {
1910 return;
1911 }
1912 Label load_true, done;
1913 __ cmpl(EAX, EDX);
1914 if (node->kind() == Token::kEQ_STRICT) {
1915 __ j(EQUAL, &load_true, Assembler::kNearJump);
1916 } else {
1917 __ j(NOT_EQUAL, &load_true, Assembler::kNearJump);
1918 }
1919 __ LoadObject(EAX, bool_false);
1920 __ jmp(&done, Assembler::kNearJump);
1921 __ Bind(&load_true);
1922 __ LoadObject(EAX, bool_true);
1923 __ Bind(&done);
1924 // Result is in EAX.
1925 __ pushl(EAX);
1926 return;
1927 }
1928
1929 MarkDeoptPoint(node->id(), node->token_pos());
1930
1931 // '!=' not overloadable, always implements negation of '=='.
1932 // Call operator for '=='.
1933 if ((node->kind() == Token::kEQ) || (node->kind() == Token::kNE)) {
1934 // Null is a special receiver with a special type and frequently used on
1935 // operators "==" and "!=". Emit inlined code for null so that it does not
1936 // pollute type information at call site.
1937 Label null_done;
1938 {
1939 const Immediate raw_null =
1940 Immediate(reinterpret_cast<intptr_t>(Object::null()));
1941 Label non_null_compare, load_true;
1942 // Check if left argument is null.
1943 __ cmpl(Address(ESP, 1 * kWordSize), raw_null);
1944 __ j(NOT_EQUAL, &non_null_compare, Assembler::kNearJump);
1945 // Comparison with NULL is "===".
1946 // Load/remove arguments.
1947 __ popl(EDX);
1948 __ popl(EAX);
1949 __ cmpl(EAX, EDX);
1950 if (node->kind() == Token::kEQ) {
1951 __ j(EQUAL, &load_true, Assembler::kNearJump);
1952 } else {
1953 __ j(NOT_EQUAL, &load_true, Assembler::kNearJump);
1954 }
1955 __ LoadObject(EAX, bool_false);
1956 __ jmp(&null_done, Assembler::kNearJump);
1957 __ Bind(&load_true);
1958 __ LoadObject(EAX, bool_true);
1959 __ jmp(&null_done, Assembler::kNearJump);
1960 __ Bind(&non_null_compare);
1961 }
1962 // Do '==' first then negate if necessary,
1963 const String& operator_name = String::ZoneHandle(String::NewSymbol("=="));
1964 const int kNumberOfArguments = 2;
1965 const Array& kNoArgumentNames = Array::Handle();
1966 const int kNumArgumentsChecked = 1;
1967 GenerateInstanceCall(node->id(),
1968 node->token_pos(),
1969 operator_name,
1970 kNumberOfArguments,
1971 kNoArgumentNames,
1972 kNumArgumentsChecked);
1973
1974 // Result is in EAX. No need to negate if result is not needed.
1975 if ((node->kind() == Token::kNE) && IsResultNeeded(node)) {
1976 // Negate result.
1977 Label load_true, done;
1978 __ LoadObject(EDX, bool_false);
1979 __ cmpl(EAX, EDX);
1980 __ j(EQUAL, &load_true, Assembler::kNearJump);
1981 __ movl(EAX, EDX); // false.
1982 __ jmp(&done, Assembler::kNearJump);
1983 __ Bind(&load_true);
1984 __ LoadObject(EAX, bool_true);
1985 __ Bind(&done);
1986 }
1987 __ Bind(&null_done);
1988 // Result is in EAX.
1989 if (IsResultNeeded(node)) {
1990 __ pushl(EAX);
1991 }
1992 return;
1993 }
1994
1995 // Call operator.
1996 GenerateBinaryOperatorCall(node->id(), node->token_pos(), node->Name());
1997 // Result is in EAX.
1998 if (IsResultNeeded(node)) {
1999 __ pushl(EAX);
2000 }
2001 }
2002
2003
2004 void CodeGenerator::HandleBackwardBranch(
2005 intptr_t loop_id, intptr_t token_pos) {
2006 // Use stack overflow check to eventually stop execution of loops.
2007 // This is necessary only if a loop does not have calls.
2008 __ cmpl(ESP,
2009 Address::Absolute(Isolate::Current()->stack_limit_address()));
2010 Label no_stack_overflow;
2011 __ j(ABOVE, &no_stack_overflow);
2012 GenerateCallRuntime(loop_id,
2013 token_pos,
2014 kStackOverflowRuntimeEntry);
2015 __ Bind(&no_stack_overflow);
2016 }
2017
2018
2019 void CodeGenerator::VisitWhileNode(WhileNode* node) {
2020 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
2021 SourceLabel* label = node->label();
2022 __ Bind(label->continue_label());
2023 node->condition()->Visit(this);
2024 GenerateConditionTypeCheck(node->id(), node->condition()->token_pos());
2025 __ popl(EAX);
2026 __ LoadObject(EDX, bool_true);
2027 __ cmpl(EAX, EDX);
2028 __ j(NOT_EQUAL, label->break_label());
2029 node->body()->Visit(this);
2030 HandleBackwardBranch(node->id(), node->token_pos());
2031 __ jmp(label->continue_label());
2032 __ Bind(label->break_label());
2033 }
2034
2035
2036 void CodeGenerator::VisitDoWhileNode(DoWhileNode* node) {
2037 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
2038 SourceLabel* label = node->label();
2039 Label loop;
2040 __ Bind(&loop);
2041 node->body()->Visit(this);
2042 HandleBackwardBranch(node->id(), node->token_pos());
2043 __ Bind(label->continue_label());
2044 node->condition()->Visit(this);
2045 GenerateConditionTypeCheck(node->id(), node->condition()->token_pos());
2046 __ popl(EAX);
2047 __ LoadObject(EDX, bool_true);
2048 __ cmpl(EAX, EDX);
2049 __ j(EQUAL, &loop);
2050 __ Bind(label->break_label());
2051 }
2052
2053
2054 void CodeGenerator::VisitForNode(ForNode* node) {
2055 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
2056 node->initializer()->Visit(this);
2057 SourceLabel* label = node->label();
2058 Label loop;
2059 __ Bind(&loop);
2060 if (node->condition() != NULL) {
2061 node->condition()->Visit(this);
2062 GenerateConditionTypeCheck(node->id(), node->condition()->token_pos());
2063 __ popl(EAX);
2064 __ LoadObject(EDX, bool_true);
2065 __ cmpl(EAX, EDX);
2066 __ j(NOT_EQUAL, label->break_label());
2067 }
2068 node->body()->Visit(this);
2069 HandleBackwardBranch(node->id(), node->token_pos());
2070 __ Bind(label->continue_label());
2071 node->increment()->Visit(this);
2072 __ jmp(&loop);
2073 __ Bind(label->break_label());
2074 }
2075
2076
2077 void CodeGenerator::VisitJumpNode(JumpNode* node) {
2078 SourceLabel* label = node->label();
2079
2080 // Generate inlined code for all finally blocks as we may transfer
2081 // control out of the 'try' blocks if any.
2082 for (intptr_t i = 0; i < node->inlined_finally_list_length(); i++) {
2083 node->InlinedFinallyNodeAt(i)->Visit(this);
2084 }
2085
2086 // Unchain the context(s) up to the outer context level of the scope which
2087 // contains the destination label.
2088 ASSERT(label->owner() != NULL);
2089 intptr_t target_context_level = 0;
2090 LocalScope* target_scope = label->owner();
2091 if (target_scope->num_context_variables() > 0) {
2092 // The scope of the target label allocates a context, therefore its outer
2093 // scope is at a lower context level.
2094 target_context_level = target_scope->context_level() - 1;
2095 } else {
2096 // The scope of the target label does not allocate a context, so its outer
2097 // scope is at the same context level. Find it.
2098 while ((target_scope != NULL) &&
2099 (target_scope->num_context_variables() == 0)) {
2100 target_scope = target_scope->parent();
2101 }
2102 if (target_scope != NULL) {
2103 target_context_level = target_scope->context_level();
2104 }
2105 }
2106 ASSERT(target_context_level >= 0);
2107 int current_context_level = context_level();
2108 ASSERT(current_context_level >= target_context_level);
2109 while (current_context_level-- > target_context_level) {
2110 __ movl(CTX, FieldAddress(CTX, Context::parent_offset()));
2111 }
2112
2113 if (node->kind() == Token::kBREAK) {
2114 __ jmp(label->break_label());
2115 } else {
2116 __ jmp(label->continue_label());
2117 }
2118 }
2119
2120
2121 void CodeGenerator::VisitConditionalExprNode(ConditionalExprNode* node) {
2122 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
2123 Label false_label, done;
2124 node->condition()->Visit(this);
2125 GenerateConditionTypeCheck(node->id(), node->condition()->token_pos());
2126 __ popl(EAX);
2127 __ LoadObject(EDX, bool_true);
2128 __ cmpl(EAX, EDX);
2129 __ j(NOT_EQUAL, &false_label);
2130 node->true_expr()->Visit(this);
2131 __ jmp(&done);
2132 __ Bind(&false_label);
2133 node->false_expr()->Visit(this);
2134 __ Bind(&done);
2135 if (!IsResultNeeded(node)) {
2136 __ popl(EAX);
2137 }
2138 }
2139
2140
2141 void CodeGenerator::VisitSwitchNode(SwitchNode *node) {
2142 SourceLabel* label = node->label();
2143 node->body()->Visit(this);
2144 __ Bind(label->break_label());
2145 }
2146
2147
2148 void CodeGenerator::VisitCaseNode(CaseNode* node) {
2149 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
2150 Label case_statements, end_case;
2151
2152 for (int i = 0; i < node->case_expressions()->length(); i++) {
2153 // Load case expression onto stack.
2154 AstNode* case_expr = node->case_expressions()->NodeAt(i);
2155 case_expr->Visit(this);
2156 __ popl(EAX);
2157 __ CompareObject(EAX, bool_true);
2158 // Jump to case clause code if case expression equals switch expression
2159 __ j(EQUAL, &case_statements);
2160 }
2161 // If this case clause contains the default label, fall through to
2162 // case clause code, else skip this clause.
2163 if (!node->contains_default()) {
2164 __ jmp(&end_case);
2165 }
2166
2167 // If there is a label associated with this case clause, bind it.
2168 if (node->label() != NULL) {
2169 __ Bind(node->label()->continue_label());
2170 }
2171
2172 // Generate code for case clause statements. The parser guarantees that
2173 // the code contains a jump, so we should never fall through the end
2174 // of the statements.
2175 __ Bind(&case_statements);
2176 node->statements()->Visit(this);
2177 __ Bind(&end_case);
2178 }
2179
2180
2181 void CodeGenerator::VisitIfNode(IfNode* node) {
2182 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
2183 Label false_label;
2184 node->condition()->Visit(this);
2185 GenerateConditionTypeCheck(node->id(), node->condition()->token_pos());
2186 __ popl(EAX);
2187 __ LoadObject(EDX, bool_true);
2188 __ cmpl(EAX, EDX);
2189 __ j(NOT_EQUAL, &false_label);
2190 node->true_branch()->Visit(this);
2191 if (node->false_branch() != NULL) {
2192 Label done;
2193 __ jmp(&done);
2194 __ Bind(&false_label);
2195 node->false_branch()->Visit(this);
2196 __ Bind(&done);
2197 } else {
2198 __ Bind(&false_label);
2199 }
2200 }
2201
2202
2203 // Operators '&&' and '||' are not overloadabled, inline them.
2204 void CodeGenerator::GenerateLogicalAndOrOp(BinaryOpNode* node) {
2205 // Generate true if (left == true) op (right == true), otherwise generate
2206 // false, with op being either || or &&.
2207 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
2208 const Bool& bool_false = Bool::ZoneHandle(Bool::False());
2209 Label load_false, done;
2210 node->left()->Visit(this);
2211 GenerateConditionTypeCheck(node->id(), node->left()->token_pos());
2212 __ popl(EAX);
2213 __ LoadObject(EDX, bool_true);
2214 __ cmpl(EAX, EDX);
2215 if (node->kind() == Token::kAND) {
2216 __ j(NOT_EQUAL, &load_false);
2217 } else {
2218 ASSERT(node->kind() == Token::kOR);
2219 __ j(EQUAL, &done);
2220 }
2221 node->right()->Visit(this);
2222 GenerateConditionTypeCheck(node->id(), node->right()->token_pos());
2223 __ popl(EAX);
2224 __ LoadObject(EDX, bool_true);
2225 __ cmpl(EAX, EDX);
2226 __ j(EQUAL, &done);
2227 __ Bind(&load_false);
2228 __ LoadObject(EAX, bool_false);
2229 __ Bind(&done);
2230 if (IsResultNeeded(node)) {
2231 __ pushl(EAX);
2232 }
2233 }
2234
2235
2236 // Expect receiver(left operand) and right operand on stack.
2237 // Return result in EAX.
2238 void CodeGenerator::GenerateBinaryOperatorCall(intptr_t node_id,
2239 intptr_t token_pos,
2240 const char* name) {
2241 const String& operator_name = String::ZoneHandle(String::NewSymbol(name));
2242 const int kNumberOfArguments = 2;
2243 const Array& kNoArgumentNames = Array::Handle();
2244 const int kNumArgumentsChecked = 2;
2245 GenerateInstanceCall(node_id,
2246 token_pos,
2247 operator_name,
2248 kNumberOfArguments,
2249 kNoArgumentNames,
2250 kNumArgumentsChecked);
2251 }
2252
2253
2254 void CodeGenerator::VisitBinaryOpNode(BinaryOpNode* node) {
2255 if ((node->kind() == Token::kAND) || (node->kind() == Token::kOR)) {
2256 // Operators "&&" and "||" cannot be overloaded, therefore inline them
2257 // instead of calling the operator.
2258 GenerateLogicalAndOrOp(node);
2259 return;
2260 }
2261 node->left()->Visit(this);
2262 node->right()->Visit(this);
2263 MarkDeoptPoint(node->id(), node->token_pos());
2264 GenerateBinaryOperatorCall(node->id(), node->token_pos(), node->Name());
2265 if (IsResultNeeded(node)) {
2266 __ pushl(EAX);
2267 }
2268 }
2269
2270
2271 void CodeGenerator::VisitStringConcatNode(StringConcatNode* node) {
2272 const String& cls_name = String::Handle(String::NewSymbol("StringBase"));
2273 const Library& core_lib = Library::Handle(
2274 Isolate::Current()->object_store()->core_library());
2275 const Class& cls = Class::Handle(core_lib.LookupClass(cls_name));
2276 ASSERT(!cls.IsNull());
2277 const String& func_name = String::Handle(String::NewSymbol("_interpolate"));
2278 const int number_of_parameters = 1;
2279 const Function& interpol_func = Function::ZoneHandle(
2280 Resolver::ResolveStatic(cls, func_name,
2281 number_of_parameters,
2282 Array::Handle(),
2283 Resolver::kIsQualified));
2284 ASSERT(!interpol_func.IsNull());
2285
2286 // First try to concatenate and canonicalize the values at compile time.
2287 bool compile_time_interpolation = true;
2288 Array& literals = Array::Handle(Array::New(node->values()->length()));
2289 for (int i = 0; i < node->values()->length(); i++) {
2290 if (node->values()->ElementAt(i)->IsLiteralNode()) {
2291 LiteralNode* lit = node->values()->ElementAt(i)->AsLiteralNode();
2292 literals.SetAt(i, lit->literal());
2293 } else {
2294 compile_time_interpolation = false;
2295 break;
2296 }
2297 }
2298 if (compile_time_interpolation) {
2299 if (!IsResultNeeded(node)) {
2300 return;
2301 }
2302 // Build argument array to pass to the interpolation function.
2303 GrowableArray<const Object*> interpolate_arg;
2304 interpolate_arg.Add(&literals);
2305 const Array& kNoArgumentNames = Array::Handle();
2306 // Call the interpolation function.
2307 String& concatenated = String::ZoneHandle();
2308 concatenated ^= DartEntry::InvokeStatic(interpol_func,
2309 interpolate_arg,
2310 kNoArgumentNames);
2311 if (concatenated.IsUnhandledException()) {
2312 // TODO(hausner): Shouldn't we generate a throw?
2313 // Then remove unused CodeGenerator::ErrorMsg().
2314 ErrorMsg(node->token_pos(),
2315 "Exception thrown in CodeGenerator::VisitStringConcatNode");
2316 }
2317 ASSERT(!concatenated.IsNull());
2318 concatenated = String::NewSymbol(concatenated);
2319
2320 __ LoadObject(EAX, concatenated);
2321 __ pushl(EAX);
2322 return;
2323 }
2324
2325 // Could not concatenate at compile time, generate a call to
2326 // interpolation function.
2327 ArgumentListNode* interpol_arg = new ArgumentListNode(node->token_pos());
2328 interpol_arg->Add(node->values());
2329 node->values()->Visit(this);
2330 __ LoadObject(ECX, interpol_func);
2331 __ LoadObject(EDX, ArgumentsDescriptor(interpol_arg->length(),
2332 interpol_arg->names()));
2333 GenerateCall(node->token_pos(),
2334 &StubCode::CallStaticFunctionLabel(),
2335 PcDescriptors::kFuncCall);
2336 __ addl(ESP, Immediate(interpol_arg->length() * kWordSize));
2337 // Result is in EAX.
2338 if (IsResultNeeded(node)) {
2339 __ pushl(EAX);
2340 }
2341 }
2342
2343
2344 void CodeGenerator::VisitInstanceCallNode(InstanceCallNode* node) {
2345 const int number_of_arguments = node->arguments()->length() + 1;
2346 // Compute the receiver object and pass it as first argument to call.
2347 node->receiver()->Visit(this);
2348 // Now compute rest of the arguments to the call.
2349 node->arguments()->Visit(this);
2350 // Some method may be inlined using type feedback, therefore this may be a
2351 // deoptimization point.
2352 MarkDeoptPoint(node->id(), node->token_pos());
2353 const int kNumArgumentsChecked = 1;
2354 GenerateInstanceCall(node->id(),
2355 node->token_pos(),
2356 node->function_name(),
2357 number_of_arguments,
2358 node->arguments()->names(),
2359 kNumArgumentsChecked);
2360 // Result is in EAX.
2361 if (IsResultNeeded(node)) {
2362 __ pushl(EAX);
2363 }
2364 }
2365
2366
2367 void CodeGenerator::VisitStaticCallNode(StaticCallNode* node) {
2368 node->arguments()->Visit(this);
2369 __ LoadObject(ECX, node->function());
2370 __ LoadObject(EDX, ArgumentsDescriptor(node->arguments()->length(),
2371 node->arguments()->names()));
2372 GenerateCall(node->token_pos(),
2373 &StubCode::CallStaticFunctionLabel(),
2374 PcDescriptors::kFuncCall);
2375 __ addl(ESP, Immediate(node->arguments()->length() * kWordSize));
2376 // Result is in EAX.
2377 if (IsResultNeeded(node)) {
2378 __ pushl(EAX);
2379 }
2380 }
2381
2382
2383 void CodeGenerator::VisitClosureCallNode(ClosureCallNode* node) {
2384 // The spec states that the closure is evaluated before the arguments.
2385 // Preserve the current context, since it will be overridden by the closure
2386 // context during the call.
2387 __ pushl(CTX);
2388 // Compute the closure object and pass it as first argument to the stub.
2389 node->closure()->Visit(this);
2390 // Now compute the arguments to the call.
2391 node->arguments()->Visit(this);
2392 // Set up the number of arguments (excluding the closure) to the ClosureCall
2393 // stub which will setup the closure context and jump to the entrypoint of the
2394 // closure function (the function will be compiled if it has not already been
2395 // compiled).
2396 // NOTE: The stub accesses the closure before the parameter list.
2397 __ LoadObject(EDX, ArgumentsDescriptor(node->arguments()->length(),
2398 node->arguments()->names()));
2399 GenerateCall(node->token_pos(),
2400 &StubCode::CallClosureFunctionLabel(),
2401 PcDescriptors::kOther);
2402 __ addl(ESP, Immediate((node->arguments()->length() + 1) * kWordSize));
2403 // Restore the context.
2404 __ popl(CTX);
2405 // Result is in EAX.
2406 if (IsResultNeeded(node)) {
2407 __ pushl(EAX);
2408 }
2409 }
2410
2411
2412 // Pushes either the instantiator or null.
2413 // Pushes the type arguments of the instantiator on the stack.
2414 // Destroys EBX, ECX.
2415 void CodeGenerator::GenerateInstantiatorTypeArguments(intptr_t token_pos,
2416 bool push_instantiator) {
2417 if (push_instantiator) {
2418 const Immediate raw_null =
2419 Immediate(reinterpret_cast<intptr_t>(Object::null()));
2420 __ pushl(raw_null); // Initial instantiator.
2421 }
2422 const Class& instantiator_class = Class::Handle(
2423 parsed_function().function().owner());
2424 if (instantiator_class.NumTypeParameters() == 0) {
2425 // The type arguments are compile time constants.
2426 AbstractTypeArguments& type_arguments = AbstractTypeArguments::ZoneHandle();
2427 // TODO(regis): Temporary type should be allocated in new gen heap.
2428 Type& type = Type::Handle(
2429 Type::New(instantiator_class, type_arguments, token_pos));
2430 type ^= ClassFinalizer::FinalizeType(
2431 instantiator_class, type, ClassFinalizer::kFinalizeWellFormed);
2432 type_arguments = type.arguments();
2433 __ PushObject(type_arguments);
2434 } else {
2435 ASSERT(parsed_function().instantiator() != NULL);
2436 parsed_function().instantiator()->Visit(this);
2437 Function& outer_function =
2438 Function::Handle(parsed_function().function().raw());
2439 while (outer_function.IsLocalFunction()) {
2440 outer_function = outer_function.parent_function();
2441 }
2442 if (!outer_function.IsFactory()) {
2443 if (push_instantiator) {
2444 __ popl(EBX); // Pop instantiator.
2445 __ popl(ECX); // Discard null instantiator.
2446 __ pushl(EBX); // Replace it with the real one.
2447 } else {
2448 __ popl(EBX); // Pop instantiator.
2449 }
2450 // EBX: instantiator.
2451 // The instantiator is the receiver of the caller, which is not a factory.
2452 // The receiver cannot be null; extract its AbstractTypeArguments object.
2453 // Note that in the factory case, the instantiator is the first parameter
2454 // of the factory, i.e. already an AbstractTypeArguments object.
2455 intptr_t type_arguments_instance_field_offset =
2456 instantiator_class.type_arguments_instance_field_offset();
2457 ASSERT(type_arguments_instance_field_offset != Class::kNoTypeArguments);
2458 __ movl(EBX, FieldAddress(EBX, type_arguments_instance_field_offset));
2459 __ pushl(EBX);
2460 }
2461 }
2462 }
2463
2464
2465 // Pushes the type arguments on the stack in preparation of an allocation call.
2466 // If instantiate_type_arguments is true, the instantiated type arguments
2467 // are pushed on the stack (after an instantiation run time call, if necessary).
2468 // If instantiate_type_arguments is false, the (possibly uninstantiated) type
2469 // arguments are pushed on the stack, as well as the type arguments of the
2470 // instantiator (or the special kNoInstantiator Smi marker, if the type
2471 // arguments are instantiated).
2472 void CodeGenerator::GenerateTypeArguments(
2473 intptr_t node_id,
2474 intptr_t token_pos,
2475 const AbstractTypeArguments& type_arguments,
2476 bool instantiate_type_arguments) {
2477 const Immediate raw_null =
2478 Immediate(reinterpret_cast<intptr_t>(Object::null()));
2479 if (type_arguments.IsNull() || type_arguments.IsInstantiated()) {
2480 // The type arguments are instantiated.
2481 __ PushObject(type_arguments);
2482 if (!instantiate_type_arguments) {
2483 // The type arguments of the instantiator are not needed, since the
2484 // type arguments are instantiated.
2485 __ pushl(Immediate(Smi::RawValue(StubCode::kNoInstantiator)));
2486 }
2487 } else {
2488 // The type arguments are uninstantiated.
2489 const bool kPushInstantiator = false;
2490 GenerateInstantiatorTypeArguments(token_pos, kPushInstantiator);
2491 __ popl(EAX); // Pop instantiator.
2492 // EAX is the instantiator AbstractTypeArguments object (or null).
2493 // If the instantiator is null and if the type argument vector
2494 // instantiated from null becomes a vector of Dynamic, then use null as
2495 // the type arguments.
2496 Label type_arguments_instantiated;
2497 const intptr_t len = type_arguments.Length();
2498 if (type_arguments.IsRawInstantiatedRaw(len)) {
2499 __ cmpl(EAX, raw_null);
2500 __ j(EQUAL, &type_arguments_instantiated, Assembler::kNearJump);
2501 }
2502 // Instantiate non-null type arguments.
2503 if (type_arguments.IsUninstantiatedIdentity()) {
2504 // Check if the instantiator type argument vector is a TypeArguments of a
2505 // matching length and, if so, use it as the instantiated type_arguments.
2506 // No need to check RAX for null (again), because a null instance will
2507 // have the wrong class (Null instead of TypeArguments).
2508 Label type_arguments_uninstantiated;
2509 __ CompareClassId(EAX, kTypeArguments, ECX);
2510 __ j(NOT_EQUAL, &type_arguments_uninstantiated, Assembler::kNearJump);
2511 __ cmpl(FieldAddress(EAX, TypeArguments::length_offset()),
2512 Immediate(Smi::RawValue(len)));
2513 __ j(EQUAL, &type_arguments_instantiated, Assembler::kNearJump);
2514 __ Bind(&type_arguments_uninstantiated);
2515 }
2516 if (instantiate_type_arguments) {
2517 // A runtime call to instantiate the type arguments is required.
2518 __ PushObject(Object::ZoneHandle()); // Make room for the result.
2519 __ PushObject(type_arguments);
2520 __ pushl(EAX); // Push instantiator type arguments.
2521 GenerateCallRuntime(node_id,
2522 token_pos,
2523 kInstantiateTypeArgumentsRuntimeEntry);
2524 __ popl(EAX); // Pop instantiator type arguments.
2525 __ popl(EAX); // Pop uninstantiated type arguments.
2526 __ popl(EAX); // Pop instantiated type arguments.
2527 __ Bind(&type_arguments_instantiated);
2528 __ pushl(EAX); // Instantiated type arguments.
2529 } else {
2530 // The allocation stub will instantiate the type arguments.
2531 __ PushObject(type_arguments);
2532 __ pushl(EAX); // Instantiator type arguments.
2533 Label type_arguments_pushed;
2534 __ jmp(&type_arguments_pushed, Assembler::kNearJump);
2535
2536 __ Bind(&type_arguments_instantiated);
2537 __ pushl(EAX); // Instantiated type arguments.
2538 __ pushl(Immediate(Smi::RawValue(StubCode::kNoInstantiator)));
2539 __ Bind(&type_arguments_pushed);
2540 }
2541 }
2542 }
2543
2544
2545 void CodeGenerator::VisitConstructorCallNode(ConstructorCallNode* node) {
2546 if (node->constructor().IsFactory()) {
2547 const bool instantiate_type_arguments = true; // First argument to factory.
2548 GenerateTypeArguments(node->id(),
2549 node->token_pos(),
2550 node->type_arguments(),
2551 instantiate_type_arguments);
2552 // The top of stack is an instantiated AbstractTypeArguments object
2553 // (or null).
2554 int num_args = node->arguments()->length() + 1; // +1 to include type args.
2555 node->arguments()->Visit(this);
2556 // Call the factory.
2557 __ LoadObject(ECX, node->constructor());
2558 __ LoadObject(EDX, ArgumentsDescriptor(num_args,
2559 node->arguments()->names()));
2560 GenerateCall(node->token_pos(),
2561 &StubCode::CallStaticFunctionLabel(),
2562 PcDescriptors::kFuncCall);
2563 // Factory constructor returns object in EAX.
2564 __ addl(ESP, Immediate(num_args * kWordSize));
2565 if (IsResultNeeded(node)) {
2566 __ pushl(EAX);
2567 }
2568 return;
2569 }
2570
2571 const Class& cls = Class::ZoneHandle(node->constructor().owner());
2572 const bool requires_type_arguments = cls.HasTypeArguments();
2573 const bool instantiate_type_arguments = false; // Done in stub or runtime.
2574 if (requires_type_arguments) {
2575 GenerateTypeArguments(node->id(),
2576 node->token_pos(),
2577 node->type_arguments(),
2578 instantiate_type_arguments);
2579 }
2580
2581 // If cls is parameterized, the type arguments and the instantiator's
2582 // type arguments are on the stack.
2583 // In checked mode, if the type arguments are uninstantiated, they may need to
2584 // be checked against declared bounds at run time.
2585 Error& malformed_error = Error::Handle();
2586 if (FLAG_enable_type_checks &&
2587 requires_type_arguments &&
2588 !node->type_arguments().IsNull() &&
2589 !node->type_arguments().IsInstantiated() &&
2590 !node->type_arguments().IsWithinBoundsOf(cls,
2591 node->type_arguments(),
2592 &malformed_error)) {
2593 // The uninstantiated type arguments cannot be verified to be within their
2594 // bounds at compile time, so verify them at runtime.
2595 // Although the type arguments may be uninstantiated at compile time, they
2596 // may represent the identity vector and may be replaced by the instantiated
2597 // type arguments of the instantiator at run time.
2598 __ popl(ECX); // Pop instantiator type arguments.
2599 __ popl(EAX); // Pop type arguments.
2600
2601 // Push the result place holder initialized to NULL.
2602 __ PushObject(Object::ZoneHandle());
2603 __ pushl(Immediate(Smi::RawValue(node->token_pos())));
2604 __ PushObject(cls);
2605 __ pushl(EAX); // Push type arguments.
2606 __ pushl(ECX); // Push instantiator type arguments.
2607 GenerateCallRuntime(node->id(),
2608 node->token_pos(),
2609 kAllocateObjectWithBoundsCheckRuntimeEntry);
2610 __ popl(ECX); // Pop instantiator type arguments.
2611 __ popl(ECX); // Pop type arguments.
2612 __ popl(ECX); // Pop class.
2613 __ popl(ECX); // Pop source location.
2614 __ popl(EAX); // Pop new instance.
2615 } else {
2616 const Code& stub = Code::Handle(StubCode::GetAllocationStubForClass(cls));
2617 const ExternalLabel label(cls.ToCString(), stub.EntryPoint());
2618 GenerateCall(node->token_pos(), &label, PcDescriptors::kOther);
2619 if (requires_type_arguments) {
2620 __ popl(ECX); // Pop instantiator type arguments.
2621 __ popl(ECX); // Pop type arguments.
2622 }
2623 }
2624
2625 if (IsResultNeeded(node)) {
2626 __ pushl(EAX); // Set up return value from allocate.
2627 }
2628
2629 // First argument(this) for constructor call which follows.
2630 __ pushl(EAX);
2631 // Second argument is the implicit construction phase parameter.
2632 // Run both the constructor initializer list and the constructor body.
2633 __ pushl(Immediate(Smi::RawValue(Function::kCtorPhaseAll)));
2634
2635 // Now setup rest of the arguments for the constructor call.
2636 node->arguments()->Visit(this);
2637
2638 // Call the constructor.
2639 // +2 to include implicit receiver and phase arguments.
2640 int num_args = node->arguments()->length() + 2;
2641 __ LoadObject(ECX, node->constructor());
2642 __ LoadObject(EDX, ArgumentsDescriptor(num_args, node->arguments()->names()));
2643 GenerateCall(node->token_pos(),
2644 &StubCode::CallStaticFunctionLabel(),
2645 PcDescriptors::kFuncCall);
2646 // Constructors do not return any value.
2647
2648 // Pop out all the other arguments on the stack.
2649 __ addl(ESP, Immediate(num_args * kWordSize));
2650 }
2651
2652
2653 // Expects receiver on stack, returns result in EAX..
2654 void CodeGenerator::GenerateInstanceGetterCall(intptr_t node_id,
2655 intptr_t token_pos,
2656 const String& field_name) {
2657 const String& getter_name =
2658 String::ZoneHandle(Field::GetterSymbol(field_name));
2659 const int kNumberOfArguments = 1;
2660 const Array& kNoArgumentNames = Array::Handle();
2661 const int kNumArgumentsChecked = 1;
2662 GenerateInstanceCall(node_id,
2663 token_pos,
2664 getter_name,
2665 kNumberOfArguments,
2666 kNoArgumentNames,
2667 kNumArgumentsChecked);
2668 }
2669
2670
2671 // Call to the instance getter.
2672 void CodeGenerator::VisitInstanceGetterNode(InstanceGetterNode* node) {
2673 node->receiver()->Visit(this);
2674 MarkDeoptPoint(node->id(), node->token_pos());
2675 GenerateInstanceGetterCall(node->id(),
2676 node->token_pos(),
2677 node->field_name());
2678 if (IsResultNeeded(node)) {
2679 __ pushl(EAX);
2680 }
2681 }
2682
2683
2684 // Expects receiver and value on stack.
2685 void CodeGenerator::GenerateInstanceSetterCall(intptr_t node_id,
2686 intptr_t token_pos,
2687 const String& field_name) {
2688 const String& setter_name =
2689 String::ZoneHandle(Field::SetterSymbol(field_name));
2690 const int kNumberOfArguments = 2; // receiver + value.
2691 const Array& kNoArgumentNames = Array::Handle();
2692 const int kNumArgumentsChecked = 1;
2693 GenerateInstanceCall(node_id,
2694 token_pos,
2695 setter_name,
2696 kNumberOfArguments,
2697 kNoArgumentNames,
2698 kNumArgumentsChecked);
2699 }
2700
2701
2702 // The call to the instance setter implements the assignment to a field.
2703 // The result of the assignment to a field is the value being stored.
2704 void CodeGenerator::VisitInstanceSetterNode(InstanceSetterNode* node) {
2705 // Compute the receiver object and pass it as first argument to call.
2706 node->receiver()->Visit(this);
2707 node->value()->Visit(this);
2708 MarkDeoptPoint(node->id(), node->token_pos());
2709 if (IsResultNeeded(node)) {
2710 __ popl(EAX); // value.
2711 __ popl(EDX); // receiver.
2712 __ pushl(EAX); // Preserve value.
2713 __ pushl(EDX); // arg0: receiver.
2714 __ pushl(EAX); // arg1: value.
2715 }
2716 // It is not necessary to generate a type test of the assigned value here,
2717 // because the setter will check the type of its incoming arguments.
2718 GenerateInstanceSetterCall(node->id(),
2719 node->token_pos(),
2720 node->field_name());
2721 }
2722
2723
2724 // Return result in EAX.
2725 void CodeGenerator::GenerateStaticGetterCall(intptr_t token_pos,
2726 const Class& field_class,
2727 const String& field_name) {
2728 const String& getter_name = String::Handle(Field::GetterName(field_name));
2729 const Function& function =
2730 Function::ZoneHandle(field_class.LookupStaticFunction(getter_name));
2731 ASSERT(!function.IsNull());
2732 __ LoadObject(ECX, function);
2733 const int kNumberOfArguments = 0;
2734 const Array& kNoArgumentNames = Array::Handle();
2735 __ LoadObject(EDX, ArgumentsDescriptor(kNumberOfArguments, kNoArgumentNames));
2736 GenerateCall(token_pos,
2737 &StubCode::CallStaticFunctionLabel(),
2738 PcDescriptors::kFuncCall);
2739 // No arguments were pushed, hence nothing to pop.
2740 }
2741
2742
2743 // Call to static getter.
2744 void CodeGenerator::VisitStaticGetterNode(StaticGetterNode* node) {
2745 GenerateStaticGetterCall(node->token_pos(),
2746 node->cls(),
2747 node->field_name());
2748 // Result is in EAX.
2749 if (IsResultNeeded(node)) {
2750 __ pushl(EAX);
2751 }
2752 }
2753
2754
2755 // Expects value on stack.
2756 void CodeGenerator::GenerateStaticSetterCall(intptr_t token_pos,
2757 const Class& field_class,
2758 const String& field_name) {
2759 const String& setter_name = String::Handle(Field::SetterName(field_name));
2760 const Function& function =
2761 Function::ZoneHandle(field_class.LookupStaticFunction(setter_name));
2762 ASSERT(!function.IsNull());
2763 __ LoadObject(ECX, function);
2764 const int kNumberOfArguments = 1; // value.
2765 const Array& kNoArgumentNames = Array::Handle();
2766 __ LoadObject(EDX, ArgumentsDescriptor(kNumberOfArguments, kNoArgumentNames));
2767 GenerateCall(token_pos,
2768 &StubCode::CallStaticFunctionLabel(),
2769 PcDescriptors::kFuncCall);
2770 __ addl(ESP, Immediate(kNumberOfArguments * kWordSize));
2771 }
2772
2773
2774 // The call to static setter implements assignment to a static field.
2775 // The result of the assignment is the value being stored.
2776 void CodeGenerator::VisitStaticSetterNode(StaticSetterNode* node) {
2777 node->value()->Visit(this);
2778 if (IsResultNeeded(node)) {
2779 // Preserve the original value when returning from setter.
2780 __ movl(EAX, Address(ESP, 0));
2781 __ pushl(EAX); // arg0: value.
2782 }
2783 // It is not necessary to generate a type test of the assigned value here,
2784 // because the setter will check the type of its incoming arguments.
2785 GenerateStaticSetterCall(node->token_pos(),
2786 node->cls(),
2787 node->field_name());
2788 }
2789
2790
2791 void CodeGenerator::VisitNativeBodyNode(NativeBodyNode* node) {
2792 intptr_t argument_count = node->argument_count();
2793 if (node->is_native_instance_closure()) {
2794 argument_count += 1;
2795 }
2796
2797 // Push the result place holder initialized to NULL.
2798 __ PushObject(Object::ZoneHandle());
2799 // Pass a pointer to the first argument in EAX.
2800 if (!node->has_optional_parameters() && !node->is_native_instance_closure()) {
2801 __ leal(EAX, Address(EBP, (1 + argument_count) * kWordSize));
2802 } else {
2803 __ leal(EAX,
2804 Address(EBP, ParsedFunction::kFirstLocalSlotIndex * kWordSize));
2805 }
2806 __ movl(ECX, Immediate(reinterpret_cast<uword>(node->native_c_function())));
2807 __ movl(EDX, Immediate(argument_count));
2808 GenerateCall(node->token_pos(),
2809 &StubCode::CallNativeCFunctionLabel(),
2810 PcDescriptors::kOther);
2811 // Result is on the stack.
2812 if (!IsResultNeeded(node)) {
2813 __ popl(EAX);
2814 }
2815 }
2816
2817
2818 void CodeGenerator::VisitCatchClauseNode(CatchClauseNode* node) {
2819 // NOTE: The implicit variables ':saved_context', ':exception_var'
2820 // and ':stacktrace_var' can never be captured variables.
2821 // Restore CTX from local variable ':saved_context'.
2822 GenerateLoadVariable(CTX, node->context_var());
2823
2824 // Restore ESP from EBP as we are coming from a throw and the code for
2825 // popping arguments has not been run.
2826 ASSERT(locals_space_size() >= 0);
2827 intptr_t offset_size = -locals_space_size() + kLocalsOffsetFromFP;
2828 __ leal(ESP, Address(EBP, offset_size));
2829
2830 // The JumpToExceptionHandler trampoline code sets up
2831 // - the exception object in EAX (kExceptionObjectReg)
2832 // - the stacktrace object in register EDX (kStackTraceObjectReg)
2833 // We now setup the exception object and the trace object
2834 // so that the handler code has access to these objects.
2835 GenerateStoreVariable(node->exception_var(),
2836 kExceptionObjectReg,
2837 kNoRegister);
2838 GenerateStoreVariable(node->stacktrace_var(),
2839 kStackTraceObjectReg,
2840 kNoRegister);
2841
2842 // Now generate code for the catch handler block.
2843 node->VisitChildren(this);
2844 }
2845
2846
2847 void CodeGenerator::VisitTryCatchNode(TryCatchNode* node) {
2848 CodeGeneratorState codegen_state(this);
2849 int outer_try_index = state()->try_index();
2850 // We are about to generate code for a new try block, generate an
2851 // unique 'try index' for this block and set that try index in
2852 // the code generator state.
2853 int try_index = generate_next_try_index();
2854 state()->set_try_index(try_index);
2855 exception_handlers_list_->AddHandler(try_index, -1);
2856
2857 // Preserve CTX into local variable '%saved_context'.
2858 GenerateStoreVariable(node->context_var(), CTX, kNoRegister);
2859
2860 node->try_block()->Visit(this);
2861
2862 // We are done generating code for the try block.
2863 ASSERT(state()->try_index() > CatchClauseNode::kInvalidTryIndex);
2864 ASSERT(try_index == state()->try_index());
2865 state()->set_try_index(outer_try_index);
2866
2867 CatchClauseNode* catch_block = node->catch_block();
2868 if (catch_block != NULL) {
2869 // Jump over the catch handler block, when exceptions are thrown we
2870 // will end up at the next instruction.
2871 __ jmp(node->end_catch_label()->continue_label());
2872
2873 // Set the corresponding try index for this catch block so
2874 // that we can set the appropriate handler pc when we generate
2875 // code for this catch block.
2876 catch_block->set_try_index(try_index);
2877
2878 // Set the handler pc for this try index in the exception handler
2879 // table.
2880 exception_handlers_list_->SetPcOffset(try_index, assembler_->CodeSize());
2881
2882 // Generate code for the catch block.
2883 catch_block->Visit(this);
2884
2885 // Bind the end of catch blocks label here.
2886 __ Bind(node->end_catch_label()->continue_label());
2887 }
2888
2889 // Generate code for the finally block if one exists.
2890 if (node->finally_block() != NULL) {
2891 node->finally_block()->Visit(this);
2892 }
2893 }
2894
2895
2896 void CodeGenerator::VisitThrowNode(ThrowNode* node) {
2897 node->exception()->Visit(this);
2898 // Exception object is on TOS.
2899 if (node->stacktrace() != NULL) {
2900 node->stacktrace()->Visit(this);
2901 GenerateCallRuntime(node->id(), node->token_pos(), kReThrowRuntimeEntry);
2902 } else {
2903 GenerateCallRuntime(node->id(), node->token_pos(), kThrowRuntimeEntry);
2904 }
2905 // We should never return here.
2906 __ int3();
2907 }
2908
2909
2910 void CodeGenerator::VisitInlinedFinallyNode(InlinedFinallyNode* node) {
2911 int try_index = state()->try_index();
2912 if (try_index >= 0) {
2913 // We are about to generate code for an inlined finally block. Exceptions
2914 // thrown in this block of code should be treated as though they are
2915 // thrown not from the current try block but the outer try block if any.
2916 // the code generator state.
2917 state()->set_try_index((try_index - 1));
2918 }
2919
2920 // Restore CTX from local variable ':saved_context'.
2921 GenerateLoadVariable(CTX, node->context_var());
2922 node->finally_block()->Visit(this);
2923
2924 if (try_index >= 0) {
2925 state()->set_try_index(try_index);
2926 }
2927 }
2928
2929
2930 void CodeGenerator::GenerateCall(intptr_t token_pos,
2931 const ExternalLabel* ext_label,
2932 PcDescriptors::Kind desc_kind) {
2933 __ call(ext_label);
2934 AddCurrentDescriptor(desc_kind, AstNode::kNoId, token_pos);
2935 }
2936
2937
2938 void CodeGenerator::GenerateCallRuntime(intptr_t node_id,
2939 intptr_t token_pos,
2940 const RuntimeEntry& entry) {
2941 __ CallRuntime(entry);
2942 AddCurrentDescriptor(PcDescriptors::kOther, node_id, token_pos);
2943 }
2944
2945
2946 void CodeGenerator::MarkDeoptPoint(intptr_t node_id,
2947 intptr_t token_pos) {
2948 ASSERT(node_id != AstNode::kNoId);
2949 AddCurrentDescriptor(PcDescriptors::kDeopt, node_id, token_pos);
2950 }
2951
2952
2953 // Uses current pc position and try-index.
2954 void CodeGenerator::AddCurrentDescriptor(PcDescriptors::Kind kind,
2955 intptr_t node_id,
2956 intptr_t token_pos) {
2957 pc_descriptors_list_->AddDescriptor(kind,
2958 assembler_->CodeSize(),
2959 node_id,
2960 token_pos,
2961 state()->try_index());
2962 }
2963
2964
2965 void CodeGenerator::ErrorMsg(intptr_t token_pos, const char* format, ...) {
2966 va_list args;
2967 va_start(args, format);
2968 const Class& cls = Class::Handle(parsed_function_.function().owner());
2969 const Script& script = Script::Handle(cls.script());
2970 const Error& error = Error::Handle(
2971 Parser::FormatError(script, token_pos, "Error", format, args));
2972 va_end(args);
2973 Isolate::Current()->long_jump_base()->Jump(1, error);
2974 UNREACHABLE();
2975 }
2976
2977 } // namespace dart
2978
2979 #endif // defined TARGET_ARCH_IA32
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698