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

Side by Side Diff: runtime/vm/opt_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, 6 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) 2011, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32.
6 #if defined(TARGET_ARCH_IA32)
7
8 #include "vm/opt_code_generator.h"
9
10 #include "vm/assembler_macros.h"
11 #include "vm/ast_printer.h"
12 #include "vm/object.h"
13 #include "vm/object_store.h"
14 #include "vm/resolver.h"
15 #include "vm/stub_code.h"
16
17 namespace dart {
18
19 #define __ assembler_->
20
21 DEFINE_FLAG(bool, trace_optimization, false, "Trace optimizations.");
22 DECLARE_FLAG(bool, enable_type_checks);
23
24
25 // Property list to be used in CodeGenInfo. Each property has a setter
26 // and a getter of specified type and name.
27 // (name, type, default)
28 #define PROPERTY_LIST(V) \
29 V(is_temp, bool, false) \
30 V(allow_temp, bool, false) \
31 V(true_label, Label*, NULL) \
32 V(false_label, Label*, NULL) \
33 V(labels_used, bool, false) \
34 V(request_result_in_eax, bool, false) \
35 V(result_returned_in_eax, bool, false) \
36 V(fallthrough_label, Label*, NULL) \
37 V(is_class, const Class*, &Class::ZoneHandle()) \
38
39
40 // Class holding information being passed from source to destination.
41 // Add needed properties in the PROPERTY_LIST above.
42 class CodeGenInfo : public ValueObject {
43 public:
44 explicit CodeGenInfo(AstNode* node)
45 : node_(node), data_(4) {
46 ASSERT(node != NULL);
47 ASSERT(node->info() == NULL);
48 node->set_info(this);
49 }
50
51 ~CodeGenInfo() {
52 ASSERT(node_->info() == this);
53 node_->set_info(NULL);
54 }
55
56 bool IsClass(const Class& cls) const {
57 return is_class()->raw() == cls.raw();
58 }
59
60 #define GETTER(name, type, default) \
61 type name() const { \
62 Pair* p = Get(k_##name); \
63 return p == NULL ? default : p->name; \
64 }
65 PROPERTY_LIST(GETTER)
66 #undef GETTER
67
68 #define SETTER(name, type, default) \
69 void set_##name(type value) { \
70 ASSERT(Get(k_##name) == NULL); \
71 Pair p; \
72 p.kind = k_##name; \
73 p.name = value; \
74 data_.Add(p); \
75 }
76 PROPERTY_LIST(SETTER)
77 #undef SETTER
78
79 private:
80 enum Kind {
81 #define DEFINE_KIND(name, type, value) k_##name,
82 PROPERTY_LIST(DEFINE_KIND)
83 #undef DEFINE_KIND
84 };
85 struct Pair {
86 Kind kind;
87 union {
88 #define UNION_ELEMENTS(name, type, value) type name;
89 PROPERTY_LIST(UNION_ELEMENTS)
90 #undef UNION_ELEMENTS
91 };
92 };
93
94 Pair* Get(Kind kind) const {
95 for (int i = 0; i < data_.length(); i++) {
96 if (data_[i].kind == kind) {
97 return &data_[i];
98 }
99 }
100 return NULL;
101 }
102 AstNode* node_;
103 GrowableArray<Pair> data_;
104 DISALLOW_COPY_AND_ASSIGN(CodeGenInfo);
105 };
106
107
108 // Code that calls the deoptimizer, emitted as deferred code (out of line).
109 // Specify the corresponding 'node' and the registers that need to
110 // be pushed for the deoptimization point in unoptimized code.
111 class DeoptimizationBlob : public ZoneAllocated {
112 public:
113 DeoptimizationBlob(AstNode* node, DeoptReasonId deopt_reason_id)
114 : node_(node),
115 registers_(2),
116 label_(),
117 deopt_reason_id_(deopt_reason_id) {}
118
119 void Push(Register reg) { registers_.Add(reg); }
120
121 void Generate(OptimizingCodeGenerator* codegen) {
122 codegen->assembler()->Bind(&label_);
123 for (int i = 0; i < registers_.length(); i++) {
124 codegen->assembler()->pushl(registers_[i]);
125 }
126 codegen->assembler()->movl(EAX, Immediate(Smi::RawValue(deopt_reason_id_)));
127 codegen->CallDeoptimize(node_->id(), node_->token_pos());
128 #if defined(DEBUG)
129 // Check that deoptimization point exists in unoptimized code.
130 const Code& unoptimized_code =
131 Code::Handle(codegen->parsed_function().function().unoptimized_code());
132 ASSERT(!unoptimized_code.IsNull());
133 uword continue_at_pc =
134 unoptimized_code.GetDeoptPcAtNodeId(node_->id());
135 ASSERT(continue_at_pc != 0);
136 #endif // DEBUG
137 }
138
139 // Jump to this label to deoptimize.
140 Label* label() { return &label_; }
141
142 private:
143 const AstNode* node_;
144 GrowableArray<Register> registers_;
145 Label label_;
146 DeoptReasonId deopt_reason_id_;
147
148 DISALLOW_COPY_AND_ASSIGN(DeoptimizationBlob);
149 };
150
151 // TODO(srdjan): Add String_charCodeAt, String_hashCode.
152
153 #define RECOGNIZED_LIST(V) \
154 V(ObjectArray, get:length, ObjectArrayLength) \
155 V(GrowableObjectArray, get:length, GrowableArrayLength) \
156 V(StringBase, get:length, StringBaseLength) \
157 V(IntegerImplementation, toDouble, IntegerToDouble) \
158 V(Double, toDouble, DoubleToDouble) \
159 V(Math, sqrt, MathSqrt) \
160
161 // Class that recognizes the name and owner of a function and returns the
162 // corresponding enum. See RECOGNIZED_LIST above for list of recognizable
163 // functions.
164 class Recognizer : public AllStatic {
165 public:
166 enum Kind {
167 kUnknown,
168 #define DEFINE_ENUM_LIST(class_name, function_name, enum_name) k##enum_name,
169 RECOGNIZED_LIST(DEFINE_ENUM_LIST)
170 #undef DEFINE_ENUM_LIST
171 };
172
173 // TODO(srdjan): Check that the library is the coreimpl one.
174 static Kind RecognizeKind(const Function& function) {
175 const String& recognize_name = String::Handle(function.name());
176 const String& recognize_class =
177 String::Handle(Class::Handle(function.owner()).Name());
178 String& test_function_name = String::Handle();
179 String& test_class_name = String::Handle();
180 #define RECOGNIZE_FUNCTION(class_name, function_name, enum_name) \
181 test_function_name = String::NewSymbol(#function_name); \
182 test_class_name = String::NewSymbol(#class_name); \
183 if (recognize_name.Equals(test_function_name) && \
184 recognize_class.Equals(test_class_name)) { \
185 return k##enum_name; \
186 }
187 RECOGNIZED_LIST(RECOGNIZE_FUNCTION)
188 #undef RECOGNIZE_FUNCTION
189 return kUnknown;
190 }
191
192 static const char* KindToCString(Kind kind) {
193 #define KIND_TO_STRING(class_name, function_name, enum_name) \
194 if (kind == k##enum_name) return #enum_name;
195 RECOGNIZED_LIST(KIND_TO_STRING)
196 #undef KIND_TO_STRING
197 return "?";
198 }
199
200 private:
201 DISALLOW_COPY_AND_ASSIGN(Recognizer);
202 };
203
204
205 // Maintain classes of locals as defined by a store to that local.
206 // A simple initial implementation, memorizes last typed stores. Does not
207 // scale well for large code pieces. This will be replaced by SSA based
208 // type propagation.
209 class ClassesForLocals : public ZoneAllocated {
210 public:
211 ClassesForLocals() : classes_(), locals_() {}
212 void SetLocalType(const LocalVariable& local, const Class& cls) {
213 classes_.Add(&cls);
214 locals_.Add(&local);
215 }
216 // If no type is stored/known, we return a null class in 'cls'.
217 void GetLocalClass(const LocalVariable& local, const Class** cls) const {
218 for (intptr_t i = locals_.length() - 1; i >=0; i--) {
219 if (locals_[i]->Equals(local)) {
220 *cls = classes_[i];
221 return;
222 }
223 }
224 *cls = &Class::ZoneHandle();
225 }
226
227 void Clear() {
228 classes_.Clear();
229 locals_.Clear();
230 }
231
232 private:
233 GrowableArray<const Class*> classes_;
234 GrowableArray<const LocalVariable*> locals_;
235
236 DISALLOW_COPY_AND_ASSIGN(ClassesForLocals);
237 };
238
239
240 OptimizingCodeGenerator::OptimizingCodeGenerator(
241 Assembler* assembler, const ParsedFunction& parsed_function)
242 : CodeGenerator(assembler, parsed_function),
243 deoptimization_blobs_(4),
244 classes_for_locals_(NULL),
245 smi_class_(Class::ZoneHandle(Isolate::Current()->object_store()
246 ->smi_class())),
247 double_class_(Class::ZoneHandle(Isolate::Current()->object_store()
248 ->double_class())),
249 growable_object_array_class_(Class::ZoneHandle(Isolate::Current()
250 ->object_store()->growable_object_array_class())) {
251 ASSERT(parsed_function.function().is_optimizable());
252 }
253
254
255 void OptimizingCodeGenerator::InitGenerator() {
256 CodeGenerator::InitGenerator();
257 classes_for_locals_ = new ClassesForLocals();
258 }
259
260
261 DeoptimizationBlob*
262 OptimizingCodeGenerator::AddDeoptimizationBlob(AstNode* node,
263 DeoptReasonId reason_id) {
264 DeoptimizationBlob* d = new DeoptimizationBlob(node, reason_id);
265 deoptimization_blobs_.Add(d);
266 return d;
267 }
268
269
270 DeoptimizationBlob*
271 OptimizingCodeGenerator::AddDeoptimizationBlob(AstNode* node,
272 Register reg,
273 DeoptReasonId reason_id) {
274 DeoptimizationBlob* d = AddDeoptimizationBlob(node, reason_id);
275 d->Push(reg);
276 return d;
277 }
278
279
280 DeoptimizationBlob*
281 OptimizingCodeGenerator::AddDeoptimizationBlob(AstNode* node,
282 Register reg1,
283 Register reg2,
284 DeoptReasonId reason_id) {
285 DeoptimizationBlob* d = AddDeoptimizationBlob(node, reason_id);
286 d->Push(reg1);
287 d->Push(reg2);
288 return d;
289 }
290
291
292 DeoptimizationBlob*
293 OptimizingCodeGenerator::AddDeoptimizationBlob(AstNode* node,
294 Register reg1,
295 Register reg2,
296 Register reg3,
297 DeoptReasonId reason_id) {
298 DeoptimizationBlob* d = AddDeoptimizationBlob(node, reason_id);
299 d->Push(reg1);
300 d->Push(reg2);
301 d->Push(reg3);
302 return d;
303 }
304
305
306 void OptimizingCodeGenerator::GenerateDeferredCode() {
307 CodeGenerator::GenerateDeferredCode();
308 for (int i = 0; i < deoptimization_blobs_.length(); i++) {
309 deoptimization_blobs_[i]->Generate(this);
310 }
311 }
312
313
314 bool OptimizingCodeGenerator::IsResultInEaxRequested(AstNode* node) const {
315 return (node->info() != NULL) && node->info()->request_result_in_eax();
316 }
317
318
319 static const ZoneGrowableArray<const Class*>*
320 CollectedClassesAtNode(AstNode* node) {
321 ZoneGrowableArray<const Class*>* result =
322 new ZoneGrowableArray<const Class*>();
323 const ICData& ic_data = node->ic_data();
324 if (ic_data.NumberOfChecks() == 0) {
325 return result;
326 }
327 ASSERT(ic_data.num_args_tested() == 1);
328 Function& target = Function::Handle();
329 for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
330 intptr_t class_id;
331 ic_data.GetOneClassCheckAt(i, &class_id, &target);
332 result->Add(&Class::ZoneHandle(
333 Isolate::Current()->class_table()->At(class_id)));
334 }
335 return result;
336 }
337
338
339 // Debugging helper function. TODO(srdjan): Remove
340 void OptimizingCodeGenerator::PrintCollectedClasses(AstNode* node) {
341 const ICData& ic_data = node->ic_data();
342 OS::Print("Collected classes id %d num: %d\n",
343 node->id(), ic_data.NumberOfChecks());
344 }
345
346
347 void OptimizingCodeGenerator::TraceOpt(AstNode* node, const char* message) {
348 if (FLAG_trace_optimization) {
349 OS::Print("Opt node ix: %d; %s\n", node->token_pos(), message);
350 }
351 }
352
353
354 void OptimizingCodeGenerator::TraceNotOpt(AstNode* node, const char* message) {
355 if (FLAG_trace_optimization) {
356 OS::Print("NOTOpt node ix: %d; %s: ", node->token_pos(), message);
357 AstPrinter::PrintNode(node);
358 OS::Print("\n");
359 }
360 }
361
362
363 void OptimizingCodeGenerator::CallDeoptimize(intptr_t node_id,
364 intptr_t token_pos) {
365 __ call(&StubCode::DeoptimizeLabel());
366 AddCurrentDescriptor(PcDescriptors::kOther, node_id, token_pos);
367 #if defined(DEBUG)
368 __ int3();
369 #endif
370 }
371
372
373 // Quick loads do not clobber registers.
374 static bool IsQuickLoad(AstNode* node) {
375 if (node->IsLoadLocalNode() && (!node->AsLoadLocalNode()->HasPseudo())) {
376 return true;
377 }
378 return node->IsLiteralNode();
379 }
380
381
382 // Method is closely tied to "VisitLoadTwo".
383 void OptimizingCodeGenerator::VisitLoadOne(AstNode* node, Register reg) {
384 if (!IsQuickLoad(node)) {
385 node->Visit(this);
386 __ popl(reg);
387 return;
388 }
389 if (node->IsLoadLocalNode()) {
390 LoadLocalNode* local_node = node->AsLoadLocalNode();
391 ASSERT(local_node != NULL);
392 GenerateLoadVariable(reg, local_node->local());
393 if (node->info() != NULL) {
394 const Class* cls = NULL;
395 classes_for_locals_->GetLocalClass(local_node->local(), &cls);
396 if (cls != NULL) {
397 node->info()->set_is_class(cls);
398 }
399 }
400 return;
401 }
402 if (node->IsLiteralNode()) {
403 LiteralNode* literal_node = node->AsLiteralNode();
404 ASSERT(literal_node != NULL);
405 __ LoadObject(reg, literal_node->literal());
406 if (node->info() != NULL) {
407 const Object& literal = literal_node->literal();
408 if (literal.IsSmi()) {
409 node->info()->set_is_class(&smi_class_);
410 } else if (literal.IsDouble()) {
411 node->info()->set_is_class(&double_class_);
412 }
413 }
414 return;
415 }
416 UNREACHABLE();
417 }
418
419
420 // Method is closely tied to "VisitLoadOne".
421 void OptimizingCodeGenerator::VisitLoadTwo(AstNode* left,
422 AstNode* right,
423 Register left_reg,
424 Register right_reg) {
425 ASSERT(left_reg != right_reg);
426 if (IsQuickLoad(right)) {
427 #if defined(DEBUG)
428 // Verify that left_reg does not get clobbered by VisitLoadOne(right, ...).
429 VisitLoadOne(left, left_reg);
430 __ pushl(left_reg);
431 VisitLoadOne(right, right_reg);
432 __ cmpl(left_reg, Address(ESP, 0));
433 Label ok;
434 __ j(EQUAL, &ok, Assembler::kNearJump);
435 __ Stop("Internal error at VisitLoadTwo");
436 __ Bind(&ok);
437 __ popl(left_reg);
438 #else
439 VisitLoadOne(left, left_reg);
440 VisitLoadOne(right, right_reg);
441 #endif
442 return;
443 }
444 left->Visit(this);
445 VisitLoadOne(right, right_reg);
446 __ popl(left_reg);
447 }
448
449
450 void OptimizingCodeGenerator::VisitLiteralNode(LiteralNode* node) {
451 if (!IsResultNeeded(node)) return;
452 const Object& literal = node->literal();
453 if (literal.IsSmi()) {
454 if (node->info() != NULL) {
455 node->info()->set_is_class(&smi_class_);
456 }
457 if (IsResultInEaxRequested(node)) {
458 __ movl(EAX, Immediate(reinterpret_cast<int32_t>(literal.raw())));
459 node->info()->set_result_returned_in_eax(true);
460 } else {
461 __ pushl(Immediate(reinterpret_cast<int32_t>(literal.raw())));
462 }
463 } else {
464 if ((node->info() != NULL) && literal.IsDouble()) {
465 node->info()->set_is_class(&double_class_);
466 }
467 if (IsResultInEaxRequested(node)) {
468 __ LoadObject(EAX, literal);
469 node->info()->set_result_returned_in_eax(true);
470 } else {
471 __ PushObject(literal);
472 }
473 }
474 }
475
476
477 void OptimizingCodeGenerator::VisitLoadLocalNode(LoadLocalNode* node) {
478 if (node->HasPseudo()) {
479 node->pseudo()->Visit(this);
480 __ popl(EAX);
481 }
482 if (!IsResultNeeded(node)) return;
483 if (IsResultInEaxRequested(node)) {
484 GenerateLoadVariable(EAX, node->local());
485 node->info()->set_result_returned_in_eax(true);
486 } else {
487 GeneratePushVariable(node->local(), EAX);
488 }
489 if (node->info() != NULL) {
490 const Class* cls = NULL;
491 classes_for_locals_->GetLocalClass(node->local(), &cls);
492 if (cls != NULL) {
493 node->info()->set_is_class(cls);
494 }
495 }
496 }
497
498
499 void OptimizingCodeGenerator::HandleResult(AstNode* node, Register result_reg) {
500 if (IsResultNeeded(node)) {
501 if (IsResultInEaxRequested(node)) {
502 if (result_reg != EAX) {
503 __ movl(EAX, result_reg);
504 }
505 node->info()->set_result_returned_in_eax(true);
506 } else {
507 __ pushl(result_reg);
508 }
509 }
510 }
511
512
513 void OptimizingCodeGenerator::VisitStoreLocalNode(StoreLocalNode* node) {
514 if (FLAG_enable_type_checks) {
515 CodeGenerator::VisitStoreLocalNode(node);
516 classes_for_locals_->SetLocalType(node->local(), Class::ZoneHandle());
517 return;
518 }
519 CodeGenInfo value_info(node->value());
520 value_info.set_allow_temp(false);
521 value_info.set_request_result_in_eax(true);
522 node->value()->Visit(this);
523 if (!value_info.result_returned_in_eax()) {
524 __ popl(EAX);
525 }
526 CodeGenerator::GenerateStoreVariable(node->local(), EAX, EDX);
527 HandleResult(node, EAX);
528 classes_for_locals_->SetLocalType(node->local(), *value_info.is_class());
529 }
530
531
532 static bool NodeHasBothReceiverClasses(AstNode* node,
533 const Class& cls1,
534 const Class& cls2) {
535 ASSERT(node != NULL);
536 ASSERT(!cls1.IsNull() && !cls2.IsNull());
537 const ICData& ic_data = node->ic_data();
538 bool cls1_found = false;
539 bool cls2_found = false;
540 for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
541 GrowableArray<intptr_t> class_ids;
542 Function& target = Function::Handle();
543 ic_data.GetCheckAt(i, &class_ids, &target);
544 if (!class_ids.is_empty()) {
545 if (class_ids[0] == cls1.id()) {
546 cls1_found = true;
547 }
548 if (class_ids[0] == cls2.id()) {
549 cls2_found = true;
550 }
551 if (cls1_found && cls2_found) {
552 return true;
553 }
554 }
555 }
556 return false;
557 }
558
559
560 // Look only at the first class in all check groups. Returns true if all
561 // receiver classes are 'cls'.
562 static bool NodeHasClassAt(AstNode* node,
563 const Class& cls,
564 intptr_t arg_index) {
565 ASSERT(node != NULL);
566 ASSERT(!cls.IsNull());
567 const ICData& ic_data = node->ic_data();
568 if (ic_data.NumberOfChecks() == 0) {
569 return false;
570 }
571 ASSERT(ic_data.num_args_tested() > arg_index);
572 for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
573 GrowableArray<intptr_t> class_ids;
574 Function& target = Function::Handle();
575 ic_data.GetCheckAt(i, &class_ids, &target);
576 if (class_ids.is_empty()) {
577 return false;
578 }
579 if (class_ids[arg_index] != cls.id()) {
580 return false;
581 }
582 }
583 return true;
584 }
585
586
587 // IC data may have only one check, and it has to contain the two classes in
588 // specified order.
589 static bool NodeHasTwoClasses(AstNode* node,
590 const Class& cls0,
591 const Class& cls1) {
592 ASSERT(node != NULL);
593 ASSERT(!cls0.IsNull() && !cls1.IsNull());
594 const ICData& ic_data = node->ic_data();
595 ASSERT(ic_data.num_args_tested() == 2);
596 if (ic_data.NumberOfChecks() != 1) {
597 return false;
598 }
599 Function& target = Function::Handle();
600 GrowableArray<intptr_t> class_ids;
601 ic_data.GetCheckAt(0, &class_ids, &target);
602 if ((cls0.id() == class_ids[0]) && (cls1.id() == class_ids[1])) {
603 return true;
604 }
605 return false;
606 }
607
608
609 // SHL: Implement with slow case so that it works both with Smi and Mint types.
610 // Result is in EAX. Mangles ECX, EBX, EDX.
611 void OptimizingCodeGenerator::GenerateSmiShiftBinaryOp(BinaryOpNode* node) {
612 if (node->kind() == Token::kSHR) {
613 // TODO(srdjan): Implement for Mint?
614 DeoptimizationBlob* deopt_blob =
615 AddDeoptimizationBlob(node, EAX, ECX, kDeoptSAR);
616 CodeGenInfo left_info(node->left());
617 CodeGenInfo right_info(node->right());
618 // EAX: value to shift, ECX: amount to shift.
619 VisitLoadTwo(node->left(), node->right(), EAX, ECX);
620 if (!left_info.IsClass(smi_class_) || !right_info.IsClass(smi_class_)) {
621 // Check if both Smi.
622 __ movl(EBX, EAX);
623 __ orl(EBX, ECX);
624 __ testl(EBX, Immediate(kSmiTagMask));
625 __ j(NOT_ZERO, deopt_blob->label());
626 PropagateBackLocalClass(node->left(), smi_class_);
627 PropagateBackLocalClass(node->right(), smi_class_);
628 }
629 __ cmpl(ECX, Immediate(0));
630 __ j(LESS, deopt_blob->label());
631 Immediate count_limit = Immediate(0x1F);
632 __ SmiUntag(ECX);
633 __ cmpl(ECX, count_limit);
634 Label shift_count_ok;
635 __ j(LESS_EQUAL, &shift_count_ok, Assembler::kNearJump);
636 __ movl(ECX, count_limit);
637 __ Bind(&shift_count_ok);
638 // Shift amount must be in ECX.
639 __ SmiUntag(EAX); // Value.
640 __ sarl(EAX, ECX);
641 __ SmiTag(EAX);
642 return;
643 }
644 ASSERT(node->kind() == Token::kSHL);
645 if (node->right()->IsLiteralNode() &&
646 node->right()->AsLiteralNode()->literal().IsSmi()) {
647 Label done;
648 // Shift count is a Smi literal.
649 Smi& smi = Smi::Handle();
650 smi ^= node->right()->AsLiteralNode()->literal().raw();
651 if (smi.Value() < Smi::kBits) {
652 Label slow_case;
653 CodeGenInfo left_info(node->left());
654 VisitLoadOne(node->left(), EAX);
655 if (!left_info.IsClass(smi_class_)) {
656 __ testl(EAX, Immediate(kSmiTagMask));
657 __ j(NOT_ZERO, &slow_case, Assembler::kNearJump); // left not smi
658 }
659 // Overflow test.
660 __ movl(EBX, EAX);
661 Immediate imm(smi.Value());
662 __ shll(EBX, imm);
663 __ sarl(EBX, imm);
664 __ cmpl(EAX, EBX);
665 __ j(NOT_EQUAL, &slow_case, Assembler::kNearJump); // Overflow.
666 __ shll(EAX, imm); // Shift for result now we know there is no overflow.
667 __ jmp(&done);
668 __ Bind(&slow_case);
669 __ pushl(EAX);
670 __ pushl(Immediate(reinterpret_cast<int32_t>(smi.raw())));
671 const int number_of_arguments = 2;
672 const Array& no_optional_argument_names = Array::Handle();
673 GenerateCheckedInstanceCalls(node,
674 node->left(),
675 node->token_pos(),
676 number_of_arguments,
677 no_optional_argument_names);
678 __ Bind(&done);
679 return;
680 }
681 }
682
683 Label slow_case, done;
684 CodeGenInfo left_info(node->left());
685 CodeGenInfo right_info(node->right());
686 VisitLoadTwo(node->left(), node->right(), EAX, EDX);
687 // TODO(srdjan): Better code for count being a Smi literal.
688 // EAX: value, EDX: shift amount. Preserve them for slow case.
689 // Fast case only if both ar Smi.
690 if (!left_info.IsClass(smi_class_) || !right_info.IsClass(smi_class_)) {
691 __ movl(EBX, EAX);
692 __ orl(EBX, EDX);
693 __ testl(EBX, Immediate(kSmiTagMask));
694 __ j(NOT_ZERO, &slow_case, Assembler::kNearJump);
695 }
696 // Check if count too large for handling it inlined.
697 __ cmpl(EDX, Immediate(reinterpret_cast<int32_t>(Smi::New(Smi::kBits))));
698 __ j(ABOVE_EQUAL, &slow_case, Assembler::kNearJump);
699 // Shift amount must be in ECX.
700 __ movl(ECX, EDX);
701 __ movl(EBX, EAX);
702 __ SmiUntag(ECX);
703 // Overflow test.
704 __ shll(EBX, ECX);
705 __ sarl(EBX, ECX);
706 __ cmpl(EAX, EBX);
707 __ j(NOT_EQUAL, &slow_case, Assembler::kNearJump); // Overflow.
708
709 __ shll(EAX, ECX); // Shift for result now we know there is no overflow.
710 // EAX is the correctly tagged Smi.
711 __ jmp(&done);
712 __ Bind(&slow_case);
713 __ pushl(EAX);
714 __ pushl(EDX);
715 const int number_of_arguments = 2;
716 const Array& no_optional_argument_names = Array::Handle();
717 GenerateCheckedInstanceCalls(node,
718 node->left(),
719 node->token_pos(),
720 number_of_arguments,
721 no_optional_argument_names);
722 __ Bind(&done);
723 }
724
725
726 // Implement Token::kSUB and Token::kBIT_NOT.
727 void OptimizingCodeGenerator::GenerateSmiUnaryOp(UnaryOpNode* node) {
728 const ICData& ic_data = node->ic_data();
729 ASSERT(ic_data.num_args_tested() == 1);
730 DeoptReasonId deopt_reason_id = ic_data.NumberOfChecks() == 0 ?
731 kDeoptNoTypeFeedback : kDeoptUnaryOp;
732 DeoptimizationBlob* deopt_blob =
733 AddDeoptimizationBlob(node, EAX, deopt_reason_id);
734 CodeGenInfo info(node->operand());
735 VisitLoadOne(node->operand(), EAX);
736 if (ic_data.NumberOfChecks() == 0) {
737 // No type feedback.
738 __ jmp(deopt_blob->label());
739 return;
740 }
741 ASSERT(ic_data.NumberOfChecks() == 1);
742 if (!info.IsClass(smi_class_)) {
743 __ testl(EAX, Immediate(kSmiTagMask));
744 __ j(NOT_ZERO, deopt_blob->label());
745 PropagateBackLocalClass(node->operand(), smi_class_);
746 }
747 if (node->kind() == Token::kSUB) {
748 __ negl(EAX);
749 __ j(OVERFLOW, deopt_blob->label());
750 } else {
751 ASSERT(node->kind() == Token::kBIT_NOT);
752 __ notl(EAX);
753 __ andl(EAX, Immediate(~kSmiTagMask)); // Remove inverted smi-tag.
754 }
755 HandleResult(node, EAX);
756 }
757
758
759 void OptimizingCodeGenerator::GenerateDoubleUnaryOp(UnaryOpNode* node) {
760 const Register kOperandRegister = ECX;
761 const Register kTempRegister = EBX;
762 const Register kResultRegister = EAX;
763 const ICData& ic_data = node->ic_data();
764 DeoptReasonId deopt_reason_id = ic_data.NumberOfChecks() == 0 ?
765 kDeoptNoTypeFeedback : kDeoptUnaryOp;
766 DeoptimizationBlob* deopt_blob =
767 AddDeoptimizationBlob(node, kOperandRegister, deopt_reason_id);
768 CodeGenInfo info(node->operand());
769 info.set_allow_temp(true);
770 VisitLoadOne(node->operand(), kOperandRegister);
771 if (ic_data.NumberOfChecks() == 0) {
772 // No type feedback.
773 __ jmp(deopt_blob->label());
774 return;
775 }
776 ASSERT(ic_data.NumberOfChecks() == 1);
777 if (!info.IsClass(double_class_)) {
778 // Deoptimize if not double.
779 CheckIfDoubleOrSmi(kOperandRegister,
780 kTempRegister,
781 deopt_blob->label(),
782 deopt_blob->label());
783 PropagateBackLocalClass(node->operand(), double_class_);
784 }
785 const bool using_temp =
786 (node->info() != NULL) && node->info()->allow_temp();
787 if (!using_temp) {
788 const Code& stub =
789 Code::Handle(StubCode::GetAllocationStubForClass(double_class_));
790 const ExternalLabel label(double_class_.ToCString(), stub.EntryPoint());
791 __ pushl(kOperandRegister);
792 GenerateCall(node->token_pos(), &label, PcDescriptors::kOther);
793 ASSERT(kResultRegister == EAX);
794 __ popl(kOperandRegister);
795 } else if (info.is_temp()) {
796 __ movl(kResultRegister, kOperandRegister);
797 } else {
798 const Double& double_object =
799 Double::ZoneHandle(Double::New(0.0, Heap::kOld));
800 __ LoadObject(kResultRegister, double_object);
801 }
802 __ movsd(XMM0, FieldAddress(kOperandRegister, Double::value_offset()));
803 ASSERT(node->kind() == Token::kSUB);
804 __ DoubleNegate(XMM0);
805 __ movsd(FieldAddress(kResultRegister, Double::value_offset()), XMM0);
806 if (IsResultNeeded(node)) {
807 if (node->info() != NULL) {
808 node->info()->set_is_temp(using_temp);
809 node->info()->set_is_class(&double_class_);
810 }
811 HandleResult(node, kResultRegister);
812 }
813 }
814
815
816 // Handles only Smi & Smi.
817 // TODO(srdjan): Certain operations always overflow, and thus cause
818 // deoptimization. We need to mark those places and handle them.
819 void OptimizingCodeGenerator::GenerateSmiBinaryOp(BinaryOpNode* node) {
820 const char* kOptMessage = "Inlines BinaryOp for Smi";
821 Label done;
822 const Token::Kind kind = node->kind();
823 if ((kind == Token::kADD) ||
824 (kind == Token::kSUB) ||
825 (kind == Token::kMUL) ||
826 (kind == Token::kTRUNCDIV) ||
827 (kind == Token::kBIT_AND) ||
828 (kind == Token::kBIT_OR) ||
829 (kind == Token::kBIT_XOR)) {
830 TraceOpt(node, kOptMessage);
831 // Check if both arguments are expected to be Smi.
832 const ICData& ic_data = node->ic_data();
833 ASSERT(ic_data.num_args_tested() == 2);
834 ASSERT(ic_data.NumberOfChecks() > 0);
835 Function& target = Function::Handle();
836 GrowableArray<intptr_t> class_ids;
837 ic_data.GetCheckAt(0, &class_ids, &target);
838 ASSERT(ic_data.NumberOfChecks() == 1);
839 ASSERT((class_ids[0] == kSmi) && (class_ids[1] == kSmi));
840 CodeGenInfo left_info(node->left());
841 CodeGenInfo right_info(node->right());
842 VisitLoadTwo(node->left(), node->right(), EAX, EDX);
843 Label two_smis, call_operator;
844 DeoptimizationBlob* deopt_blob =
845 AddDeoptimizationBlob(node, ECX, EDX, kDeoptSmiBinaryOp);
846 __ movl(ECX, EAX); // Save if overflow (needs original value).
847
848 if (left_info.IsClass(smi_class_) || right_info.IsClass(smi_class_)) {
849 if (!left_info.IsClass(smi_class_)) {
850 __ testl(EAX, Immediate(kSmiTagMask));
851 __ j(NOT_ZERO, deopt_blob->label());
852 PropagateBackLocalClass(node->left(), smi_class_);
853 }
854 if (!right_info.IsClass(smi_class_)) {
855 __ testl(EDX, Immediate(kSmiTagMask));
856 __ j(NOT_ZERO, deopt_blob->label());
857 PropagateBackLocalClass(node->right(), smi_class_);
858 }
859 } else {
860 // Type feedback says both types are Smi, but static type analysis
861 // does not know if any of them is Smi, therefore check.
862 __ orl(EAX, EDX);
863 __ testl(EAX, Immediate(kSmiTagMask));
864 __ j(NOT_ZERO, deopt_blob->label());
865 __ movl(EAX, ECX);
866 PropagateBackLocalClass(node->left(), smi_class_);
867 PropagateBackLocalClass(node->right(), smi_class_);
868 }
869 if (node->info() != NULL) {
870 node->info()->set_is_class(&smi_class_);
871 }
872 switch (kind) {
873 case Token::kADD: {
874 __ addl(EAX, EDX);
875 __ j(OVERFLOW, deopt_blob->label());
876 break;
877 }
878 case Token::kSUB: {
879 __ subl(EAX, EDX);
880 __ j(OVERFLOW, deopt_blob->label());
881 break;
882 }
883 case Token::kMUL: {
884 __ SmiUntag(EAX);
885 __ imull(EAX, EDX);
886 __ j(OVERFLOW, deopt_blob->label());
887 break;
888 }
889 case Token::kBIT_AND: {
890 // No overflow check.
891 __ andl(EAX, EDX);
892 break;
893 }
894 case Token::kBIT_OR: {
895 // No overflow check.
896 __ orl(EAX, EDX);
897 break;
898 }
899 case Token::kBIT_XOR: {
900 // No overflow check.
901 __ xorl(EAX, EDX);
902 break;
903 }
904 case Token::kTRUNCDIV: {
905 // Handle divide by zero in runtime.
906 __ cmpl(EDX, Immediate(0));
907 __ j(EQUAL, deopt_blob->label());
908 // Preserve left & right in case of 'overflow'.
909 __ pushl(EDX);
910 __ pushl(ECX);
911 // Move right to ECX, left is in EAX.
912 __ movl(ECX, EDX);
913 __ SmiUntag(ECX);
914 __ SmiUntag(EAX);
915 // Sign extend EAX -> EDX:EAX.
916 __ cdq();
917 __ idivl(ECX); // Result in EAX.
918 __ popl(ECX);
919 __ popl(EDX);
920 // Check the corner case of dividing the 'MIN_SMI' with -1, in which
921 // case we cannot tag the result.
922 __ cmpl(EAX, Immediate(0x40000000));
923 __ j(EQUAL, deopt_blob->label());
924 __ SmiTag(EAX);
925 break;
926 }
927 default:
928 UNREACHABLE();
929 }
930 } else if ((kind == Token::kSHL) || (kind == Token::kSHR)) {
931 GenerateSmiShiftBinaryOp(node);
932 } else {
933 // Unhandled node kind.
934 TraceNotOpt(node, kOptMessage);
935 node->left()->Visit(this);
936 node->right()->Visit(this);
937 CodeGenerator::GenerateBinaryOperatorCall(node->id(),
938 node->token_pos(),
939 node->Name());
940 }
941 __ Bind(&done);
942 HandleResult(node, EAX);
943 }
944
945
946 // Supports some mixed Smi/Mint operations.
947 // For BIT_AND operation with right operand being Smi, we can throw away
948 // any Mint bits above the Smi range as long as the right operand is positive.
949 // 'allow_smi' is true if Smi and Mint classes have been encountered.
950 void OptimizingCodeGenerator::GenerateMintBinaryOp(BinaryOpNode* node,
951 bool allow_smi) {
952 const char* kOptMessage = "Inline Mint binop.";
953 const Token::Kind kind = node->kind();
954 if (kind == Token::kBIT_AND) {
955 TraceOpt(node, kOptMessage);
956 Label is_smi, slow_case, done;
957 DeoptimizationBlob* deopt_blob =
958 AddDeoptimizationBlob(node, EAX, EDX, kDeoptMintBinaryOp);
959 VisitLoadTwo(node->left(), node->right(), EAX, EDX);
960 __ testl(EDX, Immediate(kSmiTagMask));
961 __ j(NOT_ZERO, &slow_case); // Call operator if right is not Smi.
962 __ cmpl(EDX, Immediate(0));
963 __ j(LESS, &slow_case); // Result will not be Smi.
964
965 // Test left.
966 __ testl(EAX, Immediate(kSmiTagMask));
967 __ j(ZERO, &is_smi);
968
969 __ CompareClassId(EAX, kMint, EBX);
970 __ j(NOT_EQUAL, deopt_blob->label());
971
972 // Load lower Mint word, convert to Smi. It is OK to loose bits.
973 __ movl(EAX, FieldAddress(EAX, Mint::value_offset()));
974 __ SmiTag(EAX);
975
976 __ Bind(&is_smi);
977 __ andl(EAX, EDX);
978 __ jmp(&done);
979 __ Bind(&slow_case);
980 __ pushl(EAX);
981 __ pushl(EDX);
982 const int number_of_arguments = 2;
983 const Array& no_optional_argument_names = Array::Handle();
984 GenerateCheckedInstanceCalls(node,
985 node->left(),
986 node->token_pos(),
987 number_of_arguments,
988 no_optional_argument_names);
989 __ Bind(&done);
990 HandleResult(node, EAX);
991 return;
992 }
993 if ((kind == Token::kSHL) && allow_smi) {
994 GenerateSmiShiftBinaryOp(node);
995 HandleResult(node, EAX);
996 return;
997 }
998 TraceNotOpt(node, kOptMessage);
999 CodeGenerator::VisitBinaryOpNode(node);
1000 }
1001
1002
1003 // Conservative approach:
1004 // - true if both nodes are LoadLocalNodes with the same index.
1005 static bool AreNodesOfSameType(AstNode* a, AstNode* b) {
1006 ASSERT((a != NULL) && (b != NULL));
1007 if (a->IsLoadLocalNode() && b->IsLoadLocalNode()) {
1008 return a->AsLoadLocalNode()->local().Equals(b->AsLoadLocalNode()->local());
1009 }
1010 return false;
1011 }
1012
1013
1014 // If possible propagate node type back to the local, therefore next load
1015 // of local can use that class and eliminate type checks.
1016 void OptimizingCodeGenerator::PropagateBackLocalClass(AstNode* node,
1017 const Class& cls) {
1018 if (node->IsLoadLocalNode()) {
1019 LoadLocalNode* local_node = node->AsLoadLocalNode();
1020 classes_for_locals_->SetLocalType(local_node->local(), cls);
1021 }
1022 }
1023
1024
1025 // 'reg' is not modified, 'temp' is trashed.
1026 // Fall through if double, jump to 'is_smi' if Smi and
1027 // jump to 'not_double_or_smi' if neither double nor Smi.
1028 void OptimizingCodeGenerator::CheckIfDoubleOrSmi(Register reg,
1029 Register temp,
1030 Label* is_smi,
1031 Label* not_double_or_smi) {
1032 __ testl(reg, Immediate(kSmiTagMask));
1033 __ j(ZERO, is_smi);
1034 __ CompareClassId(reg, kDouble, temp);
1035 __ j(NOT_EQUAL, not_double_or_smi);
1036 }
1037
1038
1039 // Result of the computation is a newly allocated double object or
1040 // a temporary object if the parent node specifies a CodeGenInfo for this node
1041 // and therefore knows how to handle a temporary. A temporary object cannot
1042 // be used for long living values (e.g., the ones stored on stack or into other
1043 // objects).
1044 // Implement for combinations: Double/Double, Double/Smi, Smi/Double, as
1045 // the result is always double.
1046 // TODO(srdjan): Implement Smi/Smi for kDIV (result also double).
1047 void OptimizingCodeGenerator::GenerateDoubleBinaryOp(BinaryOpNode* node,
1048 bool receiver_can_be_smi) {
1049 const char* kOptMessage = "Inlines BinaryOp for Doubles";
1050 const Token::Kind kind = node->kind();
1051 if ((kind == Token::kADD) ||
1052 (kind == Token::kSUB) ||
1053 (kind == Token::kMUL) ||
1054 (kind == Token::kDIV)) {
1055 TraceOpt(node, kOptMessage);
1056 // All four register below must be different.
1057 const Register kLeftRegister = EAX;
1058 const Register kRightRegister = EDX;
1059 const Register kAllocatedRegister = ECX;
1060 const Register kTempRegister = EBX;
1061 CodeGenInfo left_info(node->left()); // Receiver.
1062 CodeGenInfo right_info(node->right());
1063 left_info.set_allow_temp(true);
1064 right_info.set_allow_temp(true);
1065 VisitLoadTwo(node->left(), node->right(), kLeftRegister, kRightRegister);
1066 // First allocate result object or specify an existing object as result.
1067 Register result_register = kNoRegister;
1068 const bool using_temp =
1069 (node->info() != NULL) && node->info()->allow_temp();
1070 if (!using_temp) {
1071 // Parent node cannot handle a temporary double object, allocate one
1072 // each time.
1073 result_register = kAllocatedRegister;
1074 const Code& stub =
1075 Code::Handle(StubCode::GetAllocationStubForClass(double_class_));
1076 const ExternalLabel label(double_class_.ToCString(), stub.EntryPoint());
1077 __ pushl(kLeftRegister);
1078 __ pushl(kRightRegister);
1079 GenerateCall(node->token_pos(), &label, PcDescriptors::kOther);
1080 __ movl(result_register, EAX);
1081 __ popl(kRightRegister);
1082 __ popl(kLeftRegister);
1083 } else if (left_info.IsClass(double_class_) && left_info.is_temp()) {
1084 result_register = kLeftRegister;
1085 } else if (right_info.IsClass(double_class_) && right_info.is_temp()) {
1086 result_register = kRightRegister;
1087 } else {
1088 result_register = kAllocatedRegister;
1089 // Use inlined temporary double object.
1090 const Double& double_object =
1091 Double::ZoneHandle(Double::New(0.0, Heap::kOld));
1092 __ LoadObject(result_register, double_object);
1093 }
1094
1095 DeoptimizationBlob* deopt_blob = NULL;
1096 Label* deopt_lbl = NULL;
1097 // Deoptimization can only occur if one of arguments is not double.
1098 if (!left_info.IsClass(double_class_) ||
1099 !right_info.IsClass(double_class_)) {
1100 deopt_blob = AddDeoptimizationBlob(node,
1101 kLeftRegister,
1102 kRightRegister,
1103 kDeoptDoubleBinaryOp);
1104 deopt_lbl = deopt_blob->label();
1105 }
1106
1107 if (receiver_can_be_smi) {
1108 // Only deoptimize if both argument are Smi.
1109 __ movl(kTempRegister, kLeftRegister);
1110 __ orl(kTempRegister, kRightRegister);
1111 __ testl(kTempRegister, Immediate(kSmiTagMask));
1112 __ j(ZERO, deopt_lbl);
1113 }
1114
1115 bool args_of_same_type = AreNodesOfSameType(node->left(), node->right());
1116 if (left_info.IsClass(double_class_)) {
1117 __ movsd(XMM0, FieldAddress(kLeftRegister, Double::value_offset()));
1118 } else {
1119 if (receiver_can_be_smi) {
1120 Label is_smi, done;
1121 CheckIfDoubleOrSmi(kLeftRegister, kTempRegister, &is_smi, deopt_lbl);
1122 // Fall through for double. Jump to 'is_smi' if double, jump to
1123 // 'deopt' if neither smi nor double.
1124 __ movsd(XMM0, FieldAddress(kLeftRegister, Double::value_offset()));
1125 __ jmp(&done);
1126 __ Bind(&is_smi);
1127 __ SmiUntag(kLeftRegister);
1128 __ cvtsi2sd(XMM0, kLeftRegister);
1129 __ Bind(&done);
1130 } else {
1131 CheckIfDoubleOrSmi(kLeftRegister, kTempRegister, deopt_lbl, deopt_lbl);
1132 __ movsd(XMM0, FieldAddress(kLeftRegister, Double::value_offset()));
1133 PropagateBackLocalClass(node->left(), double_class_);
1134 }
1135 }
1136
1137 const bool right_must_be_double = NodeHasClassAt(node, double_class_, 1);
1138
1139 // If arguments are of same type (e.g., same local), then the test of left
1140 // argument was sufficient.
1141 if (right_info.IsClass(double_class_) || args_of_same_type) {
1142 __ movsd(XMM1, FieldAddress(kRightRegister, Double::value_offset()));
1143 if (!right_info.IsClass(double_class_)) {
1144 PropagateBackLocalClass(node->right(), double_class_);
1145 }
1146 } else {
1147 if (right_must_be_double) {
1148 CheckIfDoubleOrSmi(kRightRegister, kTempRegister, deopt_lbl, deopt_lbl);
1149 __ movsd(XMM1, FieldAddress(kRightRegister, Double::value_offset()));
1150 PropagateBackLocalClass(node->right(), double_class_);
1151 } else {
1152 Label is_smi, done;
1153 CheckIfDoubleOrSmi(kRightRegister, kTempRegister, &is_smi, deopt_lbl);
1154 // Fall through for double. Jump to 'is_smi' if double, jump to
1155 // 'deopt' if neither smi nor double.
1156 __ movsd(XMM1, FieldAddress(kRightRegister, Double::value_offset()));
1157 __ jmp(&done);
1158 __ Bind(&is_smi);
1159 __ SmiUntag(kRightRegister);
1160 __ cvtsi2sd(XMM1, kRightRegister);
1161 __ Bind(&done);
1162 }
1163 }
1164
1165 switch (kind) {
1166 case Token::kADD: __ addsd(XMM0, XMM1); break;
1167 case Token::kSUB: __ subsd(XMM0, XMM1); break;
1168 case Token::kMUL: __ mulsd(XMM0, XMM1); break;
1169 case Token::kDIV: __ divsd(XMM0, XMM1); break;
1170 default: UNREACHABLE();
1171 }
1172 __ movsd(FieldAddress(result_register, Double::value_offset()), XMM0);
1173 if (IsResultNeeded(node)) {
1174 if (node->info() != NULL) {
1175 node->info()->set_is_temp(using_temp);
1176 node->info()->set_is_class(&double_class_);
1177 }
1178 HandleResult(node, result_register);
1179 }
1180 return;
1181 }
1182
1183 TraceNotOpt(node, kOptMessage);
1184 CodeGenerator::VisitBinaryOpNode(node);
1185 }
1186
1187
1188 static bool NodeInfoHasLabels(AstNode* node) {
1189 return (node->info() != NULL) &&
1190 (node->info()->true_label() != NULL) &&
1191 (node->info()->false_label() != NULL);
1192 }
1193
1194
1195 // Generates code for logical OR, AND operations.
1196 // A logical binary operation either pushes a true/false object on the stack,
1197 // or jumps to the true/false label of the parent node.
1198 // For AND operation, if left argument is false, then the result is false.
1199 // For OR operation, if left argument is true, then the result is true.
1200 // Otherwise the right argument is evaluated and the result corresponds to the
1201 // right argument.
1202 void OptimizingCodeGenerator::GenerateLogicalBinaryOp(BinaryOpNode* node) {
1203 ASSERT((node->kind() == Token::kAND) || (node->kind() == Token::kOR));
1204 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
1205 const Bool& bool_false = Bool::ZoneHandle(Bool::False());
1206
1207 // If NodeInfoHasLabels is true, then we do not return a result but
1208 // jump to the specified true/false labels.
1209 Label return_false_object, return_true_object, evaluate_right_label;
1210 Label* false_label = NodeInfoHasLabels(node) ?
1211 node->info()->false_label() : &return_false_object;
1212 Label* true_label = NodeInfoHasLabels(node) ?
1213 node->info()->true_label() : &return_true_object;
1214
1215 CodeGenInfo left_bool(node->left());
1216 if (node->kind() == Token::kAND) {
1217 left_bool.set_true_label(&evaluate_right_label);
1218 left_bool.set_false_label(false_label);
1219 } else {
1220 left_bool.set_true_label(true_label);
1221 left_bool.set_false_label(&evaluate_right_label);
1222 }
1223 VisitLoadOne(node->left(), EAX);
1224 if (left_bool.labels_used()) {
1225 __ Bind(&evaluate_right_label);
1226 } else {
1227 __ CompareObject(EAX, bool_true);
1228 if (node->kind() == Token::kAND) {
1229 __ j(NOT_EQUAL, false_label);
1230 } else {
1231 __ j(EQUAL, true_label);
1232 }
1233 }
1234
1235 CodeGenInfo right_bool(node->right());
1236 right_bool.set_true_label(true_label);
1237 right_bool.set_false_label(false_label);
1238 VisitLoadOne(node->right(), EAX);
1239 if (right_bool.labels_used()) {
1240 // The control flow continues at the parent's false or true labels.
1241 #if defined(DEBUG)
1242 __ Unreachable("BinaryOp");
1243 #endif
1244 } else {
1245 __ CompareObject(EAX, bool_true);
1246 __ j(NOT_EQUAL, false_label);
1247 if (NodeInfoHasLabels(node)) {
1248 __ jmp(true_label);
1249 }
1250 }
1251 if (NodeInfoHasLabels(node)) {
1252 node->info()->set_labels_used(true);
1253 } else {
1254 Label done;
1255 __ Bind(&return_true_object);
1256 __ LoadObject(EAX, bool_true);
1257 __ jmp(&done, Assembler::kNearJump);
1258 __ Bind(&return_false_object);
1259 __ LoadObject(EAX, bool_false);
1260 __ Bind(&done);
1261 HandleResult(node, EAX);
1262 }
1263 }
1264
1265
1266 void OptimizingCodeGenerator::VisitBinaryOpNode(BinaryOpNode* node) {
1267 // Operators "&&" and "||" cannot be overloaded, therefore inline them
1268 // instead of calling the operator.
1269 if ((node->kind() == Token::kAND) || (node->kind() == Token::kOR)) {
1270 // TODO(srdjan): Test in checked mode if they are Booleans otherwise
1271 // throw exception.
1272 if (FLAG_enable_type_checks) {
1273 CodeGenerator::VisitBinaryOpNode(node);
1274 return;
1275 }
1276 GenerateLogicalBinaryOp(node);
1277 return;
1278 }
1279
1280 const ICData& ic_data = node->ic_data();
1281 if (ic_data.NumberOfChecks() == 0) {
1282 VisitLoadTwo(node->left(), node->right(), EAX, EDX);
1283 DeoptimizationBlob* deopt_blob =
1284 AddDeoptimizationBlob(node, EAX, EDX, kDeoptNoTypeFeedback);
1285 __ jmp(deopt_blob->label());
1286 return;
1287 }
1288
1289 ASSERT(ic_data.num_args_tested() == 2);
1290
1291 if (NodeHasTwoClasses(node, smi_class_, smi_class_)) {
1292 GenerateSmiBinaryOp(node);
1293 return;
1294 }
1295
1296 if (NodeHasClassAt(node, double_class_, 0)) {
1297 const bool receiver_can_be_smi = false;
1298 GenerateDoubleBinaryOp(node, receiver_can_be_smi);
1299 return;
1300 }
1301
1302 if (NodeHasTwoClasses(node, smi_class_, double_class_)) {
1303 const bool receiver_can_be_smi = true;
1304 GenerateDoubleBinaryOp(node, receiver_can_be_smi);
1305 return;
1306 }
1307
1308 const Class& mint_class =
1309 Class::Handle(Isolate::Current()->object_store()->mint_class());
1310 if (NodeHasClassAt(node, mint_class, 0)) {
1311 GenerateMintBinaryOp(node, false);
1312 return;
1313 }
1314
1315 if (NodeHasBothReceiverClasses(node, smi_class_, mint_class)) {
1316 GenerateMintBinaryOp(node, true);
1317 return;
1318 }
1319
1320 // TODO(srdjan): Implement "+" for Strings.
1321 // Type feedback tells this is not a Smi or Double operation.
1322 TraceNotOpt(node,
1323 "BinaryOp: type feedback tells this is not a Smi, Mint or Double op");
1324 node->left()->Visit(this);
1325 node->right()->Visit(this);
1326 const int number_of_arguments = 2;
1327 const Array& no_optional_argument_names = Array::Handle();
1328 GenerateCheckedInstanceCalls(node,
1329 node->left(),
1330 node->token_pos(),
1331 number_of_arguments,
1332 no_optional_argument_names);
1333 HandleResult(node, EAX);
1334 return;
1335 }
1336
1337
1338 // Return offset of a field or -1 if field is not found.
1339 static intptr_t GetFieldOffset(const Class& field_class,
1340 const String& field_name) {
1341 Class& cls = Class::Handle(field_class.raw());
1342 Field& field = Field::Handle();
1343 while (!cls.IsNull()) {
1344 field = cls.LookupInstanceField(field_name);
1345 if (!field.IsNull()) {
1346 return field.Offset();
1347 }
1348 cls = cls.SuperClass();
1349 }
1350 return -1;
1351 }
1352
1353
1354 // For now, check if the node is the receiver of a non-Smi class.
1355 bool OptimizingCodeGenerator::NodeMayBeSmi(AstNode* node) const {
1356 if (parsed_function_.function().is_static() ||
1357 parsed_function_.function().IsConstructor() ||
1358 parsed_function_.function().IsClosureFunction()) {
1359 return true;
1360 }
1361 LocalScope* scope = parsed_function_.node_sequence()->scope();
1362 LocalVariable* receiver = scope->VariableAt(0);
1363 if (node->IsLoadLocalNode() &&
1364 (&node->AsLoadLocalNode()->local() == receiver)) {
1365 const Class& function_owner =
1366 Class::Handle(parsed_function_.function().owner());
1367 const String& integer_implementation_class_name =
1368 String::Handle(String::NewSymbol("IntegerImplementation"));
1369 const Class& integer_implementation_class = Class::Handle(
1370 Library::Handle(Library::CoreImplLibrary()).
1371 LookupClass(integer_implementation_class_name));
1372 if (!function_owner.IsSmi() &&
1373 (function_owner.raw() != integer_implementation_class.raw())) {
1374 return false;
1375 }
1376 }
1377 return true;
1378 }
1379
1380
1381 // Emits code for an instance getter that has one or more collected classes,
1382 // all with the same target. Deoptimizes for Smi or unexpected class.
1383 // EBX: loaded receiver.
1384 // Result is returned in EAX.
1385 void OptimizingCodeGenerator::InlineInstanceGettersWithSameTarget(
1386 AstNode* node,
1387 AstNode* receiver,
1388 const String& field_name,
1389 Register recv_reg) {
1390 if (recv_reg != EBX) {
1391 // TODO(srdjan): Do not hardwire register.
1392 UNIMPLEMENTED();
1393 }
1394 DeoptimizationBlob* deopt_blob =
1395 AddDeoptimizationBlob(node, EBX, kDeoptInstanceGetterSameTarget);
1396 if (NodeMayBeSmi(receiver)) {
1397 __ testl(EBX, Immediate(kSmiTagMask));
1398 __ j(ZERO, deopt_blob->label());
1399 }
1400
1401 __ LoadClassId(EAX, EBX);
1402 const ICData& ic_data = node->ic_data();
1403 Function& target = Function::Handle();
1404 Label load_field;
1405 for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
1406 intptr_t class_id = kIllegalObjectKind;
1407 ic_data.GetOneClassCheckAt(i, &class_id, &target);
1408 __ cmpl(EAX, Immediate(class_id));
1409 if (i == (ic_data.NumberOfChecks() - 1)) {
1410 __ j(NOT_EQUAL, deopt_blob->label());
1411 } else {
1412 __ j(EQUAL, &load_field);
1413 }
1414 }
1415 intptr_t class_id = kIllegalObjectKind;
1416 ic_data.GetOneClassCheckAt(0, &class_id, &target);
1417
1418 __ Bind(&load_field);
1419 // EBX: receiver.
1420 if (target.kind() == RawFunction::kImplicitGetter) {
1421 TraceOpt(node, "Inlines instance getter with same target");
1422 const Class& cls =
1423 Class::Handle(Isolate::Current()->class_table()->At(class_id));
1424 intptr_t field_offset = GetFieldOffset(cls, field_name);
1425 ASSERT(field_offset >= 0);
1426 __ movl(EAX, FieldAddress(EBX, field_offset));
1427 return;
1428 }
1429
1430 Recognizer::Kind recognized_kind = Recognizer::RecognizeKind(target);
1431 switch (recognized_kind) {
1432 case Recognizer::kObjectArrayLength: {
1433 TraceOpt(node, "Inlines ObjectArray.length");
1434 __ movl(EAX, FieldAddress(EBX, Array::length_offset()));
1435 return;
1436 }
1437 case Recognizer::kGrowableArrayLength: {
1438 TraceOpt(node, "Inlines GrowableObjectArray.length");
1439 __ movl(EAX, FieldAddress(EBX, GrowableObjectArray::length_offset()));
1440 return;
1441 }
1442 case Recognizer::kStringBaseLength: {
1443 TraceOpt(node, "Inlines StringBase.length");
1444 __ movl(EAX, FieldAddress(EBX, String::length_offset()));
1445 return;
1446 }
1447 default:
1448 UNIMPLEMENTED();
1449 }
1450 UNREACHABLE();
1451 }
1452
1453
1454 static bool IsInlineableInstanceGetter(const Function& function) {
1455 if (function.kind() == RawFunction::kImplicitGetter) {
1456 return true;
1457 }
1458 Recognizer::Kind recognized = Recognizer::RecognizeKind(function);
1459 if ((recognized == Recognizer::kObjectArrayLength) ||
1460 (recognized == Recognizer::kGrowableArrayLength) ||
1461 (recognized == Recognizer::kStringBaseLength)) {
1462 return true;
1463 }
1464 return false;
1465 }
1466
1467
1468 // Return the unique target of all checks or null.
1469 static RawFunction* GetUniqueTarget(const ICData& ic_data) {
1470 Function& prev_target = Function::Handle();
1471 Function& target = Function::Handle();
1472 for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
1473 intptr_t class_id;
1474 ic_data.GetOneClassCheckAt(i, &class_id, &target);
1475 ASSERT(!target.IsNull());
1476 if (!prev_target.IsNull() && (prev_target.raw() != target.raw())) {
1477 return Function::null();
1478 }
1479 prev_target = target.raw();
1480 }
1481 return target.raw();
1482 }
1483
1484
1485 // Return true if all targets in 'ic_data' point to same
1486 // inlineable getter target.
1487 static bool ICDataToSameInlineableInstanceGetter(const ICData& ic_data) {
1488 const Function& target = Function::Handle(GetUniqueTarget(ic_data));
1489 return !target.IsNull() && IsInlineableInstanceGetter(target);
1490 }
1491
1492
1493 void OptimizingCodeGenerator::InlineInstanceGetter(AstNode* node,
1494 AstNode* receiver,
1495 const String& field_name,
1496 Register recv_reg) {
1497 if (ICDataToSameInlineableInstanceGetter(node->ic_data())) {
1498 InlineInstanceGettersWithSameTarget(node,
1499 receiver,
1500 field_name,
1501 recv_reg);
1502 } else {
1503 // TODO(srdjan): Inline access.
1504 __ pushl(recv_reg);
1505 const int kNumberOfArguments = 1;
1506 const Array& kNoArgumentNames = Array::Handle();
1507 GenerateCheckedInstanceCalls(node,
1508 receiver,
1509 node->token_pos(),
1510 kNumberOfArguments,
1511 kNoArgumentNames);
1512 }
1513 }
1514
1515
1516 // TODO(srdjan): Implement for multiple getter targets.
1517 // For every class inline its implicit getter, or call the instance getter.
1518 void OptimizingCodeGenerator::VisitInstanceGetterNode(
1519 InstanceGetterNode* node) {
1520 const ICData& ic_data = node->ic_data();
1521 if (ic_data.NumberOfChecks() == 0) {
1522 // No type feedback collected.
1523 node->receiver()->Visit(this);
1524 DeoptimizationBlob* deopt_blob =
1525 AddDeoptimizationBlob(node, kDeoptInstanceGetter);
1526 __ jmp(deopt_blob->label());
1527 return;
1528 }
1529
1530 VisitLoadOne(node->receiver(), EBX);
1531 InlineInstanceGetter(node,
1532 node->receiver(),
1533 node->field_name(),
1534 EBX);
1535 // Result is in EAX.
1536 HandleResult(node, EAX);
1537 }
1538
1539
1540 // Helper struct to pass arguments to 'GenerateInstanceSetter'.
1541 struct InstanceSetterArgs {
1542 const Class* cls;
1543 const Function* target;
1544 const String* field_name;
1545 Register recv_reg;
1546 Register value_reg;
1547 intptr_t id;
1548 intptr_t token_pos;
1549 };
1550
1551
1552 // Preserves 'args.value_reg'. Either stores instance field directly or
1553 // calls the setter method.
1554 void OptimizingCodeGenerator::GenerateInstanceSetter(
1555 const InstanceSetterArgs& args) {
1556 if (args.target->kind() == RawFunction::kImplicitSetter) {
1557 intptr_t field_offset = GetFieldOffset(*(args.cls), *(args.field_name));
1558 ASSERT(field_offset >= 0);
1559 __ StoreIntoObject(args.recv_reg,
1560 FieldAddress(args.recv_reg, field_offset), args.value_reg);
1561 } else {
1562 __ pushl(args.value_reg);
1563 __ pushl(args.recv_reg);
1564 __ pushl(args.value_reg);
1565 const Array& no_optional_argument_names = Array::Handle();
1566 GenerateDirectCall(args.id,
1567 args.token_pos,
1568 *(args.target),
1569 2,
1570 no_optional_argument_names);
1571 __ popl(args.value_reg);
1572 }
1573 }
1574
1575
1576 // Returns value in 'value_reg', clobbers EBX.
1577 void OptimizingCodeGenerator::InlineInstanceSetter(AstNode* node,
1578 AstNode* receiver,
1579 const String& field_name,
1580 Register recv_reg,
1581 Register value_reg) {
1582 // EBX is used as temporary register for class.
1583 ASSERT((recv_reg != EBX) && (value_reg != EBX));
1584 GrowableArray<Class*> classes;
1585 GrowableArray<Function*> targets;
1586 bool unique_target = true;
1587 {
1588 const ICData& ic_data = node->ic_data();
1589 ASSERT(ic_data.NumberOfChecks() > 0);
1590 ASSERT(ic_data.num_args_tested() == 1);
1591 for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
1592 Function& target = Function::ZoneHandle();
1593 intptr_t class_id;
1594 ic_data.GetOneClassCheckAt(i, &class_id, &target);
1595 Class& cls =
1596 Class::ZoneHandle(Isolate::Current()->class_table()->At(class_id));
1597 classes.Add(&cls);
1598 targets.Add(&target);
1599 }
1600 for (intptr_t i = 1; i < targets.length(); i++) {
1601 if (targets[i - 1]->raw() != targets[i]->raw()) {
1602 unique_target = false;
1603 break;
1604 }
1605 }
1606 }
1607 // TODO(srdjan): sort classes/target by their invocation count.
1608 DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(
1609 node, recv_reg, value_reg, kDeoptInstanceSetterSameTarget);
1610 // Deoptimize if Smi, since they do not have setters.
1611 if (NodeMayBeSmi(receiver)) {
1612 __ testl(recv_reg, Immediate(kSmiTagMask));
1613 __ j(ZERO, deopt_blob->label());
1614 }
1615 __ LoadClassId(EBX, recv_reg);
1616 // Initialize setter arguments, but leave the class and target fields NULL.
1617 InstanceSetterArgs setter_args =
1618 {NULL, NULL, &field_name, recv_reg, value_reg,
1619 node->id(), node->token_pos()};
1620
1621 if (unique_target) {
1622 Label store_field;
1623 for (intptr_t i = 0; i < classes.length(); i++) {
1624 __ cmpl(EBX, Immediate(classes[i]->id()));
1625 if (i == (classes.length() - 1)) {
1626 __ j(NOT_EQUAL, deopt_blob->label());
1627 } else {
1628 __ j(EQUAL, &store_field);
1629 }
1630 }
1631 __ Bind(&store_field);
1632 setter_args.cls = classes[0];
1633 setter_args.target = targets[0];
1634 GenerateInstanceSetter(setter_args);
1635 return;
1636 }
1637 // Targets are different.
1638 Label done;
1639 for (intptr_t i = 0; i < classes.length(); i++) {
1640 setter_args.cls = classes[i];
1641 setter_args.target = targets[i];
1642 __ cmpl(EBX, Immediate(classes[i]->id()));
1643 if (i == (classes.length() - 1)) {
1644 __ j(NOT_EQUAL, deopt_blob->label());
1645 GenerateInstanceSetter(setter_args);
1646 } else {
1647 Label next_check;
1648 __ j(NOT_EQUAL, &next_check);
1649 GenerateInstanceSetter(setter_args);
1650 __ jmp(&done);
1651 __ Bind(&next_check);
1652 }
1653 }
1654 __ Bind(&done);
1655 }
1656
1657
1658 // The call to the instance setter implements the assignment to a field.
1659 // The result of the assignment to a field is the value being stored.
1660 void OptimizingCodeGenerator::VisitInstanceSetterNode(
1661 InstanceSetterNode* node) {
1662 // TODO(srdjan): inline setters to different targets as well.
1663 if (FLAG_enable_type_checks) {
1664 CodeGenerator::VisitInstanceSetterNode(node);
1665 return;
1666 }
1667 VisitLoadTwo(node->receiver(), node->value(), EDX, EAX);
1668 const ICData& ic_data = node->ic_data();
1669 if (ic_data.NumberOfChecks() == 0) {
1670 DeoptimizationBlob* deopt_blob =
1671 AddDeoptimizationBlob(node, EDX, EAX, kDeoptInstanceSetter);
1672 __ jmp(deopt_blob->label());
1673 return;
1674 }
1675 // Value in EAX survives and will be stored on stack if result is needed.
1676 InlineInstanceSetter(node,
1677 node->receiver(),
1678 node->field_name(),
1679 EDX,
1680 EAX);
1681
1682 HandleResult(node, EAX);
1683 }
1684
1685
1686 // Return false if condition is not supported.
1687 static bool SupportedTokenKindToSmiCondition(Token::Kind kind,
1688 Condition* condition) {
1689 switch (kind) {
1690 case Token::kEQ:
1691 *condition = EQUAL;
1692 return true;
1693 case Token::kNE:
1694 *condition = NOT_EQUAL;
1695 return true;
1696 case Token::kLT:
1697 *condition = LESS;
1698 return true;
1699 case Token::kGT:
1700 *condition = GREATER;
1701 return true;
1702 case Token::kLTE:
1703 *condition = LESS_EQUAL;
1704 return true;
1705 case Token::kGTE:
1706 *condition = GREATER_EQUAL;
1707 return true;
1708 default:
1709 return false;
1710 }
1711 }
1712
1713
1714 static Condition NegateCondition(Condition condition) {
1715 switch (condition) {
1716 case EQUAL: return NOT_EQUAL;
1717 case NOT_EQUAL: return EQUAL;
1718 case LESS: return GREATER_EQUAL;
1719 case LESS_EQUAL: return GREATER;
1720 case GREATER: return LESS_EQUAL;
1721 case GREATER_EQUAL: return LESS;
1722 case BELOW: return ABOVE_EQUAL;
1723 case BELOW_EQUAL: return ABOVE;
1724 case ABOVE: return BELOW_EQUAL;
1725 case ABOVE_EQUAL: return BELOW;
1726 default:
1727 OS::Print("Error %d\n", condition);
1728 UNIMPLEMENTED();
1729 return EQUAL;
1730 }
1731 }
1732
1733
1734 void OptimizingCodeGenerator::GenerateConditionalJumps(const CodeGenInfo& nInfo,
1735 Condition condition) {
1736 if (nInfo.fallthrough_label() == NULL) {
1737 __ j(condition, nInfo.true_label());
1738 __ jmp(nInfo.false_label());
1739 } else if (nInfo.fallthrough_label() == nInfo.false_label()) {
1740 __ j(condition, nInfo.true_label());
1741 } else if (nInfo.fallthrough_label() == nInfo.true_label()) {
1742 __ j(NegateCondition(condition), nInfo.false_label());
1743 }
1744 }
1745
1746
1747 // Generate code under assumption that it is common that a Smi
1748 // is compared with null.
1749 // Left argument can be Smi or null, otherwise deoptimize and collect more
1750 // type information.
1751 // Right operand can be Smi or null, otherwise call operator on Smi (e.g,
1752 // when compared with double).
1753 // This code will be more optimized once we collect types for two arguments.
1754 void OptimizingCodeGenerator::GenerateSmiEquality(ComparisonNode* node) {
1755 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
1756 const Bool& bool_false = Bool::ZoneHandle(Bool::False());
1757 ASSERT((node->kind() == Token::kEQ) || (node->kind() == Token::kNE));
1758 CodeGenInfo left_info(node->left());
1759 CodeGenInfo right_info(node->right());
1760 VisitLoadTwo(node->left(), node->right(), EAX, EDX);
1761 if (!IsResultNeeded(node)) {
1762 return;
1763 }
1764 const Immediate raw_null =
1765 Immediate(reinterpret_cast<intptr_t>(Object::null()));
1766 Label evaluate_comparison;
1767 if (!left_info.IsClass(smi_class_)) {
1768 DeoptimizationBlob* deopt_blob =
1769 AddDeoptimizationBlob(node, EAX, EDX, kDeoptSmiEquality);
1770 Label left_not_null;
1771 __ cmpl(EAX, raw_null);
1772 __ j(NOT_EQUAL, &left_not_null, Assembler::kNearJump);
1773
1774 // Left is null, strict compare.
1775 __ cmpl(EAX, EDX);
1776 __ jmp(&evaluate_comparison, Assembler::kNearJump);
1777
1778 // Deoptimize if left is not Smi.
1779 __ Bind(&left_not_null);
1780 __ testl(EAX, Immediate(kSmiTagMask));
1781 __ j(NOT_ZERO, deopt_blob->label());
1782 }
1783 Label done;
1784 if (right_info.IsClass(smi_class_)) {
1785 __ cmpl(EAX, EDX);
1786 // Fall through to evaluate comparison.
1787 } else {
1788 Label call_operator, inlined_compare;
1789 // Test right for being Smi.
1790 __ testl(EDX, Immediate(kSmiTagMask));
1791 __ j(ZERO, &inlined_compare, Assembler::kNearJump);
1792 // Right is not Smi, test it for being null; if so result is false which
1793 // is generated by comparing it to left. If right is not null call operator
1794 // (could be double).
1795 __ cmpl(EDX, raw_null);
1796 __ j(NOT_EQUAL, &call_operator, Assembler::kNearJump);
1797
1798 __ Bind(&inlined_compare);
1799 // Left is Smi, right is Smi or Null.
1800 __ cmpl(EAX, EDX);
1801 __ jmp(&evaluate_comparison);
1802
1803 __ Bind(&call_operator);
1804 // Left is Smi.
1805 const int kNumberOfArguments = 2;
1806 const Array& kNoArgumentNames = Array::Handle();
1807 __ pushl(EAX);
1808 __ pushl(EDX);
1809 GenerateCheckedInstanceCalls(node,
1810 node->left(),
1811 node->token_pos(),
1812 kNumberOfArguments,
1813 kNoArgumentNames);
1814 __ CompareObject(EAX, bool_true);
1815 // Fall through to evaluate result.
1816 }
1817 __ Bind(&evaluate_comparison);
1818 // Condition is set by a previous comparison operation.
1819 Condition condition = OVERFLOW; // Initialize to something.
1820 bool ok = SupportedTokenKindToSmiCondition(node->kind(), &condition);
1821 ASSERT(ok);
1822 if (NodeInfoHasLabels(node)) {
1823 GenerateConditionalJumps(*(node->info()), condition);
1824 node->info()->set_labels_used(true);
1825 } else {
1826 Label true_label;
1827 __ j(condition, &true_label, Assembler::kNearJump);
1828 __ PushObject(bool_false);
1829 __ jmp(&done, Assembler::kNearJump);
1830 __ Bind(&true_label);
1831 __ PushObject(bool_true);
1832 }
1833 __ Bind(&done);
1834 }
1835
1836
1837 // Return false if the code cannot be generated. It is expected that
1838 // node->left() is Smi (or null for equality comparison).
1839 bool OptimizingCodeGenerator::GenerateSmiComparison(ComparisonNode* node) {
1840 if ((node->kind() == Token::kEQ) || (node->kind() == Token::kNE)) {
1841 GenerateSmiEquality(node);
1842 return true;
1843 }
1844 Condition condition;
1845 if (!SupportedTokenKindToSmiCondition(node->kind(), &condition)) {
1846 return false;
1847 }
1848 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
1849 const Bool& bool_false = Bool::ZoneHandle(Bool::False());
1850 CodeGenInfo left_info(node->left());
1851 CodeGenInfo right_info(node->right());
1852 VisitLoadTwo(node->left(), node->right(), EAX, EDX);
1853 if (!IsResultNeeded(node)) {
1854 return true;
1855 }
1856 if (left_info.IsClass(smi_class_) && right_info.IsClass(smi_class_)) {
1857 __ cmpl(EAX, EDX);
1858 } else if (left_info.IsClass(smi_class_) || right_info.IsClass(smi_class_)) {
1859 // One is Smi.
1860 DeoptimizationBlob* deopt_blob =
1861 AddDeoptimizationBlob(node, EAX, EDX, kDeoptSmiCompareSmis);
1862 Register reg_to_test = left_info.IsClass(smi_class_) ? EDX : EAX;
1863 __ testl(reg_to_test, Immediate(kSmiTagMask));
1864 __ j(NOT_ZERO, deopt_blob->label());
1865 __ cmpl(EAX, EDX);
1866 } else {
1867 DeoptimizationBlob* deopt_blob =
1868 AddDeoptimizationBlob(node, ECX, EDX, kDeoptSmiCompareAny);
1869 __ movl(ECX, EAX);
1870 __ orl(EAX, EDX);
1871 __ testl(EAX, Immediate(kSmiTagMask));
1872 __ j(NOT_ZERO, deopt_blob->label());
1873 __ cmpl(ECX, EDX);
1874 }
1875 if (NodeInfoHasLabels(node)) {
1876 GenerateConditionalJumps(*(node->info()), condition);
1877 node->info()->set_labels_used(true);
1878 } else {
1879 Label true_label, done;
1880 __ j(condition, &true_label, Assembler::kNearJump);
1881 __ PushObject(bool_false);
1882 __ jmp(&done, Assembler::kNearJump);
1883 __ Bind(&true_label);
1884 __ PushObject(bool_true);
1885 __ Bind(&done);
1886 }
1887 return true;
1888 }
1889
1890
1891 static bool SupportedTokenKindToDoubleCondition(Token::Kind kind,
1892 Condition* condition) {
1893 switch (kind) {
1894 case Token::kEQ:
1895 *condition = EQUAL;
1896 return true;
1897 case Token::kLT:
1898 *condition = BELOW;
1899 return true;
1900 case Token::kGT:
1901 *condition = ABOVE;
1902 return true;
1903 case Token::kLTE:
1904 *condition = BELOW_EQUAL;
1905 return true;
1906 case Token::kGTE:
1907 *condition = ABOVE_EQUAL;
1908 return true;
1909 default:
1910 return false;
1911 }
1912 }
1913
1914
1915 // Checks if an inlined equality/non-equality operation can be emitted:
1916 // - type feedback must exist.
1917 // - no class in type feedback list overrides '=='.
1918 // - no Smi class in type feedback class list (Smi overrides equality operator).
1919 bool OptimizingCodeGenerator::GenerateEqualityComparison(ComparisonNode* node) {
1920 ASSERT((node->kind() == Token::kEQ) || (node->kind() == Token::kNE));
1921 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
1922 const Bool& bool_false = Bool::ZoneHandle(Bool::False());
1923 const ZoneGrowableArray<const Class*>* classes = CollectedClassesAtNode(node);
1924 if (classes == NULL) {
1925 return false;
1926 }
1927 const int num_classes = classes->length();
1928 // 'num_classes' can be 0 if the receiver was always null.
1929 const String& operator_name = String::Handle(String::NewSymbol("=="));
1930 // Check that all classes resolve to Object.==. Object.!= is not overridable
1931 // and is based on Object.==.
1932 ObjectStore* object_store = Isolate::Current()->object_store();
1933 Function& function = Function::Handle();
1934 for (intptr_t i = 0; i < num_classes; i++) {
1935 const Class& cls = *(*classes)[i];
1936 const int kNumArguments = 2; // 'this' and 'other' arguments.
1937 const int kNumNamedArguments = 0;
1938 function ^=
1939 Resolver::ResolveDynamicForReceiverClass(cls,
1940 operator_name,
1941 kNumArguments,
1942 kNumNamedArguments);
1943 ASSERT(!function.IsNull()); // '==' must be defined.
1944 if (function.owner() != object_store->object_class()) {
1945 // Overridden '==' operator exists skip optimized comparison.
1946 TraceNotOpt(node, "Equality comparison, overridden ==");
1947 return false;
1948 }
1949 if (cls.raw() == smi_class_.raw()) {
1950 // TODO(srdjan): implement mixed smi/non-smi comparison, for the moment
1951 // bail out.
1952 TraceNotOpt(node, "Equality comparison, mixed with Smi");
1953 return false;
1954 }
1955 }
1956
1957 // All targets are Object.==, i.e., '==='. Smi is not among the classes.
1958 VisitLoadTwo(node->left(), node->right(), EAX, EDX);
1959 if (!IsResultNeeded(node)) {
1960 return true;
1961 }
1962 Label compare;
1963 // Comparison with NULL is "===".
1964 const Immediate raw_null =
1965 Immediate(reinterpret_cast<intptr_t>(Object::null()));
1966 __ cmpl(EAX, raw_null);
1967 if (num_classes == 0) {
1968 DeoptimizationBlob* deopt_blob =
1969 AddDeoptimizationBlob(node, EAX, EDX, kDeoptEqualityNoFeedback);
1970 __ j(NOT_EQUAL, deopt_blob->label());
1971 } else {
1972 DeoptimizationBlob* deopt_blob =
1973 AddDeoptimizationBlob(node, EAX, EDX, kDeoptEqualityClassCheck);
1974 __ j(EQUAL, &compare);
1975 // Smi causes deoptimization.
1976 __ testl(EAX, Immediate(kSmiTagMask));
1977 __ j(ZERO, deopt_blob->label());
1978 __ LoadClassId(EBX, EAX);
1979 for (intptr_t i = 0; i < num_classes; i++) {
1980 const Class& cls = *(*classes)[i];
1981 __ cmpl(EBX, Immediate(cls.id()));
1982 if (i == (num_classes - 1)) {
1983 __ j(NOT_EQUAL, deopt_blob->label());
1984 } else {
1985 __ j(EQUAL, &compare);
1986 }
1987 }
1988 }
1989 __ Bind(&compare);
1990 __ cmpl(EAX, EDX);
1991 if (NodeInfoHasLabels(node)) {
1992 if (node->kind() == Token::kEQ) {
1993 GenerateConditionalJumps(*(node->info()), EQUAL);
1994 } else {
1995 GenerateConditionalJumps(*(node->info()), NOT_EQUAL);
1996 }
1997 node->info()->set_labels_used(true);
1998 } else {
1999 Label done, load_true;
2000 if (node->kind() == Token::kEQ) {
2001 __ j(EQUAL, &load_true, Assembler::kNearJump);
2002 } else {
2003 __ j(NOT_EQUAL, &load_true, Assembler::kNearJump);
2004 }
2005 __ PushObject(bool_false);
2006 __ jmp(&done, Assembler::kNearJump);
2007 __ Bind(&load_true);
2008 __ PushObject(bool_true);
2009 __ Bind(&done);
2010 }
2011 TraceOpt(node, "Equality comparison");
2012 return true;
2013 }
2014
2015
2016 // Return false if the code cannot be generated.
2017 bool OptimizingCodeGenerator::GenerateDoubleComparison(ComparisonNode* node) {
2018 Condition true_condition;
2019 if (!SupportedTokenKindToDoubleCondition(node->kind(), &true_condition)) {
2020 return false;
2021 }
2022 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
2023 const Bool& bool_false = Bool::ZoneHandle(Bool::False());
2024 CodeGenInfo left_info(node->left());
2025 CodeGenInfo right_info(node->right());
2026 left_info.set_allow_temp(true);
2027 right_info.set_allow_temp(true);
2028 VisitLoadTwo(node->left(), node->right(), EAX, EDX);
2029 DeoptimizationBlob* deopt_blob = NULL;
2030 if (!left_info.IsClass(double_class_) || !right_info.IsClass(double_class_)) {
2031 deopt_blob = AddDeoptimizationBlob(node, EAX, EDX, kDeoptDoubleComparison);
2032 }
2033 if (!left_info.IsClass(double_class_)) {
2034 CheckIfDoubleOrSmi(EAX, EBX, deopt_blob->label(), deopt_blob->label());
2035 PropagateBackLocalClass(node->left(), double_class_);
2036 }
2037 if (!right_info.IsClass(double_class_)) {
2038 CheckIfDoubleOrSmi(EDX, EBX, deopt_blob->label(), deopt_blob->label());
2039 PropagateBackLocalClass(node->right(), double_class_);
2040 }
2041 __ movsd(XMM0, FieldAddress(EAX, Double::value_offset()));
2042 __ movsd(XMM1, FieldAddress(EDX, Double::value_offset()));
2043 __ comisd(XMM0, XMM1);
2044 if (NodeInfoHasLabels(node)) {
2045 __ j(PARITY_EVEN, node->info()->false_label()); // NaN -> false;
2046 GenerateConditionalJumps(*(node->info()), true_condition);
2047 node->info()->set_labels_used(true);
2048 } else {
2049 Label is_false, is_true, done;
2050 __ j(PARITY_EVEN, &is_false, Assembler::kNearJump); // NaN -> false;
2051 __ j(true_condition, &is_true, Assembler::kNearJump);
2052 __ Bind(&is_false);
2053 if (IsResultNeeded(node)) {
2054 __ PushObject(bool_false);
2055 }
2056 __ jmp(&done);
2057 __ Bind(&is_true);
2058 if (IsResultNeeded(node)) {
2059 __ PushObject(bool_true);
2060 }
2061 __ Bind(&done);
2062 }
2063 return true;
2064 }
2065
2066
2067 // IS, ISNOT are handled in class CodeGenerator.
2068 void OptimizingCodeGenerator::VisitComparisonNode(ComparisonNode* node) {
2069 if ((node->kind() == Token::kEQ_STRICT) ||
2070 (node->kind() == Token::kNE_STRICT)) {
2071 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
2072 const Bool& bool_false = Bool::ZoneHandle(Bool::False());
2073 // Note that evaluation of right may cause deoptimization, therefore left
2074 // must be on stack when evaluating right.
2075 if (node->right()->IsLiteralNode()) {
2076 VisitLoadOne(node->left(), EAX);
2077 __ CompareObject(EAX, node->right()->AsLiteralNode()->literal());
2078 } else {
2079 VisitLoadTwo(node->left(), node->right(), EAX, EDX);
2080 __ cmpl(EAX, EDX);
2081 }
2082 if (!IsResultNeeded(node)) {
2083 return;
2084 }
2085 Condition condition = node->kind() == Token::kEQ_STRICT ? EQUAL : NOT_EQUAL;
2086 if (NodeInfoHasLabels(node)) {
2087 GenerateConditionalJumps(*(node->info()), condition);
2088 node->info()->set_labels_used(true);
2089 } else {
2090 Label done, is_true;
2091 __ j(condition, &is_true);
2092 __ PushObject(bool_false);
2093 __ jmp(&done);
2094 __ Bind(&is_true);
2095 __ PushObject(bool_true);
2096 __ Bind(&done);
2097 }
2098 return;
2099 }
2100
2101 if (Token::IsTypeTestOperator(node->kind())) {
2102 VisitLoadOne(node->left(), EAX);
2103 ASSERT(node->right()->IsTypeNode());
2104 GenerateInstanceOf(node->id(),
2105 node->token_pos(),
2106 node->left(),
2107 node->right()->AsTypeNode()->type(),
2108 (node->kind() == Token::kISNOT));
2109 if (!IsResultNeeded(node)) {
2110 __ popl(EAX); // Pop the result of the instanceof operation.
2111 }
2112 return;
2113 }
2114
2115 if (Token::IsTypeCastOperator(node->kind())) {
2116 // This code generator is not maintained anymore.
2117 UNIMPLEMENTED();
2118 }
2119
2120 if (NodeHasClassAt(node, smi_class_, 0)) {
2121 if (GenerateSmiComparison(node)) {
2122 // The comparison was handled, code was emitted.
2123 return;
2124 }
2125 // Fall through if condition is not supported.
2126 } else if (NodeHasClassAt(node, double_class_, 0)) {
2127 // Double comparison.
2128 if (GenerateDoubleComparison(node)) {
2129 return;
2130 }
2131 } else if ((node->kind() == Token::kEQ) || (node->kind() == Token::kNE)) {
2132 // Equality, not-equality comparison of any other type.
2133 if (GenerateEqualityComparison(node)) {
2134 return;
2135 }
2136 }
2137
2138 // Fall through here if a comparison was not implemented.
2139 // TODO(srdjan): Implement for Strings.
2140 CodeGenerator::VisitComparisonNode(node);
2141 }
2142
2143
2144 void OptimizingCodeGenerator::VisitLoadIndexedNode(LoadIndexedNode* node) {
2145 const char* kMessage = "Inline indexed access";
2146 ObjectStore* object_store = Isolate::Current()->object_store();
2147 const Class& object_array_class =
2148 Class::ZoneHandle(object_store->array_class());
2149 const Class& immutable_object_array_class =
2150 Class::ZoneHandle(object_store->immutable_array_class());
2151 if (NodeHasClassAt(node, object_array_class, 0) ||
2152 NodeHasClassAt(node, immutable_object_array_class, 0)) {
2153 CodeGenInfo array_info(node->array());
2154 CodeGenInfo index_info(node->index_expr());
2155 VisitLoadTwo(node->array(), node->index_expr(), EBX, EDX);
2156 DeoptimizationBlob* deopt_blob =
2157 AddDeoptimizationBlob(node, EBX, EDX, kDeoptLoadIndexedFixedArray);
2158 const Class& test_class = NodeHasClassAt(node, object_array_class, 0) ?
2159 object_array_class : immutable_object_array_class;
2160 // Type checks of array.
2161 if (!array_info.IsClass(test_class)) {
2162 __ testl(EBX, Immediate(kSmiTagMask)); // Deoptimize if Smi.
2163 __ j(ZERO, deopt_blob->label());
2164 __ CompareClassId(EBX, test_class.id(), EAX);
2165 __ j(NOT_EQUAL, deopt_blob->label());
2166 PropagateBackLocalClass(node->array(), test_class);
2167 }
2168
2169 // Type check of index.
2170 if (!index_info.IsClass(smi_class_)) {
2171 __ testl(EDX, Immediate(kSmiTagMask));
2172 __ j(NOT_ZERO, deopt_blob->label());
2173 PropagateBackLocalClass(node->index_expr(), smi_class_);
2174 }
2175 // Range check.
2176 __ cmpl(EDX, FieldAddress(EBX, Array::length_offset()));
2177 __ j(ABOVE_EQUAL, deopt_blob->label());
2178 // Note that EDX is Smi, i.e, times 2.
2179 ASSERT(kSmiTagShift == 1);
2180 __ movl(EAX, FieldAddress(EBX, EDX, TIMES_2, sizeof(RawArray)));
2181 HandleResult(node, EAX);
2182 TraceOpt(node, kMessage);
2183 return;
2184 }
2185
2186 if (NodeHasClassAt(node, growable_object_array_class_, 0)) {
2187 CodeGenInfo array_info(node->array());
2188 CodeGenInfo index_info(node->index_expr());
2189 VisitLoadTwo(node->array(), node->index_expr(), EDX, EAX);
2190 DeoptimizationBlob* deopt_blob =
2191 AddDeoptimizationBlob(node, EDX, EAX, kDeoptLoadIndexedGrowableArray);
2192 // EAX: index, EDX: array.
2193 if (!index_info.IsClass(smi_class_)) {
2194 __ testl(EAX, Immediate(kSmiTagMask));
2195 __ j(NOT_ZERO, deopt_blob->label()); // Not Smi index.
2196 PropagateBackLocalClass(node->index_expr(), smi_class_);
2197 }
2198 if (!array_info.IsClass(growable_object_array_class_)) {
2199 __ testl(EDX, Immediate(kSmiTagMask));
2200 __ j(ZERO, deopt_blob->label()); // Array is Smi.
2201 __ CompareClassId(EDX, kGrowableObjectArray, EBX);
2202 __ j(NOT_EQUAL, deopt_blob->label()); // Not GrowableObjectArray.
2203 PropagateBackLocalClass(node->array(), growable_object_array_class_);
2204 }
2205 // Range check: deoptimize if out of bounds.
2206 __ cmpl(EAX, FieldAddress(EDX, GrowableObjectArray::length_offset()));
2207 __ j(ABOVE_EQUAL, deopt_blob->label());
2208 __ movl(EDX, FieldAddress(EDX, GrowableObjectArray::data_offset()));
2209 // Note that EAX is Smi, i.e, times 2.
2210 ASSERT(kSmiTagShift == 1);
2211 __ movl(EAX, FieldAddress(EDX, EAX, TIMES_2, sizeof(RawArray)));
2212 HandleResult(node, EAX);
2213 return;
2214 } else {
2215 // E.g., HashMap.
2216 TraceNotOpt(node, kMessage);
2217 }
2218 CodeGenerator::VisitLoadIndexedNode(node);
2219 }
2220
2221
2222 void OptimizingCodeGenerator::VisitStoreIndexedNode(StoreIndexedNode* node) {
2223 if (FLAG_enable_type_checks) {
2224 CodeGenerator::VisitStoreIndexedNode(node);
2225 return;
2226 }
2227 Class& class_of_this_array = Class::Handle();
2228 // Load array and release its CodeGenInfo as value may refer to the same
2229 // array (e.g. in a[x] += 3). Fixes issue 1570.
2230 {
2231 CodeGenInfo array_info(node->array());
2232 node->array()->Visit(this);
2233 class_of_this_array = array_info.is_class()->raw();
2234 }
2235 // TODO(srdjan): Use VisitLoadTwo and check if index is smi (CodeGenInfo).
2236 ObjectStore* object_store = Isolate::Current()->object_store();
2237 const Class& object_array_class =
2238 Class::ZoneHandle(object_store->array_class());
2239 const ICData& ic_data = node->ic_data();
2240 if (ic_data.NumberOfChecks() == 0) {
2241 VisitLoadTwo(node->index_expr(), node->value(), EBX, ECX);
2242 DeoptimizationBlob* deopt_blob =
2243 AddDeoptimizationBlob(node, EBX, ECX, kDeoptNoTypeFeedback);
2244 __ jmp(deopt_blob->label());
2245 return;
2246 }
2247
2248
2249 if (NodeHasClassAt(node, object_array_class, 0)) {
2250 // Release CodeGenInfo of index quickly as it may be used in the value,
2251 // e.g. a[i] += 3. Fixes issue 1570.
2252 bool index_is_smi = false;
2253 {
2254 CodeGenInfo index_info(node->index_expr());
2255 node->index_expr()->Visit(this);
2256 index_is_smi = index_info.IsClass(smi_class_);
2257 }
2258 VisitLoadOne(node->value(), ECX);
2259 DeoptimizationBlob* deopt_blob =
2260 AddDeoptimizationBlob(node, EAX, EBX, ECX, kDeoptStoreIndexed);
2261 __ popl(EBX); // index.
2262 __ popl(EAX); // array.
2263 // ECX: value, EBX:index, EAX: array.
2264 // Check class of array.
2265 if (class_of_this_array.raw() != object_array_class.raw()) {
2266 __ testl(EAX, Immediate(kSmiTagMask));
2267 __ j(ZERO, deopt_blob->label()); // Array is smi -> deopt.
2268 __ CompareClassId(EAX, kArray, EDX);
2269 __ j(NOT_EQUAL, deopt_blob->label()); // Not ObjectArray -> deopt.
2270 PropagateBackLocalClass(node->array(), object_array_class);
2271 }
2272 // Check class of index.
2273 if (!index_is_smi) {
2274 __ testl(EBX, Immediate(kSmiTagMask));
2275 __ j(NOT_ZERO, deopt_blob->label()); // Index not Smi -> deopt.
2276 PropagateBackLocalClass(node->index_expr(), smi_class_);
2277 }
2278 // Range check.
2279 __ cmpl(EBX, FieldAddress(EAX, Array::length_offset()));
2280 __ j(ABOVE_EQUAL, deopt_blob->label()); // Range error -> deopt.
2281 ASSERT(kSmiTagShift == 1);
2282 __ StoreIntoObject(EAX,
2283 FieldAddress(EAX, EBX, TIMES_2, sizeof(RawArray)),
2284 ECX);
2285 HandleResult(node, ECX);
2286 return;
2287 }
2288
2289 if (NodeHasClassAt(node, growable_object_array_class_, 0)) {
2290 bool index_is_smi = false;
2291 // Release CodeGenInfo of index quickly as it may be used in the value,
2292 // e.g. a[i] += 3. Fixes issue 1570.
2293 {
2294 CodeGenInfo index_info(node->index_expr());
2295 node->index_expr()->Visit(this);
2296 index_is_smi = index_info.IsClass(smi_class_);
2297 }
2298 VisitLoadOne(node->value(), ECX);
2299 DeoptimizationBlob* deopt_blob =
2300 AddDeoptimizationBlob(node, EAX, EBX, ECX, kDeoptStoreIndexed);
2301 __ popl(EBX); // index.
2302 __ popl(EAX); // array.
2303 // ECX: value, EBX:index, EAX: array, EDX: scratch.
2304 // Check class of array.
2305 if (class_of_this_array.raw() != growable_object_array_class_.raw()) {
2306 __ testl(EAX, Immediate(kSmiTagMask));
2307 __ j(ZERO, deopt_blob->label()); // Array is smi -> deopt.
2308 __ CompareClassId(EAX, kGrowableObjectArray, EDX);
2309 __ j(NOT_EQUAL, deopt_blob->label()); // Not GrowableObjectArray.
2310 PropagateBackLocalClass(node->array(), growable_object_array_class_);
2311 }
2312 // Check class of index.
2313 if (!index_is_smi) {
2314 __ testl(EBX, Immediate(kSmiTagMask));
2315 __ j(NOT_ZERO, deopt_blob->label()); // Index not Smi -> deopt.
2316 PropagateBackLocalClass(node->index_expr(), smi_class_);
2317 }
2318 // Range check: deoptimize if out of bounds.
2319 __ cmpl(EBX, FieldAddress(EAX, GrowableObjectArray::length_offset()));
2320 __ j(ABOVE_EQUAL, deopt_blob->label());
2321 __ movl(EDX, FieldAddress(EAX, GrowableObjectArray::data_offset()));
2322 // Note that EAX is Smi, i.e, times 2.
2323 ASSERT(kSmiTagShift == 1);
2324 __ StoreIntoObject(EDX,
2325 FieldAddress(EDX, EBX, TIMES_2, sizeof(RawArray)),
2326 ECX);
2327 HandleResult(node, ECX);
2328 return;
2329 }
2330 node->index_expr()->Visit(this);
2331 node->value()->Visit(this);
2332 GenerateStoreIndexed(node->id(), node->token_pos(), IsResultNeeded(node));
2333 }
2334
2335
2336 void OptimizingCodeGenerator::VisitForNode(ForNode* node) {
2337 if (FLAG_enable_type_checks) {
2338 CodeGenerator::VisitForNode(node);
2339 return;
2340 }
2341 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
2342 node->initializer()->Visit(this);
2343 SourceLabel* label = node->label();
2344 Label loop;
2345 __ Bind(&loop);
2346 if (node->condition() != NULL) {
2347 Label iterate_label;
2348 CodeGenInfo condition_info(node->condition());
2349 condition_info.set_false_label(label->break_label());
2350 condition_info.set_true_label(&iterate_label);
2351 condition_info.set_fallthrough_label(&iterate_label);
2352 node->condition()->Visit(this);
2353 if (condition_info.labels_used()) {
2354 __ Bind(&iterate_label);
2355 } else {
2356 __ popl(EAX);
2357 __ LoadObject(EDX, bool_true);
2358 __ cmpl(EAX, EDX);
2359 __ j(NOT_EQUAL, label->break_label());
2360 }
2361 }
2362 node->body()->Visit(this);
2363 HandleBackwardBranch(node->id(), node->token_pos());
2364 __ Bind(label->continue_label());
2365 node->increment()->Visit(this);
2366 __ jmp(&loop);
2367 __ Bind(label->break_label());
2368 }
2369
2370
2371 void OptimizingCodeGenerator::VisitDoWhileNode(DoWhileNode* node) {
2372 if (FLAG_enable_type_checks) {
2373 CodeGenerator::VisitDoWhileNode(node);
2374 return;
2375 }
2376 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
2377 SourceLabel* label = node->label();
2378 Label loop;
2379 __ Bind(&loop);
2380 node->body()->Visit(this);
2381 HandleBackwardBranch(node->id(), node->token_pos());
2382 __ Bind(label->continue_label());
2383 CodeGenInfo condition_info(node->condition());
2384 condition_info.set_false_label(label->break_label());
2385 condition_info.set_true_label(&loop);
2386 condition_info.set_fallthrough_label(label->break_label());
2387 node->condition()->Visit(this);
2388 if (!condition_info.labels_used()) {
2389 __ popl(EAX);
2390 __ LoadObject(EDX, bool_true);
2391 __ cmpl(EAX, EDX);
2392 __ j(EQUAL, &loop);
2393 }
2394 __ Bind(label->break_label());
2395 }
2396
2397
2398 void OptimizingCodeGenerator::VisitWhileNode(WhileNode* node) {
2399 if (FLAG_enable_type_checks) {
2400 CodeGenerator::VisitWhileNode(node);
2401 return;
2402 }
2403 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
2404 SourceLabel* label = node->label();
2405 __ Bind(label->continue_label());
2406 Label iterate_label;
2407 CodeGenInfo condition_info(node->condition());
2408 condition_info.set_false_label(label->break_label());
2409 condition_info.set_true_label(&iterate_label);
2410 condition_info.set_fallthrough_label(&iterate_label);
2411 node->condition()->Visit(this);
2412 if (condition_info.labels_used()) {
2413 __ Bind(&iterate_label);
2414 } else {
2415 __ popl(EAX);
2416 __ LoadObject(EDX, bool_true);
2417 __ cmpl(EAX, EDX);
2418 __ j(NOT_EQUAL, label->break_label());
2419 }
2420 node->body()->Visit(this);
2421 HandleBackwardBranch(node->id(), node->token_pos());
2422 __ jmp(label->continue_label());
2423 __ Bind(label->break_label());
2424 }
2425
2426
2427 void OptimizingCodeGenerator::VisitIfNode(IfNode* node) {
2428 if (FLAG_enable_type_checks) {
2429 CodeGenerator::VisitIfNode(node);
2430 return;
2431 }
2432 const Bool& bool_true = Bool::ZoneHandle(Bool::True());
2433 Label false_label, true_label, done;
2434 CodeGenInfo condition_info(node->condition());
2435 condition_info.set_false_label(&false_label);
2436 condition_info.set_true_label(&true_label);
2437 condition_info.set_fallthrough_label(&true_label);
2438 node->condition()->Visit(this);
2439 if (condition_info.labels_used()) {
2440 __ Bind(&true_label);
2441 } else {
2442 __ popl(EAX);
2443 __ CompareObject(EAX, bool_true);
2444 __ j(NOT_EQUAL, &false_label);
2445 }
2446 node->true_branch()->Visit(this);
2447 if (node->false_branch() != NULL) {
2448 Label done;
2449 __ jmp(&done);
2450 __ Bind(&false_label);
2451 node->false_branch()->Visit(this);
2452 __ Bind(&done);
2453 } else {
2454 __ Bind(&false_label);
2455 }
2456 __ Bind(&done);
2457 }
2458
2459
2460 void OptimizingCodeGenerator::GenerateDirectCall(
2461 intptr_t node_id,
2462 intptr_t token_pos,
2463 const Function& target,
2464 intptr_t arg_count,
2465 const Array& optional_argument_names) {
2466 ASSERT(!target.IsNull());
2467 const Code& code = Code::Handle(target.CurrentCode());
2468 ASSERT(!code.IsNull());
2469 ExternalLabel target_label("DirectInstanceCall", code.EntryPoint());
2470
2471 __ LoadObject(ECX, target);
2472 __ LoadObject(EDX, ArgumentsDescriptor(arg_count, optional_argument_names));
2473 __ call(&target_label);
2474 AddCurrentDescriptor(PcDescriptors::kOther, node_id, token_pos);
2475 __ addl(ESP, Immediate(arg_count * kWordSize));
2476 }
2477
2478
2479 // Generate inline cache calls instead of deoptimizing when no type feedback is
2480 // provided.
2481 // TODO(srdjan): Recompilation framework should recognize active IC calls
2482 // in optimized code and mark them for reoptimization since type feedback was
2483 // collected in the meantime.
2484 void OptimizingCodeGenerator::GenerateInlineCacheCall(
2485 intptr_t node_id,
2486 intptr_t token_pos,
2487 const ICData& ic_data,
2488 intptr_t num_args,
2489 const Array& optional_arguments_names) {
2490 __ LoadObject(ECX, ic_data);
2491 __ LoadObject(EDX, ArgumentsDescriptor(num_args, optional_arguments_names));
2492
2493 uword label_address = 0;
2494 switch (ic_data.num_args_tested()) {
2495 case 1:
2496 label_address = StubCode::OneArgCheckInlineCacheEntryPoint();
2497 break;
2498 case 2:
2499 label_address = StubCode::TwoArgsCheckInlineCacheEntryPoint();
2500 break;
2501 default:
2502 UNIMPLEMENTED();
2503 }
2504
2505 ExternalLabel target_label("InlineCache", label_address);
2506
2507 __ call(&target_label);
2508 AddCurrentDescriptor(PcDescriptors::kIcCall,
2509 node_id,
2510 token_pos);
2511 __ addl(ESP, Immediate(num_args * kWordSize));
2512 }
2513
2514
2515 // Normalizes the ic_data class/target pairs:
2516 // - If Smi class exists, make it the first one.
2517 // - If 'null_target' not null, append null-class/'null_target'
2518 void OptimizingCodeGenerator::NormalizeClassChecks(
2519 const ICData& ic_data,
2520 const Function& null_target,
2521 GrowableArray<const Class*>* classes,
2522 GrowableArray<const Function*>* targets) {
2523 ASSERT(classes != NULL);
2524 ASSERT(targets != NULL);
2525 // Check if we can add Smi class in front.
2526 Function& smi_target = Function::ZoneHandle();
2527 for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
2528 GrowableArray<intptr_t> test_class_ids;
2529 ic_data.GetCheckAt(i, &test_class_ids, &smi_target);
2530 if (test_class_ids[0] == kSmi) {
2531 classes->Add(&Class::ZoneHandle(smi_class_.raw()));
2532 targets->Add(&Function::ZoneHandle(smi_target.raw()));
2533 break;
2534 }
2535 }
2536 // Add all classes except Smi.
2537 for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
2538 Function& target = Function::ZoneHandle();
2539 GrowableArray<intptr_t> test_class_ids;
2540 ic_data.GetCheckAt(i, &test_class_ids, &target);
2541 ASSERT(test_class_ids[0] != kNullClassId);
2542 if (test_class_ids[0] != kSmi) {
2543 const Class& cls =
2544 Class::Handle(Isolate::Current()->class_table()->At(
2545 test_class_ids[0]));
2546 ASSERT(!cls.IsNull());
2547 ASSERT(!target.IsNull());
2548 classes->Add(&cls);
2549 targets->Add(&target);
2550 }
2551 }
2552 // Do not add a target that has not been compiled yet.
2553 if (!null_target.IsNull() && null_target.HasCode()) {
2554 ASSERT(null_target.IsZoneHandle());
2555 classes->Add(&Class::ZoneHandle(Object::null_class()));
2556 targets->Add(&null_target);
2557 }
2558 }
2559
2560
2561 // Use IC data in 'node' to issues checks and calls.
2562 // IC data can contain one or more argument checks.
2563 void OptimizingCodeGenerator::GenerateCheckedInstanceCalls(
2564 AstNode* node,
2565 AstNode* receiver,
2566 intptr_t token_pos,
2567 intptr_t num_args,
2568 const Array& optional_arguments_names) {
2569 ASSERT(node != NULL);
2570 ASSERT(receiver != NULL);
2571 ASSERT(num_args > 0);
2572 const ICData& ic_data = node->ic_data();
2573 if (ic_data.NumberOfChecks() == 0) {
2574 // No type feedback means node was never executed. However that can be
2575 // a common case especially in case of large switch statements.
2576 // Use a special inline cache call which can help us decide when to
2577 // re-optimize this optiumized function.
2578 GenerateInlineCacheCall(
2579 node->id(), token_pos, ic_data, num_args, optional_arguments_names);
2580 return;
2581 }
2582
2583 Function& target_for_null = Function::ZoneHandle();
2584 ObjectStore* object_store = Isolate::Current()->object_store();
2585 int num_optional_args =
2586 optional_arguments_names.IsNull() ? 0 : optional_arguments_names.Length();
2587 target_for_null = Resolver::ResolveDynamicForReceiverClass(
2588 Class::Handle(object_store->object_class()),
2589 String::Handle(ic_data.target_name()),
2590 num_args,
2591 num_optional_args);
2592 GrowableArray<const Class*> classes;
2593 GrowableArray<const Function*> targets;
2594 // Make Smi class the first one, if it is in the list.
2595 NormalizeClassChecks(ic_data, target_for_null, &classes, &targets);
2596 ASSERT(!classes.is_empty());
2597 ASSERT(classes.length() == targets.length());
2598 intptr_t start_ix = 0;
2599
2600 Label done;
2601 __ movl(EAX, Address(ESP, (num_args - 1) * kWordSize)); // Load receiver.
2602 if (classes[0]->raw() == smi_class_.raw()) {
2603 start_ix++;
2604 // Smi test is needed.
2605 __ testl(EAX, Immediate(kSmiTagMask));
2606 if (classes.length() == 1) {
2607 // Only Smi test.
2608 DeoptimizationBlob* deopt_blob =
2609 AddDeoptimizationBlob(node, kDeoptPolymorphicInstanceCallSmiOnly);
2610 __ j(NOT_ZERO, deopt_blob->label());
2611 GenerateDirectCall(node->id(),
2612 token_pos,
2613 *targets[0],
2614 num_args,
2615 optional_arguments_names);
2616 return;
2617 }
2618 Label not_smi;
2619 __ j(NOT_ZERO, &not_smi);
2620 GenerateDirectCall(node->id(),
2621 token_pos,
2622 *targets[0],
2623 num_args,
2624 optional_arguments_names);
2625 __ jmp(&done);
2626 __ Bind(&not_smi); // Continue with other test below.
2627 } else if (NodeMayBeSmi(receiver)) {
2628 DeoptimizationBlob* deopt_blob =
2629 AddDeoptimizationBlob(node, kDeoptPolymorphicInstanceCallSmiFail);
2630 __ testl(EAX, Immediate(kSmiTagMask));
2631 __ j(ZERO, deopt_blob->label());
2632 } else {
2633 // Receiver cannot be Smi, no need to test it.
2634 }
2635 __ LoadClassId(EAX, EAX); // Receiver's class id.
2636 for (intptr_t i = start_ix; i < classes.length(); i++) {
2637 const Class& cls = *classes[i];
2638 const Function& target = *targets[i];
2639 __ cmpl(EAX, Immediate(cls.id()));
2640 if (i == (classes.length() - 1)) {
2641 // Last check.
2642 DeoptimizationBlob* deopt_blob =
2643 AddDeoptimizationBlob(node, kDeoptPolymorphicInstanceCallTestFail);
2644 __ j(NOT_EQUAL, deopt_blob->label());
2645 GenerateDirectCall(node->id(),
2646 token_pos,
2647 target,
2648 num_args,
2649 optional_arguments_names);
2650 } else {
2651 Label next;
2652 __ j(NOT_EQUAL, &next);
2653 GenerateDirectCall(node->id(),
2654 token_pos,
2655 target,
2656 num_args,
2657 optional_arguments_names);
2658 __ jmp(&done);
2659 __ Bind(&next);
2660 }
2661 }
2662 __ Bind(&done);
2663 }
2664
2665
2666 void OptimizingCodeGenerator::VisitInstanceCallNode(InstanceCallNode* node) {
2667 const int number_of_arguments = node->arguments()->length() + 1;
2668 // Compute the receiver object and pass it as first argument to call.
2669 node->receiver()->Visit(this);
2670 // Now compute rest of the arguments to the call.
2671 node->arguments()->Visit(this);
2672 if (TryInlineInstanceCall(node)) {
2673 // Instance call is inlined.
2674 } else {
2675 GenerateCheckedInstanceCalls(node,
2676 node->receiver(),
2677 node->token_pos(),
2678 number_of_arguments,
2679 node->arguments()->names());
2680 }
2681 // Result is in EAX.
2682 HandleResult(node, EAX);
2683 }
2684
2685
2686 // Returns true if an instance call was replaced with its intrinsic.
2687 // Returns result in EAX.
2688 bool OptimizingCodeGenerator::TryInlineInstanceCall(InstanceCallNode* node) {
2689 const ZoneGrowableArray<const Class*>* classes = CollectedClassesAtNode(node);
2690 if ((classes != NULL) && (classes->length() == 1)) {
2691 const int num_arguments = node->arguments()->length() + 1;
2692 const int num_named_arguments = node->arguments()->names().IsNull() ?
2693 0 : node->arguments()->names().Length();
2694 const Function& target = Function::ZoneHandle(
2695 Resolver::ResolveDynamicForReceiverClass(*(*classes)[0],
2696 node->function_name(),
2697 num_arguments,
2698 num_named_arguments));
2699 Recognizer::Kind recognized = Recognizer::RecognizeKind(target);
2700 if (FLAG_trace_optimization) {
2701 OS::Print("Monomorphic inline candidate: %s -> %s\n",
2702 target.ToFullyQualifiedCString(),
2703 Recognizer::KindToCString(recognized));
2704 }
2705 if ((recognized == Recognizer::kIntegerToDouble) &&
2706 NodeHasClassAt(node, smi_class_, 0)) {
2707 // TODO(srdjan): Check if we could use temporary double instead of
2708 // allocating a new object every time.
2709 const Code& stub =
2710 Code::Handle(StubCode::GetAllocationStubForClass(double_class_));
2711 const ExternalLabel label(double_class_.ToCString(), stub.EntryPoint());
2712 GenerateCall(node->token_pos(), &label, PcDescriptors::kOther);
2713 // EAX is double object.
2714 DeoptimizationBlob* deopt_blob =
2715 AddDeoptimizationBlob(node, EBX, kDeoptIntegerToDouble);
2716 __ popl(EBX); // Receiver
2717 __ testl(EBX, Immediate(kSmiTagMask));
2718 __ j(NOT_ZERO, deopt_blob->label()); // Deoptimize if not Smi.
2719 __ SmiUntag(EBX);
2720 __ cvtsi2sd(XMM0, EBX);
2721 __ movsd(FieldAddress(EAX, Double::value_offset()), XMM0);
2722 return true;
2723 }
2724
2725 if ((recognized == Recognizer::kDoubleToDouble) &&
2726 NodeHasClassAt(node, double_class_, 0)) {
2727 DeoptimizationBlob* deopt_blob =
2728 AddDeoptimizationBlob(node, EAX, kDeoptDoubleToDouble);
2729 __ popl(EAX);
2730 CheckIfDoubleOrSmi(EAX, EBX, deopt_blob->label(), deopt_blob->label());
2731 return true;
2732 }
2733 }
2734 return false;
2735 }
2736
2737
2738 // TODO(srdjan): For Math.sqrt read type feedback in Math.sqrt and decide
2739 // if the argument is double, smi or something else.
2740 bool OptimizingCodeGenerator::TryInlineStaticCall(StaticCallNode* node) {
2741 Recognizer::Kind recognized = Recognizer::RecognizeKind(node->function());
2742 if (recognized == Recognizer::kMathSqrt) {
2743 Label smi_to_double, call_method, done;
2744 __ movl(EAX, Address(ESP, 0));
2745 CheckIfDoubleOrSmi(EAX, EBX, &smi_to_double, &call_method);
2746 __ movsd(XMM1, FieldAddress(EAX, Double::value_offset()));
2747 __ sqrtsd(XMM0, XMM1);
2748 AssemblerMacros::TryAllocate(assembler_,
2749 double_class_,
2750 &call_method,
2751 EAX); // Result register.
2752 __ movsd(FieldAddress(EAX, Double::value_offset()), XMM0);
2753 __ jmp(&done);
2754 __ Bind(&smi_to_double);
2755 __ Bind(&call_method);
2756 __ LoadObject(ECX, node->function());
2757 __ LoadObject(EDX, ArgumentsDescriptor(node->arguments()->length(),
2758 node->arguments()->names()));
2759 GenerateCall(node->token_pos(), &StubCode::CallStaticFunctionLabel(),
2760 PcDescriptors::kFuncCall);
2761 __ Bind(&done);
2762 return true;
2763 }
2764 return false;
2765 }
2766
2767
2768 void OptimizingCodeGenerator::VisitStaticCallNode(StaticCallNode* node) {
2769 node->arguments()->Visit(this);
2770 if (TryInlineStaticCall(node)) {
2771 // Static method is inlined, result is in EAX.
2772 } else {
2773 __ LoadObject(ECX, node->function());
2774 __ LoadObject(EDX, ArgumentsDescriptor(node->arguments()->length(),
2775 node->arguments()->names()));
2776 GenerateCall(node->token_pos(), &StubCode::CallStaticFunctionLabel(),
2777 PcDescriptors::kFuncCall);
2778 }
2779 __ addl(ESP, Immediate(node->arguments()->length() * kWordSize));
2780 // Result is in EAX.
2781 HandleResult(node, EAX);
2782 }
2783
2784
2785 void OptimizingCodeGenerator::VisitReturnNode(ReturnNode* node) {
2786 if ((node->inlined_finally_list_length() > 0) || FLAG_enable_type_checks) {
2787 CodeGenerator::VisitReturnNode(node);
2788 return;
2789 }
2790 ASSERT(!IsResultNeeded(node));
2791 ASSERT(node->value() != NULL);
2792 CodeGenInfo value_info(node->value());
2793 value_info.set_request_result_in_eax(true);
2794 node->value()->Visit(this);
2795 if (!value_info.result_returned_in_eax()) {
2796 __ popl(EAX);
2797 }
2798 GenerateReturnEpilog(node);
2799 }
2800
2801
2802 void OptimizingCodeGenerator::VisitSequenceNode(SequenceNode* node_sequence) {
2803 // TODO(srdjan): Allow limited forwarding of types across sequence nodes.
2804 classes_for_locals_->Clear();
2805 const intptr_t num_context_variables = (node_sequence->scope() != NULL) ?
2806 node_sequence->scope()->num_context_variables() : 0;
2807 if (FLAG_enable_type_checks || (num_context_variables > 0)) {
2808 CodeGenerator::VisitSequenceNode(node_sequence);
2809 return;
2810 }
2811 for (int i = 0; i < node_sequence->length(); i++) {
2812 AstNode* child_node = node_sequence->NodeAt(i);
2813 state()->set_root_node(child_node);
2814 child_node->Visit(this);
2815 }
2816 if (node_sequence->label() != NULL) {
2817 __ Bind(node_sequence->label()->break_label());
2818 }
2819 classes_for_locals_->Clear();
2820 }
2821
2822
2823 void OptimizingCodeGenerator::VisitStoreInstanceFieldNode(
2824 StoreInstanceFieldNode* node) {
2825 if (FLAG_enable_type_checks) {
2826 CodeGenerator::VisitStoreInstanceFieldNode(node);
2827 return;
2828 }
2829 VisitLoadTwo(node->instance(), node->value(), EDX, EAX);
2830 __ StoreIntoObject(EDX, FieldAddress(EDX, node->field().Offset()), EAX);
2831 ASSERT(!IsResultNeeded(node));
2832 }
2833
2834
2835 void OptimizingCodeGenerator::VisitCatchClauseNode(CatchClauseNode* node) {
2836 // TODO(srdjan): Set classes for locals.
2837 classes_for_locals_->Clear();
2838 CodeGenerator::VisitCatchClauseNode(node);
2839 }
2840
2841
2842 void OptimizingCodeGenerator::VisitTryCatchNode(TryCatchNode* node) {
2843 // TODO(srdjan): Set classes for locals.
2844 classes_for_locals_->Clear();
2845 CodeGenerator::VisitTryCatchNode(node);
2846 }
2847
2848
2849 void OptimizingCodeGenerator::VisitUnaryOpNode(UnaryOpNode* node) {
2850 // TODO(srdjan): Test in checked mode if value is Boolean, throw error
2851 // otherwise.
2852 if (FLAG_enable_type_checks && node->kind() == Token::kNOT) {
2853 CodeGenerator::VisitUnaryOpNode(node);
2854 return;
2855 }
2856 // TODO(srdjan): Jump directly to labels instead of returning a boolean.
2857 if (node->kind() == Token::kNOT) {
2858 // Only a true bool returns false, everything else is true.
2859 CodeGenInfo info(node->operand());
2860 VisitLoadOne(node->operand(), EDX);
2861 Label done;
2862 __ LoadObject(EAX, Bool::ZoneHandle(Bool::True()));
2863 __ cmpl(EDX, EAX);
2864 __ j(NOT_EQUAL, &done, Assembler::kNearJump);
2865 __ LoadObject(EAX, Bool::ZoneHandle(Bool::False()));
2866 __ Bind(&done);
2867 HandleResult(node, EAX);
2868 return;
2869 }
2870
2871 if ((node->kind() == Token::kSUB) || (node->kind() == Token::kBIT_NOT)) {
2872 if (NodeHasClassAt(node, smi_class_, 0)) {
2873 const ICData& ic_data = node->ic_data();
2874 ASSERT(ic_data.num_args_tested() == 1);
2875 GenerateSmiUnaryOp(node);
2876 return;
2877 }
2878 }
2879 if (node->kind() == Token::kSUB) {
2880 if (NodeHasClassAt(node, double_class_, 0)) {
2881 const ICData& ic_data = node->ic_data();
2882 ASSERT(ic_data.num_args_tested() == 1);
2883 GenerateDoubleUnaryOp(node);
2884 return;
2885 }
2886 }
2887 // TODO(srdjan): Implement unary kSUB (negate) Mint.
2888 CodeGenerator::VisitUnaryOpNode(node);
2889 }
2890
2891
2892 } // namespace dart
2893
2894 #endif // defined TARGET_ARCH_IA32
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698