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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: runtime/vm/opt_code_generator_ia32.cc
===================================================================
--- runtime/vm/opt_code_generator_ia32.cc (revision 9088)
+++ runtime/vm/opt_code_generator_ia32.cc (working copy)
@@ -1,2894 +0,0 @@
-// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-#include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32.
-#if defined(TARGET_ARCH_IA32)
-
-#include "vm/opt_code_generator.h"
-
-#include "vm/assembler_macros.h"
-#include "vm/ast_printer.h"
-#include "vm/object.h"
-#include "vm/object_store.h"
-#include "vm/resolver.h"
-#include "vm/stub_code.h"
-
-namespace dart {
-
-#define __ assembler_->
-
-DEFINE_FLAG(bool, trace_optimization, false, "Trace optimizations.");
-DECLARE_FLAG(bool, enable_type_checks);
-
-
-// Property list to be used in CodeGenInfo. Each property has a setter
-// and a getter of specified type and name.
-// (name, type, default)
-#define PROPERTY_LIST(V) \
- V(is_temp, bool, false) \
- V(allow_temp, bool, false) \
- V(true_label, Label*, NULL) \
- V(false_label, Label*, NULL) \
- V(labels_used, bool, false) \
- V(request_result_in_eax, bool, false) \
- V(result_returned_in_eax, bool, false) \
- V(fallthrough_label, Label*, NULL) \
- V(is_class, const Class*, &Class::ZoneHandle()) \
-
-
-// Class holding information being passed from source to destination.
-// Add needed properties in the PROPERTY_LIST above.
-class CodeGenInfo : public ValueObject {
- public:
- explicit CodeGenInfo(AstNode* node)
- : node_(node), data_(4) {
- ASSERT(node != NULL);
- ASSERT(node->info() == NULL);
- node->set_info(this);
- }
-
- ~CodeGenInfo() {
- ASSERT(node_->info() == this);
- node_->set_info(NULL);
- }
-
- bool IsClass(const Class& cls) const {
- return is_class()->raw() == cls.raw();
- }
-
-#define GETTER(name, type, default) \
- type name() const { \
- Pair* p = Get(k_##name); \
- return p == NULL ? default : p->name; \
- }
-PROPERTY_LIST(GETTER)
-#undef GETTER
-
-#define SETTER(name, type, default) \
- void set_##name(type value) { \
- ASSERT(Get(k_##name) == NULL); \
- Pair p; \
- p.kind = k_##name; \
- p.name = value; \
- data_.Add(p); \
- }
-PROPERTY_LIST(SETTER)
-#undef SETTER
-
- private:
- enum Kind {
-#define DEFINE_KIND(name, type, value) k_##name,
-PROPERTY_LIST(DEFINE_KIND)
-#undef DEFINE_KIND
- };
- struct Pair {
- Kind kind;
- union {
-#define UNION_ELEMENTS(name, type, value) type name;
-PROPERTY_LIST(UNION_ELEMENTS)
-#undef UNION_ELEMENTS
- };
- };
-
- Pair* Get(Kind kind) const {
- for (int i = 0; i < data_.length(); i++) {
- if (data_[i].kind == kind) {
- return &data_[i];
- }
- }
- return NULL;
- }
- AstNode* node_;
- GrowableArray<Pair> data_;
- DISALLOW_COPY_AND_ASSIGN(CodeGenInfo);
-};
-
-
-// Code that calls the deoptimizer, emitted as deferred code (out of line).
-// Specify the corresponding 'node' and the registers that need to
-// be pushed for the deoptimization point in unoptimized code.
-class DeoptimizationBlob : public ZoneAllocated {
- public:
- DeoptimizationBlob(AstNode* node, DeoptReasonId deopt_reason_id)
- : node_(node),
- registers_(2),
- label_(),
- deopt_reason_id_(deopt_reason_id) {}
-
- void Push(Register reg) { registers_.Add(reg); }
-
- void Generate(OptimizingCodeGenerator* codegen) {
- codegen->assembler()->Bind(&label_);
- for (int i = 0; i < registers_.length(); i++) {
- codegen->assembler()->pushl(registers_[i]);
- }
- codegen->assembler()->movl(EAX, Immediate(Smi::RawValue(deopt_reason_id_)));
- codegen->CallDeoptimize(node_->id(), node_->token_pos());
-#if defined(DEBUG)
- // Check that deoptimization point exists in unoptimized code.
- const Code& unoptimized_code =
- Code::Handle(codegen->parsed_function().function().unoptimized_code());
- ASSERT(!unoptimized_code.IsNull());
- uword continue_at_pc =
- unoptimized_code.GetDeoptPcAtNodeId(node_->id());
- ASSERT(continue_at_pc != 0);
-#endif // DEBUG
- }
-
- // Jump to this label to deoptimize.
- Label* label() { return &label_; }
-
- private:
- const AstNode* node_;
- GrowableArray<Register> registers_;
- Label label_;
- DeoptReasonId deopt_reason_id_;
-
- DISALLOW_COPY_AND_ASSIGN(DeoptimizationBlob);
-};
-
-// TODO(srdjan): Add String_charCodeAt, String_hashCode.
-
-#define RECOGNIZED_LIST(V) \
- V(ObjectArray, get:length, ObjectArrayLength) \
- V(GrowableObjectArray, get:length, GrowableArrayLength) \
- V(StringBase, get:length, StringBaseLength) \
- V(IntegerImplementation, toDouble, IntegerToDouble) \
- V(Double, toDouble, DoubleToDouble) \
- V(Math, sqrt, MathSqrt) \
-
-// Class that recognizes the name and owner of a function and returns the
-// corresponding enum. See RECOGNIZED_LIST above for list of recognizable
-// functions.
-class Recognizer : public AllStatic {
- public:
- enum Kind {
- kUnknown,
-#define DEFINE_ENUM_LIST(class_name, function_name, enum_name) k##enum_name,
-RECOGNIZED_LIST(DEFINE_ENUM_LIST)
-#undef DEFINE_ENUM_LIST
- };
-
- // TODO(srdjan): Check that the library is the coreimpl one.
- static Kind RecognizeKind(const Function& function) {
- const String& recognize_name = String::Handle(function.name());
- const String& recognize_class =
- String::Handle(Class::Handle(function.owner()).Name());
- String& test_function_name = String::Handle();
- String& test_class_name = String::Handle();
-#define RECOGNIZE_FUNCTION(class_name, function_name, enum_name) \
- test_function_name = String::NewSymbol(#function_name); \
- test_class_name = String::NewSymbol(#class_name); \
- if (recognize_name.Equals(test_function_name) && \
- recognize_class.Equals(test_class_name)) { \
- return k##enum_name; \
- }
-RECOGNIZED_LIST(RECOGNIZE_FUNCTION)
-#undef RECOGNIZE_FUNCTION
- return kUnknown;
- }
-
- static const char* KindToCString(Kind kind) {
-#define KIND_TO_STRING(class_name, function_name, enum_name) \
- if (kind == k##enum_name) return #enum_name;
-RECOGNIZED_LIST(KIND_TO_STRING)
-#undef KIND_TO_STRING
- return "?";
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(Recognizer);
-};
-
-
-// Maintain classes of locals as defined by a store to that local.
-// A simple initial implementation, memorizes last typed stores. Does not
-// scale well for large code pieces. This will be replaced by SSA based
-// type propagation.
-class ClassesForLocals : public ZoneAllocated {
- public:
- ClassesForLocals() : classes_(), locals_() {}
- void SetLocalType(const LocalVariable& local, const Class& cls) {
- classes_.Add(&cls);
- locals_.Add(&local);
- }
- // If no type is stored/known, we return a null class in 'cls'.
- void GetLocalClass(const LocalVariable& local, const Class** cls) const {
- for (intptr_t i = locals_.length() - 1; i >=0; i--) {
- if (locals_[i]->Equals(local)) {
- *cls = classes_[i];
- return;
- }
- }
- *cls = &Class::ZoneHandle();
- }
-
- void Clear() {
- classes_.Clear();
- locals_.Clear();
- }
-
- private:
- GrowableArray<const Class*> classes_;
- GrowableArray<const LocalVariable*> locals_;
-
- DISALLOW_COPY_AND_ASSIGN(ClassesForLocals);
-};
-
-
-OptimizingCodeGenerator::OptimizingCodeGenerator(
- Assembler* assembler, const ParsedFunction& parsed_function)
- : CodeGenerator(assembler, parsed_function),
- deoptimization_blobs_(4),
- classes_for_locals_(NULL),
- smi_class_(Class::ZoneHandle(Isolate::Current()->object_store()
- ->smi_class())),
- double_class_(Class::ZoneHandle(Isolate::Current()->object_store()
- ->double_class())),
- growable_object_array_class_(Class::ZoneHandle(Isolate::Current()
- ->object_store()->growable_object_array_class())) {
- ASSERT(parsed_function.function().is_optimizable());
-}
-
-
-void OptimizingCodeGenerator::InitGenerator() {
- CodeGenerator::InitGenerator();
- classes_for_locals_ = new ClassesForLocals();
-}
-
-
-DeoptimizationBlob*
-OptimizingCodeGenerator::AddDeoptimizationBlob(AstNode* node,
- DeoptReasonId reason_id) {
- DeoptimizationBlob* d = new DeoptimizationBlob(node, reason_id);
- deoptimization_blobs_.Add(d);
- return d;
-}
-
-
-DeoptimizationBlob*
-OptimizingCodeGenerator::AddDeoptimizationBlob(AstNode* node,
- Register reg,
- DeoptReasonId reason_id) {
- DeoptimizationBlob* d = AddDeoptimizationBlob(node, reason_id);
- d->Push(reg);
- return d;
-}
-
-
-DeoptimizationBlob*
-OptimizingCodeGenerator::AddDeoptimizationBlob(AstNode* node,
- Register reg1,
- Register reg2,
- DeoptReasonId reason_id) {
- DeoptimizationBlob* d = AddDeoptimizationBlob(node, reason_id);
- d->Push(reg1);
- d->Push(reg2);
- return d;
-}
-
-
-DeoptimizationBlob*
-OptimizingCodeGenerator::AddDeoptimizationBlob(AstNode* node,
- Register reg1,
- Register reg2,
- Register reg3,
- DeoptReasonId reason_id) {
- DeoptimizationBlob* d = AddDeoptimizationBlob(node, reason_id);
- d->Push(reg1);
- d->Push(reg2);
- d->Push(reg3);
- return d;
-}
-
-
-void OptimizingCodeGenerator::GenerateDeferredCode() {
- CodeGenerator::GenerateDeferredCode();
- for (int i = 0; i < deoptimization_blobs_.length(); i++) {
- deoptimization_blobs_[i]->Generate(this);
- }
-}
-
-
-bool OptimizingCodeGenerator::IsResultInEaxRequested(AstNode* node) const {
- return (node->info() != NULL) && node->info()->request_result_in_eax();
-}
-
-
-static const ZoneGrowableArray<const Class*>*
- CollectedClassesAtNode(AstNode* node) {
- ZoneGrowableArray<const Class*>* result =
- new ZoneGrowableArray<const Class*>();
- const ICData& ic_data = node->ic_data();
- if (ic_data.NumberOfChecks() == 0) {
- return result;
- }
- ASSERT(ic_data.num_args_tested() == 1);
- Function& target = Function::Handle();
- for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
- intptr_t class_id;
- ic_data.GetOneClassCheckAt(i, &class_id, &target);
- result->Add(&Class::ZoneHandle(
- Isolate::Current()->class_table()->At(class_id)));
- }
- return result;
-}
-
-
-// Debugging helper function. TODO(srdjan): Remove
-void OptimizingCodeGenerator::PrintCollectedClasses(AstNode* node) {
- const ICData& ic_data = node->ic_data();
- OS::Print("Collected classes id %d num: %d\n",
- node->id(), ic_data.NumberOfChecks());
-}
-
-
-void OptimizingCodeGenerator::TraceOpt(AstNode* node, const char* message) {
- if (FLAG_trace_optimization) {
- OS::Print("Opt node ix: %d; %s\n", node->token_pos(), message);
- }
-}
-
-
-void OptimizingCodeGenerator::TraceNotOpt(AstNode* node, const char* message) {
- if (FLAG_trace_optimization) {
- OS::Print("NOTOpt node ix: %d; %s: ", node->token_pos(), message);
- AstPrinter::PrintNode(node);
- OS::Print("\n");
- }
-}
-
-
-void OptimizingCodeGenerator::CallDeoptimize(intptr_t node_id,
- intptr_t token_pos) {
- __ call(&StubCode::DeoptimizeLabel());
- AddCurrentDescriptor(PcDescriptors::kOther, node_id, token_pos);
-#if defined(DEBUG)
- __ int3();
-#endif
-}
-
-
-// Quick loads do not clobber registers.
-static bool IsQuickLoad(AstNode* node) {
- if (node->IsLoadLocalNode() && (!node->AsLoadLocalNode()->HasPseudo())) {
- return true;
- }
- return node->IsLiteralNode();
-}
-
-
-// Method is closely tied to "VisitLoadTwo".
-void OptimizingCodeGenerator::VisitLoadOne(AstNode* node, Register reg) {
- if (!IsQuickLoad(node)) {
- node->Visit(this);
- __ popl(reg);
- return;
- }
- if (node->IsLoadLocalNode()) {
- LoadLocalNode* local_node = node->AsLoadLocalNode();
- ASSERT(local_node != NULL);
- GenerateLoadVariable(reg, local_node->local());
- if (node->info() != NULL) {
- const Class* cls = NULL;
- classes_for_locals_->GetLocalClass(local_node->local(), &cls);
- if (cls != NULL) {
- node->info()->set_is_class(cls);
- }
- }
- return;
- }
- if (node->IsLiteralNode()) {
- LiteralNode* literal_node = node->AsLiteralNode();
- ASSERT(literal_node != NULL);
- __ LoadObject(reg, literal_node->literal());
- if (node->info() != NULL) {
- const Object& literal = literal_node->literal();
- if (literal.IsSmi()) {
- node->info()->set_is_class(&smi_class_);
- } else if (literal.IsDouble()) {
- node->info()->set_is_class(&double_class_);
- }
- }
- return;
- }
- UNREACHABLE();
-}
-
-
-// Method is closely tied to "VisitLoadOne".
-void OptimizingCodeGenerator::VisitLoadTwo(AstNode* left,
- AstNode* right,
- Register left_reg,
- Register right_reg) {
- ASSERT(left_reg != right_reg);
- if (IsQuickLoad(right)) {
-#if defined(DEBUG)
- // Verify that left_reg does not get clobbered by VisitLoadOne(right, ...).
- VisitLoadOne(left, left_reg);
- __ pushl(left_reg);
- VisitLoadOne(right, right_reg);
- __ cmpl(left_reg, Address(ESP, 0));
- Label ok;
- __ j(EQUAL, &ok, Assembler::kNearJump);
- __ Stop("Internal error at VisitLoadTwo");
- __ Bind(&ok);
- __ popl(left_reg);
-#else
- VisitLoadOne(left, left_reg);
- VisitLoadOne(right, right_reg);
-#endif
- return;
- }
- left->Visit(this);
- VisitLoadOne(right, right_reg);
- __ popl(left_reg);
-}
-
-
-void OptimizingCodeGenerator::VisitLiteralNode(LiteralNode* node) {
- if (!IsResultNeeded(node)) return;
- const Object& literal = node->literal();
- if (literal.IsSmi()) {
- if (node->info() != NULL) {
- node->info()->set_is_class(&smi_class_);
- }
- if (IsResultInEaxRequested(node)) {
- __ movl(EAX, Immediate(reinterpret_cast<int32_t>(literal.raw())));
- node->info()->set_result_returned_in_eax(true);
- } else {
- __ pushl(Immediate(reinterpret_cast<int32_t>(literal.raw())));
- }
- } else {
- if ((node->info() != NULL) && literal.IsDouble()) {
- node->info()->set_is_class(&double_class_);
- }
- if (IsResultInEaxRequested(node)) {
- __ LoadObject(EAX, literal);
- node->info()->set_result_returned_in_eax(true);
- } else {
- __ PushObject(literal);
- }
- }
-}
-
-
-void OptimizingCodeGenerator::VisitLoadLocalNode(LoadLocalNode* node) {
- if (node->HasPseudo()) {
- node->pseudo()->Visit(this);
- __ popl(EAX);
- }
- if (!IsResultNeeded(node)) return;
- if (IsResultInEaxRequested(node)) {
- GenerateLoadVariable(EAX, node->local());
- node->info()->set_result_returned_in_eax(true);
- } else {
- GeneratePushVariable(node->local(), EAX);
- }
- if (node->info() != NULL) {
- const Class* cls = NULL;
- classes_for_locals_->GetLocalClass(node->local(), &cls);
- if (cls != NULL) {
- node->info()->set_is_class(cls);
- }
- }
-}
-
-
-void OptimizingCodeGenerator::HandleResult(AstNode* node, Register result_reg) {
- if (IsResultNeeded(node)) {
- if (IsResultInEaxRequested(node)) {
- if (result_reg != EAX) {
- __ movl(EAX, result_reg);
- }
- node->info()->set_result_returned_in_eax(true);
- } else {
- __ pushl(result_reg);
- }
- }
-}
-
-
-void OptimizingCodeGenerator::VisitStoreLocalNode(StoreLocalNode* node) {
- if (FLAG_enable_type_checks) {
- CodeGenerator::VisitStoreLocalNode(node);
- classes_for_locals_->SetLocalType(node->local(), Class::ZoneHandle());
- return;
- }
- CodeGenInfo value_info(node->value());
- value_info.set_allow_temp(false);
- value_info.set_request_result_in_eax(true);
- node->value()->Visit(this);
- if (!value_info.result_returned_in_eax()) {
- __ popl(EAX);
- }
- CodeGenerator::GenerateStoreVariable(node->local(), EAX, EDX);
- HandleResult(node, EAX);
- classes_for_locals_->SetLocalType(node->local(), *value_info.is_class());
-}
-
-
-static bool NodeHasBothReceiverClasses(AstNode* node,
- const Class& cls1,
- const Class& cls2) {
- ASSERT(node != NULL);
- ASSERT(!cls1.IsNull() && !cls2.IsNull());
- const ICData& ic_data = node->ic_data();
- bool cls1_found = false;
- bool cls2_found = false;
- for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
- GrowableArray<intptr_t> class_ids;
- Function& target = Function::Handle();
- ic_data.GetCheckAt(i, &class_ids, &target);
- if (!class_ids.is_empty()) {
- if (class_ids[0] == cls1.id()) {
- cls1_found = true;
- }
- if (class_ids[0] == cls2.id()) {
- cls2_found = true;
- }
- if (cls1_found && cls2_found) {
- return true;
- }
- }
- }
- return false;
-}
-
-
-// Look only at the first class in all check groups. Returns true if all
-// receiver classes are 'cls'.
-static bool NodeHasClassAt(AstNode* node,
- const Class& cls,
- intptr_t arg_index) {
- ASSERT(node != NULL);
- ASSERT(!cls.IsNull());
- const ICData& ic_data = node->ic_data();
- if (ic_data.NumberOfChecks() == 0) {
- return false;
- }
- ASSERT(ic_data.num_args_tested() > arg_index);
- for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
- GrowableArray<intptr_t> class_ids;
- Function& target = Function::Handle();
- ic_data.GetCheckAt(i, &class_ids, &target);
- if (class_ids.is_empty()) {
- return false;
- }
- if (class_ids[arg_index] != cls.id()) {
- return false;
- }
- }
- return true;
-}
-
-
-// IC data may have only one check, and it has to contain the two classes in
-// specified order.
-static bool NodeHasTwoClasses(AstNode* node,
- const Class& cls0,
- const Class& cls1) {
- ASSERT(node != NULL);
- ASSERT(!cls0.IsNull() && !cls1.IsNull());
- const ICData& ic_data = node->ic_data();
- ASSERT(ic_data.num_args_tested() == 2);
- if (ic_data.NumberOfChecks() != 1) {
- return false;
- }
- Function& target = Function::Handle();
- GrowableArray<intptr_t> class_ids;
- ic_data.GetCheckAt(0, &class_ids, &target);
- if ((cls0.id() == class_ids[0]) && (cls1.id() == class_ids[1])) {
- return true;
- }
- return false;
-}
-
-
-// SHL: Implement with slow case so that it works both with Smi and Mint types.
-// Result is in EAX. Mangles ECX, EBX, EDX.
-void OptimizingCodeGenerator::GenerateSmiShiftBinaryOp(BinaryOpNode* node) {
- if (node->kind() == Token::kSHR) {
- // TODO(srdjan): Implement for Mint?
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, EAX, ECX, kDeoptSAR);
- CodeGenInfo left_info(node->left());
- CodeGenInfo right_info(node->right());
- // EAX: value to shift, ECX: amount to shift.
- VisitLoadTwo(node->left(), node->right(), EAX, ECX);
- if (!left_info.IsClass(smi_class_) || !right_info.IsClass(smi_class_)) {
- // Check if both Smi.
- __ movl(EBX, EAX);
- __ orl(EBX, ECX);
- __ testl(EBX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, deopt_blob->label());
- PropagateBackLocalClass(node->left(), smi_class_);
- PropagateBackLocalClass(node->right(), smi_class_);
- }
- __ cmpl(ECX, Immediate(0));
- __ j(LESS, deopt_blob->label());
- Immediate count_limit = Immediate(0x1F);
- __ SmiUntag(ECX);
- __ cmpl(ECX, count_limit);
- Label shift_count_ok;
- __ j(LESS_EQUAL, &shift_count_ok, Assembler::kNearJump);
- __ movl(ECX, count_limit);
- __ Bind(&shift_count_ok);
- // Shift amount must be in ECX.
- __ SmiUntag(EAX); // Value.
- __ sarl(EAX, ECX);
- __ SmiTag(EAX);
- return;
- }
- ASSERT(node->kind() == Token::kSHL);
- if (node->right()->IsLiteralNode() &&
- node->right()->AsLiteralNode()->literal().IsSmi()) {
- Label done;
- // Shift count is a Smi literal.
- Smi& smi = Smi::Handle();
- smi ^= node->right()->AsLiteralNode()->literal().raw();
- if (smi.Value() < Smi::kBits) {
- Label slow_case;
- CodeGenInfo left_info(node->left());
- VisitLoadOne(node->left(), EAX);
- if (!left_info.IsClass(smi_class_)) {
- __ testl(EAX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, &slow_case, Assembler::kNearJump); // left not smi
- }
- // Overflow test.
- __ movl(EBX, EAX);
- Immediate imm(smi.Value());
- __ shll(EBX, imm);
- __ sarl(EBX, imm);
- __ cmpl(EAX, EBX);
- __ j(NOT_EQUAL, &slow_case, Assembler::kNearJump); // Overflow.
- __ shll(EAX, imm); // Shift for result now we know there is no overflow.
- __ jmp(&done);
- __ Bind(&slow_case);
- __ pushl(EAX);
- __ pushl(Immediate(reinterpret_cast<int32_t>(smi.raw())));
- const int number_of_arguments = 2;
- const Array& no_optional_argument_names = Array::Handle();
- GenerateCheckedInstanceCalls(node,
- node->left(),
- node->token_pos(),
- number_of_arguments,
- no_optional_argument_names);
- __ Bind(&done);
- return;
- }
- }
-
- Label slow_case, done;
- CodeGenInfo left_info(node->left());
- CodeGenInfo right_info(node->right());
- VisitLoadTwo(node->left(), node->right(), EAX, EDX);
- // TODO(srdjan): Better code for count being a Smi literal.
- // EAX: value, EDX: shift amount. Preserve them for slow case.
- // Fast case only if both ar Smi.
- if (!left_info.IsClass(smi_class_) || !right_info.IsClass(smi_class_)) {
- __ movl(EBX, EAX);
- __ orl(EBX, EDX);
- __ testl(EBX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, &slow_case, Assembler::kNearJump);
- }
- // Check if count too large for handling it inlined.
- __ cmpl(EDX, Immediate(reinterpret_cast<int32_t>(Smi::New(Smi::kBits))));
- __ j(ABOVE_EQUAL, &slow_case, Assembler::kNearJump);
- // Shift amount must be in ECX.
- __ movl(ECX, EDX);
- __ movl(EBX, EAX);
- __ SmiUntag(ECX);
- // Overflow test.
- __ shll(EBX, ECX);
- __ sarl(EBX, ECX);
- __ cmpl(EAX, EBX);
- __ j(NOT_EQUAL, &slow_case, Assembler::kNearJump); // Overflow.
-
- __ shll(EAX, ECX); // Shift for result now we know there is no overflow.
- // EAX is the correctly tagged Smi.
- __ jmp(&done);
- __ Bind(&slow_case);
- __ pushl(EAX);
- __ pushl(EDX);
- const int number_of_arguments = 2;
- const Array& no_optional_argument_names = Array::Handle();
- GenerateCheckedInstanceCalls(node,
- node->left(),
- node->token_pos(),
- number_of_arguments,
- no_optional_argument_names);
- __ Bind(&done);
-}
-
-
-// Implement Token::kSUB and Token::kBIT_NOT.
-void OptimizingCodeGenerator::GenerateSmiUnaryOp(UnaryOpNode* node) {
- const ICData& ic_data = node->ic_data();
- ASSERT(ic_data.num_args_tested() == 1);
- DeoptReasonId deopt_reason_id = ic_data.NumberOfChecks() == 0 ?
- kDeoptNoTypeFeedback : kDeoptUnaryOp;
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, EAX, deopt_reason_id);
- CodeGenInfo info(node->operand());
- VisitLoadOne(node->operand(), EAX);
- if (ic_data.NumberOfChecks() == 0) {
- // No type feedback.
- __ jmp(deopt_blob->label());
- return;
- }
- ASSERT(ic_data.NumberOfChecks() == 1);
- if (!info.IsClass(smi_class_)) {
- __ testl(EAX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, deopt_blob->label());
- PropagateBackLocalClass(node->operand(), smi_class_);
- }
- if (node->kind() == Token::kSUB) {
- __ negl(EAX);
- __ j(OVERFLOW, deopt_blob->label());
- } else {
- ASSERT(node->kind() == Token::kBIT_NOT);
- __ notl(EAX);
- __ andl(EAX, Immediate(~kSmiTagMask)); // Remove inverted smi-tag.
- }
- HandleResult(node, EAX);
-}
-
-
-void OptimizingCodeGenerator::GenerateDoubleUnaryOp(UnaryOpNode* node) {
- const Register kOperandRegister = ECX;
- const Register kTempRegister = EBX;
- const Register kResultRegister = EAX;
- const ICData& ic_data = node->ic_data();
- DeoptReasonId deopt_reason_id = ic_data.NumberOfChecks() == 0 ?
- kDeoptNoTypeFeedback : kDeoptUnaryOp;
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, kOperandRegister, deopt_reason_id);
- CodeGenInfo info(node->operand());
- info.set_allow_temp(true);
- VisitLoadOne(node->operand(), kOperandRegister);
- if (ic_data.NumberOfChecks() == 0) {
- // No type feedback.
- __ jmp(deopt_blob->label());
- return;
- }
- ASSERT(ic_data.NumberOfChecks() == 1);
- if (!info.IsClass(double_class_)) {
- // Deoptimize if not double.
- CheckIfDoubleOrSmi(kOperandRegister,
- kTempRegister,
- deopt_blob->label(),
- deopt_blob->label());
- PropagateBackLocalClass(node->operand(), double_class_);
- }
- const bool using_temp =
- (node->info() != NULL) && node->info()->allow_temp();
- if (!using_temp) {
- const Code& stub =
- Code::Handle(StubCode::GetAllocationStubForClass(double_class_));
- const ExternalLabel label(double_class_.ToCString(), stub.EntryPoint());
- __ pushl(kOperandRegister);
- GenerateCall(node->token_pos(), &label, PcDescriptors::kOther);
- ASSERT(kResultRegister == EAX);
- __ popl(kOperandRegister);
- } else if (info.is_temp()) {
- __ movl(kResultRegister, kOperandRegister);
- } else {
- const Double& double_object =
- Double::ZoneHandle(Double::New(0.0, Heap::kOld));
- __ LoadObject(kResultRegister, double_object);
- }
- __ movsd(XMM0, FieldAddress(kOperandRegister, Double::value_offset()));
- ASSERT(node->kind() == Token::kSUB);
- __ DoubleNegate(XMM0);
- __ movsd(FieldAddress(kResultRegister, Double::value_offset()), XMM0);
- if (IsResultNeeded(node)) {
- if (node->info() != NULL) {
- node->info()->set_is_temp(using_temp);
- node->info()->set_is_class(&double_class_);
- }
- HandleResult(node, kResultRegister);
- }
-}
-
-
-// Handles only Smi & Smi.
-// TODO(srdjan): Certain operations always overflow, and thus cause
-// deoptimization. We need to mark those places and handle them.
-void OptimizingCodeGenerator::GenerateSmiBinaryOp(BinaryOpNode* node) {
- const char* kOptMessage = "Inlines BinaryOp for Smi";
- Label done;
- const Token::Kind kind = node->kind();
- if ((kind == Token::kADD) ||
- (kind == Token::kSUB) ||
- (kind == Token::kMUL) ||
- (kind == Token::kTRUNCDIV) ||
- (kind == Token::kBIT_AND) ||
- (kind == Token::kBIT_OR) ||
- (kind == Token::kBIT_XOR)) {
- TraceOpt(node, kOptMessage);
- // Check if both arguments are expected to be Smi.
- const ICData& ic_data = node->ic_data();
- ASSERT(ic_data.num_args_tested() == 2);
- ASSERT(ic_data.NumberOfChecks() > 0);
- Function& target = Function::Handle();
- GrowableArray<intptr_t> class_ids;
- ic_data.GetCheckAt(0, &class_ids, &target);
- ASSERT(ic_data.NumberOfChecks() == 1);
- ASSERT((class_ids[0] == kSmi) && (class_ids[1] == kSmi));
- CodeGenInfo left_info(node->left());
- CodeGenInfo right_info(node->right());
- VisitLoadTwo(node->left(), node->right(), EAX, EDX);
- Label two_smis, call_operator;
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, ECX, EDX, kDeoptSmiBinaryOp);
- __ movl(ECX, EAX); // Save if overflow (needs original value).
-
- if (left_info.IsClass(smi_class_) || right_info.IsClass(smi_class_)) {
- if (!left_info.IsClass(smi_class_)) {
- __ testl(EAX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, deopt_blob->label());
- PropagateBackLocalClass(node->left(), smi_class_);
- }
- if (!right_info.IsClass(smi_class_)) {
- __ testl(EDX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, deopt_blob->label());
- PropagateBackLocalClass(node->right(), smi_class_);
- }
- } else {
- // Type feedback says both types are Smi, but static type analysis
- // does not know if any of them is Smi, therefore check.
- __ orl(EAX, EDX);
- __ testl(EAX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, deopt_blob->label());
- __ movl(EAX, ECX);
- PropagateBackLocalClass(node->left(), smi_class_);
- PropagateBackLocalClass(node->right(), smi_class_);
- }
- if (node->info() != NULL) {
- node->info()->set_is_class(&smi_class_);
- }
- switch (kind) {
- case Token::kADD: {
- __ addl(EAX, EDX);
- __ j(OVERFLOW, deopt_blob->label());
- break;
- }
- case Token::kSUB: {
- __ subl(EAX, EDX);
- __ j(OVERFLOW, deopt_blob->label());
- break;
- }
- case Token::kMUL: {
- __ SmiUntag(EAX);
- __ imull(EAX, EDX);
- __ j(OVERFLOW, deopt_blob->label());
- break;
- }
- case Token::kBIT_AND: {
- // No overflow check.
- __ andl(EAX, EDX);
- break;
- }
- case Token::kBIT_OR: {
- // No overflow check.
- __ orl(EAX, EDX);
- break;
- }
- case Token::kBIT_XOR: {
- // No overflow check.
- __ xorl(EAX, EDX);
- break;
- }
- case Token::kTRUNCDIV: {
- // Handle divide by zero in runtime.
- __ cmpl(EDX, Immediate(0));
- __ j(EQUAL, deopt_blob->label());
- // Preserve left & right in case of 'overflow'.
- __ pushl(EDX);
- __ pushl(ECX);
- // Move right to ECX, left is in EAX.
- __ movl(ECX, EDX);
- __ SmiUntag(ECX);
- __ SmiUntag(EAX);
- // Sign extend EAX -> EDX:EAX.
- __ cdq();
- __ idivl(ECX); // Result in EAX.
- __ popl(ECX);
- __ popl(EDX);
- // Check the corner case of dividing the 'MIN_SMI' with -1, in which
- // case we cannot tag the result.
- __ cmpl(EAX, Immediate(0x40000000));
- __ j(EQUAL, deopt_blob->label());
- __ SmiTag(EAX);
- break;
- }
- default:
- UNREACHABLE();
- }
- } else if ((kind == Token::kSHL) || (kind == Token::kSHR)) {
- GenerateSmiShiftBinaryOp(node);
- } else {
- // Unhandled node kind.
- TraceNotOpt(node, kOptMessage);
- node->left()->Visit(this);
- node->right()->Visit(this);
- CodeGenerator::GenerateBinaryOperatorCall(node->id(),
- node->token_pos(),
- node->Name());
- }
- __ Bind(&done);
- HandleResult(node, EAX);
-}
-
-
-// Supports some mixed Smi/Mint operations.
-// For BIT_AND operation with right operand being Smi, we can throw away
-// any Mint bits above the Smi range as long as the right operand is positive.
-// 'allow_smi' is true if Smi and Mint classes have been encountered.
-void OptimizingCodeGenerator::GenerateMintBinaryOp(BinaryOpNode* node,
- bool allow_smi) {
- const char* kOptMessage = "Inline Mint binop.";
- const Token::Kind kind = node->kind();
- if (kind == Token::kBIT_AND) {
- TraceOpt(node, kOptMessage);
- Label is_smi, slow_case, done;
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, EAX, EDX, kDeoptMintBinaryOp);
- VisitLoadTwo(node->left(), node->right(), EAX, EDX);
- __ testl(EDX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, &slow_case); // Call operator if right is not Smi.
- __ cmpl(EDX, Immediate(0));
- __ j(LESS, &slow_case); // Result will not be Smi.
-
- // Test left.
- __ testl(EAX, Immediate(kSmiTagMask));
- __ j(ZERO, &is_smi);
-
- __ CompareClassId(EAX, kMint, EBX);
- __ j(NOT_EQUAL, deopt_blob->label());
-
- // Load lower Mint word, convert to Smi. It is OK to loose bits.
- __ movl(EAX, FieldAddress(EAX, Mint::value_offset()));
- __ SmiTag(EAX);
-
- __ Bind(&is_smi);
- __ andl(EAX, EDX);
- __ jmp(&done);
- __ Bind(&slow_case);
- __ pushl(EAX);
- __ pushl(EDX);
- const int number_of_arguments = 2;
- const Array& no_optional_argument_names = Array::Handle();
- GenerateCheckedInstanceCalls(node,
- node->left(),
- node->token_pos(),
- number_of_arguments,
- no_optional_argument_names);
- __ Bind(&done);
- HandleResult(node, EAX);
- return;
- }
- if ((kind == Token::kSHL) && allow_smi) {
- GenerateSmiShiftBinaryOp(node);
- HandleResult(node, EAX);
- return;
- }
- TraceNotOpt(node, kOptMessage);
- CodeGenerator::VisitBinaryOpNode(node);
-}
-
-
-// Conservative approach:
-// - true if both nodes are LoadLocalNodes with the same index.
-static bool AreNodesOfSameType(AstNode* a, AstNode* b) {
- ASSERT((a != NULL) && (b != NULL));
- if (a->IsLoadLocalNode() && b->IsLoadLocalNode()) {
- return a->AsLoadLocalNode()->local().Equals(b->AsLoadLocalNode()->local());
- }
- return false;
-}
-
-
-// If possible propagate node type back to the local, therefore next load
-// of local can use that class and eliminate type checks.
-void OptimizingCodeGenerator::PropagateBackLocalClass(AstNode* node,
- const Class& cls) {
- if (node->IsLoadLocalNode()) {
- LoadLocalNode* local_node = node->AsLoadLocalNode();
- classes_for_locals_->SetLocalType(local_node->local(), cls);
- }
-}
-
-
-// 'reg' is not modified, 'temp' is trashed.
-// Fall through if double, jump to 'is_smi' if Smi and
-// jump to 'not_double_or_smi' if neither double nor Smi.
-void OptimizingCodeGenerator::CheckIfDoubleOrSmi(Register reg,
- Register temp,
- Label* is_smi,
- Label* not_double_or_smi) {
- __ testl(reg, Immediate(kSmiTagMask));
- __ j(ZERO, is_smi);
- __ CompareClassId(reg, kDouble, temp);
- __ j(NOT_EQUAL, not_double_or_smi);
-}
-
-
-// Result of the computation is a newly allocated double object or
-// a temporary object if the parent node specifies a CodeGenInfo for this node
-// and therefore knows how to handle a temporary. A temporary object cannot
-// be used for long living values (e.g., the ones stored on stack or into other
-// objects).
-// Implement for combinations: Double/Double, Double/Smi, Smi/Double, as
-// the result is always double.
-// TODO(srdjan): Implement Smi/Smi for kDIV (result also double).
-void OptimizingCodeGenerator::GenerateDoubleBinaryOp(BinaryOpNode* node,
- bool receiver_can_be_smi) {
- const char* kOptMessage = "Inlines BinaryOp for Doubles";
- const Token::Kind kind = node->kind();
- if ((kind == Token::kADD) ||
- (kind == Token::kSUB) ||
- (kind == Token::kMUL) ||
- (kind == Token::kDIV)) {
- TraceOpt(node, kOptMessage);
- // All four register below must be different.
- const Register kLeftRegister = EAX;
- const Register kRightRegister = EDX;
- const Register kAllocatedRegister = ECX;
- const Register kTempRegister = EBX;
- CodeGenInfo left_info(node->left()); // Receiver.
- CodeGenInfo right_info(node->right());
- left_info.set_allow_temp(true);
- right_info.set_allow_temp(true);
- VisitLoadTwo(node->left(), node->right(), kLeftRegister, kRightRegister);
- // First allocate result object or specify an existing object as result.
- Register result_register = kNoRegister;
- const bool using_temp =
- (node->info() != NULL) && node->info()->allow_temp();
- if (!using_temp) {
- // Parent node cannot handle a temporary double object, allocate one
- // each time.
- result_register = kAllocatedRegister;
- const Code& stub =
- Code::Handle(StubCode::GetAllocationStubForClass(double_class_));
- const ExternalLabel label(double_class_.ToCString(), stub.EntryPoint());
- __ pushl(kLeftRegister);
- __ pushl(kRightRegister);
- GenerateCall(node->token_pos(), &label, PcDescriptors::kOther);
- __ movl(result_register, EAX);
- __ popl(kRightRegister);
- __ popl(kLeftRegister);
- } else if (left_info.IsClass(double_class_) && left_info.is_temp()) {
- result_register = kLeftRegister;
- } else if (right_info.IsClass(double_class_) && right_info.is_temp()) {
- result_register = kRightRegister;
- } else {
- result_register = kAllocatedRegister;
- // Use inlined temporary double object.
- const Double& double_object =
- Double::ZoneHandle(Double::New(0.0, Heap::kOld));
- __ LoadObject(result_register, double_object);
- }
-
- DeoptimizationBlob* deopt_blob = NULL;
- Label* deopt_lbl = NULL;
- // Deoptimization can only occur if one of arguments is not double.
- if (!left_info.IsClass(double_class_) ||
- !right_info.IsClass(double_class_)) {
- deopt_blob = AddDeoptimizationBlob(node,
- kLeftRegister,
- kRightRegister,
- kDeoptDoubleBinaryOp);
- deopt_lbl = deopt_blob->label();
- }
-
- if (receiver_can_be_smi) {
- // Only deoptimize if both argument are Smi.
- __ movl(kTempRegister, kLeftRegister);
- __ orl(kTempRegister, kRightRegister);
- __ testl(kTempRegister, Immediate(kSmiTagMask));
- __ j(ZERO, deopt_lbl);
- }
-
- bool args_of_same_type = AreNodesOfSameType(node->left(), node->right());
- if (left_info.IsClass(double_class_)) {
- __ movsd(XMM0, FieldAddress(kLeftRegister, Double::value_offset()));
- } else {
- if (receiver_can_be_smi) {
- Label is_smi, done;
- CheckIfDoubleOrSmi(kLeftRegister, kTempRegister, &is_smi, deopt_lbl);
- // Fall through for double. Jump to 'is_smi' if double, jump to
- // 'deopt' if neither smi nor double.
- __ movsd(XMM0, FieldAddress(kLeftRegister, Double::value_offset()));
- __ jmp(&done);
- __ Bind(&is_smi);
- __ SmiUntag(kLeftRegister);
- __ cvtsi2sd(XMM0, kLeftRegister);
- __ Bind(&done);
- } else {
- CheckIfDoubleOrSmi(kLeftRegister, kTempRegister, deopt_lbl, deopt_lbl);
- __ movsd(XMM0, FieldAddress(kLeftRegister, Double::value_offset()));
- PropagateBackLocalClass(node->left(), double_class_);
- }
- }
-
- const bool right_must_be_double = NodeHasClassAt(node, double_class_, 1);
-
- // If arguments are of same type (e.g., same local), then the test of left
- // argument was sufficient.
- if (right_info.IsClass(double_class_) || args_of_same_type) {
- __ movsd(XMM1, FieldAddress(kRightRegister, Double::value_offset()));
- if (!right_info.IsClass(double_class_)) {
- PropagateBackLocalClass(node->right(), double_class_);
- }
- } else {
- if (right_must_be_double) {
- CheckIfDoubleOrSmi(kRightRegister, kTempRegister, deopt_lbl, deopt_lbl);
- __ movsd(XMM1, FieldAddress(kRightRegister, Double::value_offset()));
- PropagateBackLocalClass(node->right(), double_class_);
- } else {
- Label is_smi, done;
- CheckIfDoubleOrSmi(kRightRegister, kTempRegister, &is_smi, deopt_lbl);
- // Fall through for double. Jump to 'is_smi' if double, jump to
- // 'deopt' if neither smi nor double.
- __ movsd(XMM1, FieldAddress(kRightRegister, Double::value_offset()));
- __ jmp(&done);
- __ Bind(&is_smi);
- __ SmiUntag(kRightRegister);
- __ cvtsi2sd(XMM1, kRightRegister);
- __ Bind(&done);
- }
- }
-
- switch (kind) {
- case Token::kADD: __ addsd(XMM0, XMM1); break;
- case Token::kSUB: __ subsd(XMM0, XMM1); break;
- case Token::kMUL: __ mulsd(XMM0, XMM1); break;
- case Token::kDIV: __ divsd(XMM0, XMM1); break;
- default: UNREACHABLE();
- }
- __ movsd(FieldAddress(result_register, Double::value_offset()), XMM0);
- if (IsResultNeeded(node)) {
- if (node->info() != NULL) {
- node->info()->set_is_temp(using_temp);
- node->info()->set_is_class(&double_class_);
- }
- HandleResult(node, result_register);
- }
- return;
- }
-
- TraceNotOpt(node, kOptMessage);
- CodeGenerator::VisitBinaryOpNode(node);
-}
-
-
-static bool NodeInfoHasLabels(AstNode* node) {
- return (node->info() != NULL) &&
- (node->info()->true_label() != NULL) &&
- (node->info()->false_label() != NULL);
-}
-
-
-// Generates code for logical OR, AND operations.
-// A logical binary operation either pushes a true/false object on the stack,
-// or jumps to the true/false label of the parent node.
-// For AND operation, if left argument is false, then the result is false.
-// For OR operation, if left argument is true, then the result is true.
-// Otherwise the right argument is evaluated and the result corresponds to the
-// right argument.
-void OptimizingCodeGenerator::GenerateLogicalBinaryOp(BinaryOpNode* node) {
- ASSERT((node->kind() == Token::kAND) || (node->kind() == Token::kOR));
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- const Bool& bool_false = Bool::ZoneHandle(Bool::False());
-
- // If NodeInfoHasLabels is true, then we do not return a result but
- // jump to the specified true/false labels.
- Label return_false_object, return_true_object, evaluate_right_label;
- Label* false_label = NodeInfoHasLabels(node) ?
- node->info()->false_label() : &return_false_object;
- Label* true_label = NodeInfoHasLabels(node) ?
- node->info()->true_label() : &return_true_object;
-
- CodeGenInfo left_bool(node->left());
- if (node->kind() == Token::kAND) {
- left_bool.set_true_label(&evaluate_right_label);
- left_bool.set_false_label(false_label);
- } else {
- left_bool.set_true_label(true_label);
- left_bool.set_false_label(&evaluate_right_label);
- }
- VisitLoadOne(node->left(), EAX);
- if (left_bool.labels_used()) {
- __ Bind(&evaluate_right_label);
- } else {
- __ CompareObject(EAX, bool_true);
- if (node->kind() == Token::kAND) {
- __ j(NOT_EQUAL, false_label);
- } else {
- __ j(EQUAL, true_label);
- }
- }
-
- CodeGenInfo right_bool(node->right());
- right_bool.set_true_label(true_label);
- right_bool.set_false_label(false_label);
- VisitLoadOne(node->right(), EAX);
- if (right_bool.labels_used()) {
- // The control flow continues at the parent's false or true labels.
-#if defined(DEBUG)
- __ Unreachable("BinaryOp");
-#endif
- } else {
- __ CompareObject(EAX, bool_true);
- __ j(NOT_EQUAL, false_label);
- if (NodeInfoHasLabels(node)) {
- __ jmp(true_label);
- }
- }
- if (NodeInfoHasLabels(node)) {
- node->info()->set_labels_used(true);
- } else {
- Label done;
- __ Bind(&return_true_object);
- __ LoadObject(EAX, bool_true);
- __ jmp(&done, Assembler::kNearJump);
- __ Bind(&return_false_object);
- __ LoadObject(EAX, bool_false);
- __ Bind(&done);
- HandleResult(node, EAX);
- }
-}
-
-
-void OptimizingCodeGenerator::VisitBinaryOpNode(BinaryOpNode* node) {
- // Operators "&&" and "||" cannot be overloaded, therefore inline them
- // instead of calling the operator.
- if ((node->kind() == Token::kAND) || (node->kind() == Token::kOR)) {
- // TODO(srdjan): Test in checked mode if they are Booleans otherwise
- // throw exception.
- if (FLAG_enable_type_checks) {
- CodeGenerator::VisitBinaryOpNode(node);
- return;
- }
- GenerateLogicalBinaryOp(node);
- return;
- }
-
- const ICData& ic_data = node->ic_data();
- if (ic_data.NumberOfChecks() == 0) {
- VisitLoadTwo(node->left(), node->right(), EAX, EDX);
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, EAX, EDX, kDeoptNoTypeFeedback);
- __ jmp(deopt_blob->label());
- return;
- }
-
- ASSERT(ic_data.num_args_tested() == 2);
-
- if (NodeHasTwoClasses(node, smi_class_, smi_class_)) {
- GenerateSmiBinaryOp(node);
- return;
- }
-
- if (NodeHasClassAt(node, double_class_, 0)) {
- const bool receiver_can_be_smi = false;
- GenerateDoubleBinaryOp(node, receiver_can_be_smi);
- return;
- }
-
- if (NodeHasTwoClasses(node, smi_class_, double_class_)) {
- const bool receiver_can_be_smi = true;
- GenerateDoubleBinaryOp(node, receiver_can_be_smi);
- return;
- }
-
- const Class& mint_class =
- Class::Handle(Isolate::Current()->object_store()->mint_class());
- if (NodeHasClassAt(node, mint_class, 0)) {
- GenerateMintBinaryOp(node, false);
- return;
- }
-
- if (NodeHasBothReceiverClasses(node, smi_class_, mint_class)) {
- GenerateMintBinaryOp(node, true);
- return;
- }
-
- // TODO(srdjan): Implement "+" for Strings.
- // Type feedback tells this is not a Smi or Double operation.
- TraceNotOpt(node,
- "BinaryOp: type feedback tells this is not a Smi, Mint or Double op");
- node->left()->Visit(this);
- node->right()->Visit(this);
- const int number_of_arguments = 2;
- const Array& no_optional_argument_names = Array::Handle();
- GenerateCheckedInstanceCalls(node,
- node->left(),
- node->token_pos(),
- number_of_arguments,
- no_optional_argument_names);
- HandleResult(node, EAX);
- return;
-}
-
-
-// Return offset of a field or -1 if field is not found.
-static intptr_t GetFieldOffset(const Class& field_class,
- const String& field_name) {
- Class& cls = Class::Handle(field_class.raw());
- Field& field = Field::Handle();
- while (!cls.IsNull()) {
- field = cls.LookupInstanceField(field_name);
- if (!field.IsNull()) {
- return field.Offset();
- }
- cls = cls.SuperClass();
- }
- return -1;
-}
-
-
-// For now, check if the node is the receiver of a non-Smi class.
-bool OptimizingCodeGenerator::NodeMayBeSmi(AstNode* node) const {
- if (parsed_function_.function().is_static() ||
- parsed_function_.function().IsConstructor() ||
- parsed_function_.function().IsClosureFunction()) {
- return true;
- }
- LocalScope* scope = parsed_function_.node_sequence()->scope();
- LocalVariable* receiver = scope->VariableAt(0);
- if (node->IsLoadLocalNode() &&
- (&node->AsLoadLocalNode()->local() == receiver)) {
- const Class& function_owner =
- Class::Handle(parsed_function_.function().owner());
- const String& integer_implementation_class_name =
- String::Handle(String::NewSymbol("IntegerImplementation"));
- const Class& integer_implementation_class = Class::Handle(
- Library::Handle(Library::CoreImplLibrary()).
- LookupClass(integer_implementation_class_name));
- if (!function_owner.IsSmi() &&
- (function_owner.raw() != integer_implementation_class.raw())) {
- return false;
- }
- }
- return true;
-}
-
-
-// Emits code for an instance getter that has one or more collected classes,
-// all with the same target. Deoptimizes for Smi or unexpected class.
-// EBX: loaded receiver.
-// Result is returned in EAX.
-void OptimizingCodeGenerator::InlineInstanceGettersWithSameTarget(
- AstNode* node,
- AstNode* receiver,
- const String& field_name,
- Register recv_reg) {
- if (recv_reg != EBX) {
- // TODO(srdjan): Do not hardwire register.
- UNIMPLEMENTED();
- }
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, EBX, kDeoptInstanceGetterSameTarget);
- if (NodeMayBeSmi(receiver)) {
- __ testl(EBX, Immediate(kSmiTagMask));
- __ j(ZERO, deopt_blob->label());
- }
-
- __ LoadClassId(EAX, EBX);
- const ICData& ic_data = node->ic_data();
- Function& target = Function::Handle();
- Label load_field;
- for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
- intptr_t class_id = kIllegalObjectKind;
- ic_data.GetOneClassCheckAt(i, &class_id, &target);
- __ cmpl(EAX, Immediate(class_id));
- if (i == (ic_data.NumberOfChecks() - 1)) {
- __ j(NOT_EQUAL, deopt_blob->label());
- } else {
- __ j(EQUAL, &load_field);
- }
- }
- intptr_t class_id = kIllegalObjectKind;
- ic_data.GetOneClassCheckAt(0, &class_id, &target);
-
- __ Bind(&load_field);
- // EBX: receiver.
- if (target.kind() == RawFunction::kImplicitGetter) {
- TraceOpt(node, "Inlines instance getter with same target");
- const Class& cls =
- Class::Handle(Isolate::Current()->class_table()->At(class_id));
- intptr_t field_offset = GetFieldOffset(cls, field_name);
- ASSERT(field_offset >= 0);
- __ movl(EAX, FieldAddress(EBX, field_offset));
- return;
- }
-
- Recognizer::Kind recognized_kind = Recognizer::RecognizeKind(target);
- switch (recognized_kind) {
- case Recognizer::kObjectArrayLength: {
- TraceOpt(node, "Inlines ObjectArray.length");
- __ movl(EAX, FieldAddress(EBX, Array::length_offset()));
- return;
- }
- case Recognizer::kGrowableArrayLength: {
- TraceOpt(node, "Inlines GrowableObjectArray.length");
- __ movl(EAX, FieldAddress(EBX, GrowableObjectArray::length_offset()));
- return;
- }
- case Recognizer::kStringBaseLength: {
- TraceOpt(node, "Inlines StringBase.length");
- __ movl(EAX, FieldAddress(EBX, String::length_offset()));
- return;
- }
- default:
- UNIMPLEMENTED();
- }
- UNREACHABLE();
-}
-
-
-static bool IsInlineableInstanceGetter(const Function& function) {
- if (function.kind() == RawFunction::kImplicitGetter) {
- return true;
- }
- Recognizer::Kind recognized = Recognizer::RecognizeKind(function);
- if ((recognized == Recognizer::kObjectArrayLength) ||
- (recognized == Recognizer::kGrowableArrayLength) ||
- (recognized == Recognizer::kStringBaseLength)) {
- return true;
- }
- return false;
-}
-
-
-// Return the unique target of all checks or null.
-static RawFunction* GetUniqueTarget(const ICData& ic_data) {
- Function& prev_target = Function::Handle();
- Function& target = Function::Handle();
- for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
- intptr_t class_id;
- ic_data.GetOneClassCheckAt(i, &class_id, &target);
- ASSERT(!target.IsNull());
- if (!prev_target.IsNull() && (prev_target.raw() != target.raw())) {
- return Function::null();
- }
- prev_target = target.raw();
- }
- return target.raw();
-}
-
-
-// Return true if all targets in 'ic_data' point to same
-// inlineable getter target.
-static bool ICDataToSameInlineableInstanceGetter(const ICData& ic_data) {
- const Function& target = Function::Handle(GetUniqueTarget(ic_data));
- return !target.IsNull() && IsInlineableInstanceGetter(target);
-}
-
-
-void OptimizingCodeGenerator::InlineInstanceGetter(AstNode* node,
- AstNode* receiver,
- const String& field_name,
- Register recv_reg) {
- if (ICDataToSameInlineableInstanceGetter(node->ic_data())) {
- InlineInstanceGettersWithSameTarget(node,
- receiver,
- field_name,
- recv_reg);
- } else {
- // TODO(srdjan): Inline access.
- __ pushl(recv_reg);
- const int kNumberOfArguments = 1;
- const Array& kNoArgumentNames = Array::Handle();
- GenerateCheckedInstanceCalls(node,
- receiver,
- node->token_pos(),
- kNumberOfArguments,
- kNoArgumentNames);
- }
-}
-
-
-// TODO(srdjan): Implement for multiple getter targets.
-// For every class inline its implicit getter, or call the instance getter.
-void OptimizingCodeGenerator::VisitInstanceGetterNode(
- InstanceGetterNode* node) {
- const ICData& ic_data = node->ic_data();
- if (ic_data.NumberOfChecks() == 0) {
- // No type feedback collected.
- node->receiver()->Visit(this);
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, kDeoptInstanceGetter);
- __ jmp(deopt_blob->label());
- return;
- }
-
- VisitLoadOne(node->receiver(), EBX);
- InlineInstanceGetter(node,
- node->receiver(),
- node->field_name(),
- EBX);
- // Result is in EAX.
- HandleResult(node, EAX);
-}
-
-
-// Helper struct to pass arguments to 'GenerateInstanceSetter'.
-struct InstanceSetterArgs {
- const Class* cls;
- const Function* target;
- const String* field_name;
- Register recv_reg;
- Register value_reg;
- intptr_t id;
- intptr_t token_pos;
-};
-
-
-// Preserves 'args.value_reg'. Either stores instance field directly or
-// calls the setter method.
-void OptimizingCodeGenerator::GenerateInstanceSetter(
- const InstanceSetterArgs& args) {
- if (args.target->kind() == RawFunction::kImplicitSetter) {
- intptr_t field_offset = GetFieldOffset(*(args.cls), *(args.field_name));
- ASSERT(field_offset >= 0);
- __ StoreIntoObject(args.recv_reg,
- FieldAddress(args.recv_reg, field_offset), args.value_reg);
- } else {
- __ pushl(args.value_reg);
- __ pushl(args.recv_reg);
- __ pushl(args.value_reg);
- const Array& no_optional_argument_names = Array::Handle();
- GenerateDirectCall(args.id,
- args.token_pos,
- *(args.target),
- 2,
- no_optional_argument_names);
- __ popl(args.value_reg);
- }
-}
-
-
-// Returns value in 'value_reg', clobbers EBX.
-void OptimizingCodeGenerator::InlineInstanceSetter(AstNode* node,
- AstNode* receiver,
- const String& field_name,
- Register recv_reg,
- Register value_reg) {
- // EBX is used as temporary register for class.
- ASSERT((recv_reg != EBX) && (value_reg != EBX));
- GrowableArray<Class*> classes;
- GrowableArray<Function*> targets;
- bool unique_target = true;
- {
- const ICData& ic_data = node->ic_data();
- ASSERT(ic_data.NumberOfChecks() > 0);
- ASSERT(ic_data.num_args_tested() == 1);
- for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
- Function& target = Function::ZoneHandle();
- intptr_t class_id;
- ic_data.GetOneClassCheckAt(i, &class_id, &target);
- Class& cls =
- Class::ZoneHandle(Isolate::Current()->class_table()->At(class_id));
- classes.Add(&cls);
- targets.Add(&target);
- }
- for (intptr_t i = 1; i < targets.length(); i++) {
- if (targets[i - 1]->raw() != targets[i]->raw()) {
- unique_target = false;
- break;
- }
- }
- }
- // TODO(srdjan): sort classes/target by their invocation count.
- DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(
- node, recv_reg, value_reg, kDeoptInstanceSetterSameTarget);
- // Deoptimize if Smi, since they do not have setters.
- if (NodeMayBeSmi(receiver)) {
- __ testl(recv_reg, Immediate(kSmiTagMask));
- __ j(ZERO, deopt_blob->label());
- }
- __ LoadClassId(EBX, recv_reg);
- // Initialize setter arguments, but leave the class and target fields NULL.
- InstanceSetterArgs setter_args =
- {NULL, NULL, &field_name, recv_reg, value_reg,
- node->id(), node->token_pos()};
-
- if (unique_target) {
- Label store_field;
- for (intptr_t i = 0; i < classes.length(); i++) {
- __ cmpl(EBX, Immediate(classes[i]->id()));
- if (i == (classes.length() - 1)) {
- __ j(NOT_EQUAL, deopt_blob->label());
- } else {
- __ j(EQUAL, &store_field);
- }
- }
- __ Bind(&store_field);
- setter_args.cls = classes[0];
- setter_args.target = targets[0];
- GenerateInstanceSetter(setter_args);
- return;
- }
- // Targets are different.
- Label done;
- for (intptr_t i = 0; i < classes.length(); i++) {
- setter_args.cls = classes[i];
- setter_args.target = targets[i];
- __ cmpl(EBX, Immediate(classes[i]->id()));
- if (i == (classes.length() - 1)) {
- __ j(NOT_EQUAL, deopt_blob->label());
- GenerateInstanceSetter(setter_args);
- } else {
- Label next_check;
- __ j(NOT_EQUAL, &next_check);
- GenerateInstanceSetter(setter_args);
- __ jmp(&done);
- __ Bind(&next_check);
- }
- }
- __ Bind(&done);
-}
-
-
-// The call to the instance setter implements the assignment to a field.
-// The result of the assignment to a field is the value being stored.
-void OptimizingCodeGenerator::VisitInstanceSetterNode(
- InstanceSetterNode* node) {
- // TODO(srdjan): inline setters to different targets as well.
- if (FLAG_enable_type_checks) {
- CodeGenerator::VisitInstanceSetterNode(node);
- return;
- }
- VisitLoadTwo(node->receiver(), node->value(), EDX, EAX);
- const ICData& ic_data = node->ic_data();
- if (ic_data.NumberOfChecks() == 0) {
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, EDX, EAX, kDeoptInstanceSetter);
- __ jmp(deopt_blob->label());
- return;
- }
- // Value in EAX survives and will be stored on stack if result is needed.
- InlineInstanceSetter(node,
- node->receiver(),
- node->field_name(),
- EDX,
- EAX);
-
- HandleResult(node, EAX);
-}
-
-
-// Return false if condition is not supported.
-static bool SupportedTokenKindToSmiCondition(Token::Kind kind,
- Condition* condition) {
- switch (kind) {
- case Token::kEQ:
- *condition = EQUAL;
- return true;
- case Token::kNE:
- *condition = NOT_EQUAL;
- return true;
- case Token::kLT:
- *condition = LESS;
- return true;
- case Token::kGT:
- *condition = GREATER;
- return true;
- case Token::kLTE:
- *condition = LESS_EQUAL;
- return true;
- case Token::kGTE:
- *condition = GREATER_EQUAL;
- return true;
- default:
- return false;
- }
-}
-
-
-static Condition NegateCondition(Condition condition) {
- switch (condition) {
- case EQUAL: return NOT_EQUAL;
- case NOT_EQUAL: return EQUAL;
- case LESS: return GREATER_EQUAL;
- case LESS_EQUAL: return GREATER;
- case GREATER: return LESS_EQUAL;
- case GREATER_EQUAL: return LESS;
- case BELOW: return ABOVE_EQUAL;
- case BELOW_EQUAL: return ABOVE;
- case ABOVE: return BELOW_EQUAL;
- case ABOVE_EQUAL: return BELOW;
- default:
- OS::Print("Error %d\n", condition);
- UNIMPLEMENTED();
- return EQUAL;
- }
-}
-
-
-void OptimizingCodeGenerator::GenerateConditionalJumps(const CodeGenInfo& nInfo,
- Condition condition) {
- if (nInfo.fallthrough_label() == NULL) {
- __ j(condition, nInfo.true_label());
- __ jmp(nInfo.false_label());
- } else if (nInfo.fallthrough_label() == nInfo.false_label()) {
- __ j(condition, nInfo.true_label());
- } else if (nInfo.fallthrough_label() == nInfo.true_label()) {
- __ j(NegateCondition(condition), nInfo.false_label());
- }
-}
-
-
-// Generate code under assumption that it is common that a Smi
-// is compared with null.
-// Left argument can be Smi or null, otherwise deoptimize and collect more
-// type information.
-// Right operand can be Smi or null, otherwise call operator on Smi (e.g,
-// when compared with double).
-// This code will be more optimized once we collect types for two arguments.
-void OptimizingCodeGenerator::GenerateSmiEquality(ComparisonNode* node) {
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- const Bool& bool_false = Bool::ZoneHandle(Bool::False());
- ASSERT((node->kind() == Token::kEQ) || (node->kind() == Token::kNE));
- CodeGenInfo left_info(node->left());
- CodeGenInfo right_info(node->right());
- VisitLoadTwo(node->left(), node->right(), EAX, EDX);
- if (!IsResultNeeded(node)) {
- return;
- }
- const Immediate raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- Label evaluate_comparison;
- if (!left_info.IsClass(smi_class_)) {
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, EAX, EDX, kDeoptSmiEquality);
- Label left_not_null;
- __ cmpl(EAX, raw_null);
- __ j(NOT_EQUAL, &left_not_null, Assembler::kNearJump);
-
- // Left is null, strict compare.
- __ cmpl(EAX, EDX);
- __ jmp(&evaluate_comparison, Assembler::kNearJump);
-
- // Deoptimize if left is not Smi.
- __ Bind(&left_not_null);
- __ testl(EAX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, deopt_blob->label());
- }
- Label done;
- if (right_info.IsClass(smi_class_)) {
- __ cmpl(EAX, EDX);
- // Fall through to evaluate comparison.
- } else {
- Label call_operator, inlined_compare;
- // Test right for being Smi.
- __ testl(EDX, Immediate(kSmiTagMask));
- __ j(ZERO, &inlined_compare, Assembler::kNearJump);
- // Right is not Smi, test it for being null; if so result is false which
- // is generated by comparing it to left. If right is not null call operator
- // (could be double).
- __ cmpl(EDX, raw_null);
- __ j(NOT_EQUAL, &call_operator, Assembler::kNearJump);
-
- __ Bind(&inlined_compare);
- // Left is Smi, right is Smi or Null.
- __ cmpl(EAX, EDX);
- __ jmp(&evaluate_comparison);
-
- __ Bind(&call_operator);
- // Left is Smi.
- const int kNumberOfArguments = 2;
- const Array& kNoArgumentNames = Array::Handle();
- __ pushl(EAX);
- __ pushl(EDX);
- GenerateCheckedInstanceCalls(node,
- node->left(),
- node->token_pos(),
- kNumberOfArguments,
- kNoArgumentNames);
- __ CompareObject(EAX, bool_true);
- // Fall through to evaluate result.
- }
- __ Bind(&evaluate_comparison);
- // Condition is set by a previous comparison operation.
- Condition condition = OVERFLOW; // Initialize to something.
- bool ok = SupportedTokenKindToSmiCondition(node->kind(), &condition);
- ASSERT(ok);
- if (NodeInfoHasLabels(node)) {
- GenerateConditionalJumps(*(node->info()), condition);
- node->info()->set_labels_used(true);
- } else {
- Label true_label;
- __ j(condition, &true_label, Assembler::kNearJump);
- __ PushObject(bool_false);
- __ jmp(&done, Assembler::kNearJump);
- __ Bind(&true_label);
- __ PushObject(bool_true);
- }
- __ Bind(&done);
-}
-
-
-// Return false if the code cannot be generated. It is expected that
-// node->left() is Smi (or null for equality comparison).
-bool OptimizingCodeGenerator::GenerateSmiComparison(ComparisonNode* node) {
- if ((node->kind() == Token::kEQ) || (node->kind() == Token::kNE)) {
- GenerateSmiEquality(node);
- return true;
- }
- Condition condition;
- if (!SupportedTokenKindToSmiCondition(node->kind(), &condition)) {
- return false;
- }
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- const Bool& bool_false = Bool::ZoneHandle(Bool::False());
- CodeGenInfo left_info(node->left());
- CodeGenInfo right_info(node->right());
- VisitLoadTwo(node->left(), node->right(), EAX, EDX);
- if (!IsResultNeeded(node)) {
- return true;
- }
- if (left_info.IsClass(smi_class_) && right_info.IsClass(smi_class_)) {
- __ cmpl(EAX, EDX);
- } else if (left_info.IsClass(smi_class_) || right_info.IsClass(smi_class_)) {
- // One is Smi.
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, EAX, EDX, kDeoptSmiCompareSmis);
- Register reg_to_test = left_info.IsClass(smi_class_) ? EDX : EAX;
- __ testl(reg_to_test, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, deopt_blob->label());
- __ cmpl(EAX, EDX);
- } else {
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, ECX, EDX, kDeoptSmiCompareAny);
- __ movl(ECX, EAX);
- __ orl(EAX, EDX);
- __ testl(EAX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, deopt_blob->label());
- __ cmpl(ECX, EDX);
- }
- if (NodeInfoHasLabels(node)) {
- GenerateConditionalJumps(*(node->info()), condition);
- node->info()->set_labels_used(true);
- } else {
- Label true_label, done;
- __ j(condition, &true_label, Assembler::kNearJump);
- __ PushObject(bool_false);
- __ jmp(&done, Assembler::kNearJump);
- __ Bind(&true_label);
- __ PushObject(bool_true);
- __ Bind(&done);
- }
- return true;
-}
-
-
-static bool SupportedTokenKindToDoubleCondition(Token::Kind kind,
- Condition* condition) {
- switch (kind) {
- case Token::kEQ:
- *condition = EQUAL;
- return true;
- case Token::kLT:
- *condition = BELOW;
- return true;
- case Token::kGT:
- *condition = ABOVE;
- return true;
- case Token::kLTE:
- *condition = BELOW_EQUAL;
- return true;
- case Token::kGTE:
- *condition = ABOVE_EQUAL;
- return true;
- default:
- return false;
- }
-}
-
-
-// Checks if an inlined equality/non-equality operation can be emitted:
-// - type feedback must exist.
-// - no class in type feedback list overrides '=='.
-// - no Smi class in type feedback class list (Smi overrides equality operator).
-bool OptimizingCodeGenerator::GenerateEqualityComparison(ComparisonNode* node) {
- ASSERT((node->kind() == Token::kEQ) || (node->kind() == Token::kNE));
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- const Bool& bool_false = Bool::ZoneHandle(Bool::False());
- const ZoneGrowableArray<const Class*>* classes = CollectedClassesAtNode(node);
- if (classes == NULL) {
- return false;
- }
- const int num_classes = classes->length();
- // 'num_classes' can be 0 if the receiver was always null.
- const String& operator_name = String::Handle(String::NewSymbol("=="));
- // Check that all classes resolve to Object.==. Object.!= is not overridable
- // and is based on Object.==.
- ObjectStore* object_store = Isolate::Current()->object_store();
- Function& function = Function::Handle();
- for (intptr_t i = 0; i < num_classes; i++) {
- const Class& cls = *(*classes)[i];
- const int kNumArguments = 2; // 'this' and 'other' arguments.
- const int kNumNamedArguments = 0;
- function ^=
- Resolver::ResolveDynamicForReceiverClass(cls,
- operator_name,
- kNumArguments,
- kNumNamedArguments);
- ASSERT(!function.IsNull()); // '==' must be defined.
- if (function.owner() != object_store->object_class()) {
- // Overridden '==' operator exists skip optimized comparison.
- TraceNotOpt(node, "Equality comparison, overridden ==");
- return false;
- }
- if (cls.raw() == smi_class_.raw()) {
- // TODO(srdjan): implement mixed smi/non-smi comparison, for the moment
- // bail out.
- TraceNotOpt(node, "Equality comparison, mixed with Smi");
- return false;
- }
- }
-
- // All targets are Object.==, i.e., '==='. Smi is not among the classes.
- VisitLoadTwo(node->left(), node->right(), EAX, EDX);
- if (!IsResultNeeded(node)) {
- return true;
- }
- Label compare;
- // Comparison with NULL is "===".
- const Immediate raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- __ cmpl(EAX, raw_null);
- if (num_classes == 0) {
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, EAX, EDX, kDeoptEqualityNoFeedback);
- __ j(NOT_EQUAL, deopt_blob->label());
- } else {
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, EAX, EDX, kDeoptEqualityClassCheck);
- __ j(EQUAL, &compare);
- // Smi causes deoptimization.
- __ testl(EAX, Immediate(kSmiTagMask));
- __ j(ZERO, deopt_blob->label());
- __ LoadClassId(EBX, EAX);
- for (intptr_t i = 0; i < num_classes; i++) {
- const Class& cls = *(*classes)[i];
- __ cmpl(EBX, Immediate(cls.id()));
- if (i == (num_classes - 1)) {
- __ j(NOT_EQUAL, deopt_blob->label());
- } else {
- __ j(EQUAL, &compare);
- }
- }
- }
- __ Bind(&compare);
- __ cmpl(EAX, EDX);
- if (NodeInfoHasLabels(node)) {
- if (node->kind() == Token::kEQ) {
- GenerateConditionalJumps(*(node->info()), EQUAL);
- } else {
- GenerateConditionalJumps(*(node->info()), NOT_EQUAL);
- }
- node->info()->set_labels_used(true);
- } else {
- Label done, load_true;
- if (node->kind() == Token::kEQ) {
- __ j(EQUAL, &load_true, Assembler::kNearJump);
- } else {
- __ j(NOT_EQUAL, &load_true, Assembler::kNearJump);
- }
- __ PushObject(bool_false);
- __ jmp(&done, Assembler::kNearJump);
- __ Bind(&load_true);
- __ PushObject(bool_true);
- __ Bind(&done);
- }
- TraceOpt(node, "Equality comparison");
- return true;
-}
-
-
-// Return false if the code cannot be generated.
-bool OptimizingCodeGenerator::GenerateDoubleComparison(ComparisonNode* node) {
- Condition true_condition;
- if (!SupportedTokenKindToDoubleCondition(node->kind(), &true_condition)) {
- return false;
- }
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- const Bool& bool_false = Bool::ZoneHandle(Bool::False());
- CodeGenInfo left_info(node->left());
- CodeGenInfo right_info(node->right());
- left_info.set_allow_temp(true);
- right_info.set_allow_temp(true);
- VisitLoadTwo(node->left(), node->right(), EAX, EDX);
- DeoptimizationBlob* deopt_blob = NULL;
- if (!left_info.IsClass(double_class_) || !right_info.IsClass(double_class_)) {
- deopt_blob = AddDeoptimizationBlob(node, EAX, EDX, kDeoptDoubleComparison);
- }
- if (!left_info.IsClass(double_class_)) {
- CheckIfDoubleOrSmi(EAX, EBX, deopt_blob->label(), deopt_blob->label());
- PropagateBackLocalClass(node->left(), double_class_);
- }
- if (!right_info.IsClass(double_class_)) {
- CheckIfDoubleOrSmi(EDX, EBX, deopt_blob->label(), deopt_blob->label());
- PropagateBackLocalClass(node->right(), double_class_);
- }
- __ movsd(XMM0, FieldAddress(EAX, Double::value_offset()));
- __ movsd(XMM1, FieldAddress(EDX, Double::value_offset()));
- __ comisd(XMM0, XMM1);
- if (NodeInfoHasLabels(node)) {
- __ j(PARITY_EVEN, node->info()->false_label()); // NaN -> false;
- GenerateConditionalJumps(*(node->info()), true_condition);
- node->info()->set_labels_used(true);
- } else {
- Label is_false, is_true, done;
- __ j(PARITY_EVEN, &is_false, Assembler::kNearJump); // NaN -> false;
- __ j(true_condition, &is_true, Assembler::kNearJump);
- __ Bind(&is_false);
- if (IsResultNeeded(node)) {
- __ PushObject(bool_false);
- }
- __ jmp(&done);
- __ Bind(&is_true);
- if (IsResultNeeded(node)) {
- __ PushObject(bool_true);
- }
- __ Bind(&done);
- }
- return true;
-}
-
-
-// IS, ISNOT are handled in class CodeGenerator.
-void OptimizingCodeGenerator::VisitComparisonNode(ComparisonNode* node) {
- if ((node->kind() == Token::kEQ_STRICT) ||
- (node->kind() == Token::kNE_STRICT)) {
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- const Bool& bool_false = Bool::ZoneHandle(Bool::False());
- // Note that evaluation of right may cause deoptimization, therefore left
- // must be on stack when evaluating right.
- if (node->right()->IsLiteralNode()) {
- VisitLoadOne(node->left(), EAX);
- __ CompareObject(EAX, node->right()->AsLiteralNode()->literal());
- } else {
- VisitLoadTwo(node->left(), node->right(), EAX, EDX);
- __ cmpl(EAX, EDX);
- }
- if (!IsResultNeeded(node)) {
- return;
- }
- Condition condition = node->kind() == Token::kEQ_STRICT ? EQUAL : NOT_EQUAL;
- if (NodeInfoHasLabels(node)) {
- GenerateConditionalJumps(*(node->info()), condition);
- node->info()->set_labels_used(true);
- } else {
- Label done, is_true;
- __ j(condition, &is_true);
- __ PushObject(bool_false);
- __ jmp(&done);
- __ Bind(&is_true);
- __ PushObject(bool_true);
- __ Bind(&done);
- }
- return;
- }
-
- if (Token::IsTypeTestOperator(node->kind())) {
- VisitLoadOne(node->left(), EAX);
- ASSERT(node->right()->IsTypeNode());
- GenerateInstanceOf(node->id(),
- node->token_pos(),
- node->left(),
- node->right()->AsTypeNode()->type(),
- (node->kind() == Token::kISNOT));
- if (!IsResultNeeded(node)) {
- __ popl(EAX); // Pop the result of the instanceof operation.
- }
- return;
- }
-
- if (Token::IsTypeCastOperator(node->kind())) {
- // This code generator is not maintained anymore.
- UNIMPLEMENTED();
- }
-
- if (NodeHasClassAt(node, smi_class_, 0)) {
- if (GenerateSmiComparison(node)) {
- // The comparison was handled, code was emitted.
- return;
- }
- // Fall through if condition is not supported.
- } else if (NodeHasClassAt(node, double_class_, 0)) {
- // Double comparison.
- if (GenerateDoubleComparison(node)) {
- return;
- }
- } else if ((node->kind() == Token::kEQ) || (node->kind() == Token::kNE)) {
- // Equality, not-equality comparison of any other type.
- if (GenerateEqualityComparison(node)) {
- return;
- }
- }
-
- // Fall through here if a comparison was not implemented.
- // TODO(srdjan): Implement for Strings.
- CodeGenerator::VisitComparisonNode(node);
-}
-
-
-void OptimizingCodeGenerator::VisitLoadIndexedNode(LoadIndexedNode* node) {
- const char* kMessage = "Inline indexed access";
- ObjectStore* object_store = Isolate::Current()->object_store();
- const Class& object_array_class =
- Class::ZoneHandle(object_store->array_class());
- const Class& immutable_object_array_class =
- Class::ZoneHandle(object_store->immutable_array_class());
- if (NodeHasClassAt(node, object_array_class, 0) ||
- NodeHasClassAt(node, immutable_object_array_class, 0)) {
- CodeGenInfo array_info(node->array());
- CodeGenInfo index_info(node->index_expr());
- VisitLoadTwo(node->array(), node->index_expr(), EBX, EDX);
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, EBX, EDX, kDeoptLoadIndexedFixedArray);
- const Class& test_class = NodeHasClassAt(node, object_array_class, 0) ?
- object_array_class : immutable_object_array_class;
- // Type checks of array.
- if (!array_info.IsClass(test_class)) {
- __ testl(EBX, Immediate(kSmiTagMask)); // Deoptimize if Smi.
- __ j(ZERO, deopt_blob->label());
- __ CompareClassId(EBX, test_class.id(), EAX);
- __ j(NOT_EQUAL, deopt_blob->label());
- PropagateBackLocalClass(node->array(), test_class);
- }
-
- // Type check of index.
- if (!index_info.IsClass(smi_class_)) {
- __ testl(EDX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, deopt_blob->label());
- PropagateBackLocalClass(node->index_expr(), smi_class_);
- }
- // Range check.
- __ cmpl(EDX, FieldAddress(EBX, Array::length_offset()));
- __ j(ABOVE_EQUAL, deopt_blob->label());
- // Note that EDX is Smi, i.e, times 2.
- ASSERT(kSmiTagShift == 1);
- __ movl(EAX, FieldAddress(EBX, EDX, TIMES_2, sizeof(RawArray)));
- HandleResult(node, EAX);
- TraceOpt(node, kMessage);
- return;
- }
-
- if (NodeHasClassAt(node, growable_object_array_class_, 0)) {
- CodeGenInfo array_info(node->array());
- CodeGenInfo index_info(node->index_expr());
- VisitLoadTwo(node->array(), node->index_expr(), EDX, EAX);
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, EDX, EAX, kDeoptLoadIndexedGrowableArray);
- // EAX: index, EDX: array.
- if (!index_info.IsClass(smi_class_)) {
- __ testl(EAX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, deopt_blob->label()); // Not Smi index.
- PropagateBackLocalClass(node->index_expr(), smi_class_);
- }
- if (!array_info.IsClass(growable_object_array_class_)) {
- __ testl(EDX, Immediate(kSmiTagMask));
- __ j(ZERO, deopt_blob->label()); // Array is Smi.
- __ CompareClassId(EDX, kGrowableObjectArray, EBX);
- __ j(NOT_EQUAL, deopt_blob->label()); // Not GrowableObjectArray.
- PropagateBackLocalClass(node->array(), growable_object_array_class_);
- }
- // Range check: deoptimize if out of bounds.
- __ cmpl(EAX, FieldAddress(EDX, GrowableObjectArray::length_offset()));
- __ j(ABOVE_EQUAL, deopt_blob->label());
- __ movl(EDX, FieldAddress(EDX, GrowableObjectArray::data_offset()));
- // Note that EAX is Smi, i.e, times 2.
- ASSERT(kSmiTagShift == 1);
- __ movl(EAX, FieldAddress(EDX, EAX, TIMES_2, sizeof(RawArray)));
- HandleResult(node, EAX);
- return;
- } else {
- // E.g., HashMap.
- TraceNotOpt(node, kMessage);
- }
- CodeGenerator::VisitLoadIndexedNode(node);
-}
-
-
-void OptimizingCodeGenerator::VisitStoreIndexedNode(StoreIndexedNode* node) {
- if (FLAG_enable_type_checks) {
- CodeGenerator::VisitStoreIndexedNode(node);
- return;
- }
- Class& class_of_this_array = Class::Handle();
- // Load array and release its CodeGenInfo as value may refer to the same
- // array (e.g. in a[x] += 3). Fixes issue 1570.
- {
- CodeGenInfo array_info(node->array());
- node->array()->Visit(this);
- class_of_this_array = array_info.is_class()->raw();
- }
- // TODO(srdjan): Use VisitLoadTwo and check if index is smi (CodeGenInfo).
- ObjectStore* object_store = Isolate::Current()->object_store();
- const Class& object_array_class =
- Class::ZoneHandle(object_store->array_class());
- const ICData& ic_data = node->ic_data();
- if (ic_data.NumberOfChecks() == 0) {
- VisitLoadTwo(node->index_expr(), node->value(), EBX, ECX);
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, EBX, ECX, kDeoptNoTypeFeedback);
- __ jmp(deopt_blob->label());
- return;
- }
-
-
- if (NodeHasClassAt(node, object_array_class, 0)) {
- // Release CodeGenInfo of index quickly as it may be used in the value,
- // e.g. a[i] += 3. Fixes issue 1570.
- bool index_is_smi = false;
- {
- CodeGenInfo index_info(node->index_expr());
- node->index_expr()->Visit(this);
- index_is_smi = index_info.IsClass(smi_class_);
- }
- VisitLoadOne(node->value(), ECX);
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, EAX, EBX, ECX, kDeoptStoreIndexed);
- __ popl(EBX); // index.
- __ popl(EAX); // array.
- // ECX: value, EBX:index, EAX: array.
- // Check class of array.
- if (class_of_this_array.raw() != object_array_class.raw()) {
- __ testl(EAX, Immediate(kSmiTagMask));
- __ j(ZERO, deopt_blob->label()); // Array is smi -> deopt.
- __ CompareClassId(EAX, kArray, EDX);
- __ j(NOT_EQUAL, deopt_blob->label()); // Not ObjectArray -> deopt.
- PropagateBackLocalClass(node->array(), object_array_class);
- }
- // Check class of index.
- if (!index_is_smi) {
- __ testl(EBX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, deopt_blob->label()); // Index not Smi -> deopt.
- PropagateBackLocalClass(node->index_expr(), smi_class_);
- }
- // Range check.
- __ cmpl(EBX, FieldAddress(EAX, Array::length_offset()));
- __ j(ABOVE_EQUAL, deopt_blob->label()); // Range error -> deopt.
- ASSERT(kSmiTagShift == 1);
- __ StoreIntoObject(EAX,
- FieldAddress(EAX, EBX, TIMES_2, sizeof(RawArray)),
- ECX);
- HandleResult(node, ECX);
- return;
- }
-
- if (NodeHasClassAt(node, growable_object_array_class_, 0)) {
- bool index_is_smi = false;
- // Release CodeGenInfo of index quickly as it may be used in the value,
- // e.g. a[i] += 3. Fixes issue 1570.
- {
- CodeGenInfo index_info(node->index_expr());
- node->index_expr()->Visit(this);
- index_is_smi = index_info.IsClass(smi_class_);
- }
- VisitLoadOne(node->value(), ECX);
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, EAX, EBX, ECX, kDeoptStoreIndexed);
- __ popl(EBX); // index.
- __ popl(EAX); // array.
- // ECX: value, EBX:index, EAX: array, EDX: scratch.
- // Check class of array.
- if (class_of_this_array.raw() != growable_object_array_class_.raw()) {
- __ testl(EAX, Immediate(kSmiTagMask));
- __ j(ZERO, deopt_blob->label()); // Array is smi -> deopt.
- __ CompareClassId(EAX, kGrowableObjectArray, EDX);
- __ j(NOT_EQUAL, deopt_blob->label()); // Not GrowableObjectArray.
- PropagateBackLocalClass(node->array(), growable_object_array_class_);
- }
- // Check class of index.
- if (!index_is_smi) {
- __ testl(EBX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, deopt_blob->label()); // Index not Smi -> deopt.
- PropagateBackLocalClass(node->index_expr(), smi_class_);
- }
- // Range check: deoptimize if out of bounds.
- __ cmpl(EBX, FieldAddress(EAX, GrowableObjectArray::length_offset()));
- __ j(ABOVE_EQUAL, deopt_blob->label());
- __ movl(EDX, FieldAddress(EAX, GrowableObjectArray::data_offset()));
- // Note that EAX is Smi, i.e, times 2.
- ASSERT(kSmiTagShift == 1);
- __ StoreIntoObject(EDX,
- FieldAddress(EDX, EBX, TIMES_2, sizeof(RawArray)),
- ECX);
- HandleResult(node, ECX);
- return;
- }
- node->index_expr()->Visit(this);
- node->value()->Visit(this);
- GenerateStoreIndexed(node->id(), node->token_pos(), IsResultNeeded(node));
-}
-
-
-void OptimizingCodeGenerator::VisitForNode(ForNode* node) {
- if (FLAG_enable_type_checks) {
- CodeGenerator::VisitForNode(node);
- return;
- }
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- node->initializer()->Visit(this);
- SourceLabel* label = node->label();
- Label loop;
- __ Bind(&loop);
- if (node->condition() != NULL) {
- Label iterate_label;
- CodeGenInfo condition_info(node->condition());
- condition_info.set_false_label(label->break_label());
- condition_info.set_true_label(&iterate_label);
- condition_info.set_fallthrough_label(&iterate_label);
- node->condition()->Visit(this);
- if (condition_info.labels_used()) {
- __ Bind(&iterate_label);
- } else {
- __ popl(EAX);
- __ LoadObject(EDX, bool_true);
- __ cmpl(EAX, EDX);
- __ j(NOT_EQUAL, label->break_label());
- }
- }
- node->body()->Visit(this);
- HandleBackwardBranch(node->id(), node->token_pos());
- __ Bind(label->continue_label());
- node->increment()->Visit(this);
- __ jmp(&loop);
- __ Bind(label->break_label());
-}
-
-
-void OptimizingCodeGenerator::VisitDoWhileNode(DoWhileNode* node) {
- if (FLAG_enable_type_checks) {
- CodeGenerator::VisitDoWhileNode(node);
- return;
- }
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- SourceLabel* label = node->label();
- Label loop;
- __ Bind(&loop);
- node->body()->Visit(this);
- HandleBackwardBranch(node->id(), node->token_pos());
- __ Bind(label->continue_label());
- CodeGenInfo condition_info(node->condition());
- condition_info.set_false_label(label->break_label());
- condition_info.set_true_label(&loop);
- condition_info.set_fallthrough_label(label->break_label());
- node->condition()->Visit(this);
- if (!condition_info.labels_used()) {
- __ popl(EAX);
- __ LoadObject(EDX, bool_true);
- __ cmpl(EAX, EDX);
- __ j(EQUAL, &loop);
- }
- __ Bind(label->break_label());
-}
-
-
-void OptimizingCodeGenerator::VisitWhileNode(WhileNode* node) {
- if (FLAG_enable_type_checks) {
- CodeGenerator::VisitWhileNode(node);
- return;
- }
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- SourceLabel* label = node->label();
- __ Bind(label->continue_label());
- Label iterate_label;
- CodeGenInfo condition_info(node->condition());
- condition_info.set_false_label(label->break_label());
- condition_info.set_true_label(&iterate_label);
- condition_info.set_fallthrough_label(&iterate_label);
- node->condition()->Visit(this);
- if (condition_info.labels_used()) {
- __ Bind(&iterate_label);
- } else {
- __ popl(EAX);
- __ LoadObject(EDX, bool_true);
- __ cmpl(EAX, EDX);
- __ j(NOT_EQUAL, label->break_label());
- }
- node->body()->Visit(this);
- HandleBackwardBranch(node->id(), node->token_pos());
- __ jmp(label->continue_label());
- __ Bind(label->break_label());
-}
-
-
-void OptimizingCodeGenerator::VisitIfNode(IfNode* node) {
- if (FLAG_enable_type_checks) {
- CodeGenerator::VisitIfNode(node);
- return;
- }
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- Label false_label, true_label, done;
- CodeGenInfo condition_info(node->condition());
- condition_info.set_false_label(&false_label);
- condition_info.set_true_label(&true_label);
- condition_info.set_fallthrough_label(&true_label);
- node->condition()->Visit(this);
- if (condition_info.labels_used()) {
- __ Bind(&true_label);
- } else {
- __ popl(EAX);
- __ CompareObject(EAX, bool_true);
- __ j(NOT_EQUAL, &false_label);
- }
- node->true_branch()->Visit(this);
- if (node->false_branch() != NULL) {
- Label done;
- __ jmp(&done);
- __ Bind(&false_label);
- node->false_branch()->Visit(this);
- __ Bind(&done);
- } else {
- __ Bind(&false_label);
- }
- __ Bind(&done);
-}
-
-
-void OptimizingCodeGenerator::GenerateDirectCall(
- intptr_t node_id,
- intptr_t token_pos,
- const Function& target,
- intptr_t arg_count,
- const Array& optional_argument_names) {
- ASSERT(!target.IsNull());
- const Code& code = Code::Handle(target.CurrentCode());
- ASSERT(!code.IsNull());
- ExternalLabel target_label("DirectInstanceCall", code.EntryPoint());
-
- __ LoadObject(ECX, target);
- __ LoadObject(EDX, ArgumentsDescriptor(arg_count, optional_argument_names));
- __ call(&target_label);
- AddCurrentDescriptor(PcDescriptors::kOther, node_id, token_pos);
- __ addl(ESP, Immediate(arg_count * kWordSize));
-}
-
-
-// Generate inline cache calls instead of deoptimizing when no type feedback is
-// provided.
-// TODO(srdjan): Recompilation framework should recognize active IC calls
-// in optimized code and mark them for reoptimization since type feedback was
-// collected in the meantime.
-void OptimizingCodeGenerator::GenerateInlineCacheCall(
- intptr_t node_id,
- intptr_t token_pos,
- const ICData& ic_data,
- intptr_t num_args,
- const Array& optional_arguments_names) {
- __ LoadObject(ECX, ic_data);
- __ LoadObject(EDX, ArgumentsDescriptor(num_args, optional_arguments_names));
-
- uword label_address = 0;
- switch (ic_data.num_args_tested()) {
- case 1:
- label_address = StubCode::OneArgCheckInlineCacheEntryPoint();
- break;
- case 2:
- label_address = StubCode::TwoArgsCheckInlineCacheEntryPoint();
- break;
- default:
- UNIMPLEMENTED();
- }
-
- ExternalLabel target_label("InlineCache", label_address);
-
- __ call(&target_label);
- AddCurrentDescriptor(PcDescriptors::kIcCall,
- node_id,
- token_pos);
- __ addl(ESP, Immediate(num_args * kWordSize));
-}
-
-
-// Normalizes the ic_data class/target pairs:
-// - If Smi class exists, make it the first one.
-// - If 'null_target' not null, append null-class/'null_target'
-void OptimizingCodeGenerator::NormalizeClassChecks(
- const ICData& ic_data,
- const Function& null_target,
- GrowableArray<const Class*>* classes,
- GrowableArray<const Function*>* targets) {
- ASSERT(classes != NULL);
- ASSERT(targets != NULL);
- // Check if we can add Smi class in front.
- Function& smi_target = Function::ZoneHandle();
- for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
- GrowableArray<intptr_t> test_class_ids;
- ic_data.GetCheckAt(i, &test_class_ids, &smi_target);
- if (test_class_ids[0] == kSmi) {
- classes->Add(&Class::ZoneHandle(smi_class_.raw()));
- targets->Add(&Function::ZoneHandle(smi_target.raw()));
- break;
- }
- }
- // Add all classes except Smi.
- for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
- Function& target = Function::ZoneHandle();
- GrowableArray<intptr_t> test_class_ids;
- ic_data.GetCheckAt(i, &test_class_ids, &target);
- ASSERT(test_class_ids[0] != kNullClassId);
- if (test_class_ids[0] != kSmi) {
- const Class& cls =
- Class::Handle(Isolate::Current()->class_table()->At(
- test_class_ids[0]));
- ASSERT(!cls.IsNull());
- ASSERT(!target.IsNull());
- classes->Add(&cls);
- targets->Add(&target);
- }
- }
- // Do not add a target that has not been compiled yet.
- if (!null_target.IsNull() && null_target.HasCode()) {
- ASSERT(null_target.IsZoneHandle());
- classes->Add(&Class::ZoneHandle(Object::null_class()));
- targets->Add(&null_target);
- }
-}
-
-
-// Use IC data in 'node' to issues checks and calls.
-// IC data can contain one or more argument checks.
-void OptimizingCodeGenerator::GenerateCheckedInstanceCalls(
- AstNode* node,
- AstNode* receiver,
- intptr_t token_pos,
- intptr_t num_args,
- const Array& optional_arguments_names) {
- ASSERT(node != NULL);
- ASSERT(receiver != NULL);
- ASSERT(num_args > 0);
- const ICData& ic_data = node->ic_data();
- if (ic_data.NumberOfChecks() == 0) {
- // No type feedback means node was never executed. However that can be
- // a common case especially in case of large switch statements.
- // Use a special inline cache call which can help us decide when to
- // re-optimize this optiumized function.
- GenerateInlineCacheCall(
- node->id(), token_pos, ic_data, num_args, optional_arguments_names);
- return;
- }
-
- Function& target_for_null = Function::ZoneHandle();
- ObjectStore* object_store = Isolate::Current()->object_store();
- int num_optional_args =
- optional_arguments_names.IsNull() ? 0 : optional_arguments_names.Length();
- target_for_null = Resolver::ResolveDynamicForReceiverClass(
- Class::Handle(object_store->object_class()),
- String::Handle(ic_data.target_name()),
- num_args,
- num_optional_args);
- GrowableArray<const Class*> classes;
- GrowableArray<const Function*> targets;
- // Make Smi class the first one, if it is in the list.
- NormalizeClassChecks(ic_data, target_for_null, &classes, &targets);
- ASSERT(!classes.is_empty());
- ASSERT(classes.length() == targets.length());
- intptr_t start_ix = 0;
-
- Label done;
- __ movl(EAX, Address(ESP, (num_args - 1) * kWordSize)); // Load receiver.
- if (classes[0]->raw() == smi_class_.raw()) {
- start_ix++;
- // Smi test is needed.
- __ testl(EAX, Immediate(kSmiTagMask));
- if (classes.length() == 1) {
- // Only Smi test.
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, kDeoptPolymorphicInstanceCallSmiOnly);
- __ j(NOT_ZERO, deopt_blob->label());
- GenerateDirectCall(node->id(),
- token_pos,
- *targets[0],
- num_args,
- optional_arguments_names);
- return;
- }
- Label not_smi;
- __ j(NOT_ZERO, &not_smi);
- GenerateDirectCall(node->id(),
- token_pos,
- *targets[0],
- num_args,
- optional_arguments_names);
- __ jmp(&done);
- __ Bind(&not_smi); // Continue with other test below.
- } else if (NodeMayBeSmi(receiver)) {
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, kDeoptPolymorphicInstanceCallSmiFail);
- __ testl(EAX, Immediate(kSmiTagMask));
- __ j(ZERO, deopt_blob->label());
- } else {
- // Receiver cannot be Smi, no need to test it.
- }
- __ LoadClassId(EAX, EAX); // Receiver's class id.
- for (intptr_t i = start_ix; i < classes.length(); i++) {
- const Class& cls = *classes[i];
- const Function& target = *targets[i];
- __ cmpl(EAX, Immediate(cls.id()));
- if (i == (classes.length() - 1)) {
- // Last check.
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, kDeoptPolymorphicInstanceCallTestFail);
- __ j(NOT_EQUAL, deopt_blob->label());
- GenerateDirectCall(node->id(),
- token_pos,
- target,
- num_args,
- optional_arguments_names);
- } else {
- Label next;
- __ j(NOT_EQUAL, &next);
- GenerateDirectCall(node->id(),
- token_pos,
- target,
- num_args,
- optional_arguments_names);
- __ jmp(&done);
- __ Bind(&next);
- }
- }
- __ Bind(&done);
-}
-
-
-void OptimizingCodeGenerator::VisitInstanceCallNode(InstanceCallNode* node) {
- const int number_of_arguments = node->arguments()->length() + 1;
- // Compute the receiver object and pass it as first argument to call.
- node->receiver()->Visit(this);
- // Now compute rest of the arguments to the call.
- node->arguments()->Visit(this);
- if (TryInlineInstanceCall(node)) {
- // Instance call is inlined.
- } else {
- GenerateCheckedInstanceCalls(node,
- node->receiver(),
- node->token_pos(),
- number_of_arguments,
- node->arguments()->names());
- }
- // Result is in EAX.
- HandleResult(node, EAX);
-}
-
-
-// Returns true if an instance call was replaced with its intrinsic.
-// Returns result in EAX.
-bool OptimizingCodeGenerator::TryInlineInstanceCall(InstanceCallNode* node) {
- const ZoneGrowableArray<const Class*>* classes = CollectedClassesAtNode(node);
- if ((classes != NULL) && (classes->length() == 1)) {
- const int num_arguments = node->arguments()->length() + 1;
- const int num_named_arguments = node->arguments()->names().IsNull() ?
- 0 : node->arguments()->names().Length();
- const Function& target = Function::ZoneHandle(
- Resolver::ResolveDynamicForReceiverClass(*(*classes)[0],
- node->function_name(),
- num_arguments,
- num_named_arguments));
- Recognizer::Kind recognized = Recognizer::RecognizeKind(target);
- if (FLAG_trace_optimization) {
- OS::Print("Monomorphic inline candidate: %s -> %s\n",
- target.ToFullyQualifiedCString(),
- Recognizer::KindToCString(recognized));
- }
- if ((recognized == Recognizer::kIntegerToDouble) &&
- NodeHasClassAt(node, smi_class_, 0)) {
- // TODO(srdjan): Check if we could use temporary double instead of
- // allocating a new object every time.
- const Code& stub =
- Code::Handle(StubCode::GetAllocationStubForClass(double_class_));
- const ExternalLabel label(double_class_.ToCString(), stub.EntryPoint());
- GenerateCall(node->token_pos(), &label, PcDescriptors::kOther);
- // EAX is double object.
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, EBX, kDeoptIntegerToDouble);
- __ popl(EBX); // Receiver
- __ testl(EBX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, deopt_blob->label()); // Deoptimize if not Smi.
- __ SmiUntag(EBX);
- __ cvtsi2sd(XMM0, EBX);
- __ movsd(FieldAddress(EAX, Double::value_offset()), XMM0);
- return true;
- }
-
- if ((recognized == Recognizer::kDoubleToDouble) &&
- NodeHasClassAt(node, double_class_, 0)) {
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, EAX, kDeoptDoubleToDouble);
- __ popl(EAX);
- CheckIfDoubleOrSmi(EAX, EBX, deopt_blob->label(), deopt_blob->label());
- return true;
- }
- }
- return false;
-}
-
-
-// TODO(srdjan): For Math.sqrt read type feedback in Math.sqrt and decide
-// if the argument is double, smi or something else.
-bool OptimizingCodeGenerator::TryInlineStaticCall(StaticCallNode* node) {
- Recognizer::Kind recognized = Recognizer::RecognizeKind(node->function());
- if (recognized == Recognizer::kMathSqrt) {
- Label smi_to_double, call_method, done;
- __ movl(EAX, Address(ESP, 0));
- CheckIfDoubleOrSmi(EAX, EBX, &smi_to_double, &call_method);
- __ movsd(XMM1, FieldAddress(EAX, Double::value_offset()));
- __ sqrtsd(XMM0, XMM1);
- AssemblerMacros::TryAllocate(assembler_,
- double_class_,
- &call_method,
- EAX); // Result register.
- __ movsd(FieldAddress(EAX, Double::value_offset()), XMM0);
- __ jmp(&done);
- __ Bind(&smi_to_double);
- __ Bind(&call_method);
- __ LoadObject(ECX, node->function());
- __ LoadObject(EDX, ArgumentsDescriptor(node->arguments()->length(),
- node->arguments()->names()));
- GenerateCall(node->token_pos(), &StubCode::CallStaticFunctionLabel(),
- PcDescriptors::kFuncCall);
- __ Bind(&done);
- return true;
- }
- return false;
-}
-
-
-void OptimizingCodeGenerator::VisitStaticCallNode(StaticCallNode* node) {
- node->arguments()->Visit(this);
- if (TryInlineStaticCall(node)) {
- // Static method is inlined, result is in EAX.
- } else {
- __ LoadObject(ECX, node->function());
- __ LoadObject(EDX, ArgumentsDescriptor(node->arguments()->length(),
- node->arguments()->names()));
- GenerateCall(node->token_pos(), &StubCode::CallStaticFunctionLabel(),
- PcDescriptors::kFuncCall);
- }
- __ addl(ESP, Immediate(node->arguments()->length() * kWordSize));
- // Result is in EAX.
- HandleResult(node, EAX);
-}
-
-
-void OptimizingCodeGenerator::VisitReturnNode(ReturnNode* node) {
- if ((node->inlined_finally_list_length() > 0) || FLAG_enable_type_checks) {
- CodeGenerator::VisitReturnNode(node);
- return;
- }
- ASSERT(!IsResultNeeded(node));
- ASSERT(node->value() != NULL);
- CodeGenInfo value_info(node->value());
- value_info.set_request_result_in_eax(true);
- node->value()->Visit(this);
- if (!value_info.result_returned_in_eax()) {
- __ popl(EAX);
- }
- GenerateReturnEpilog(node);
-}
-
-
-void OptimizingCodeGenerator::VisitSequenceNode(SequenceNode* node_sequence) {
- // TODO(srdjan): Allow limited forwarding of types across sequence nodes.
- classes_for_locals_->Clear();
- const intptr_t num_context_variables = (node_sequence->scope() != NULL) ?
- node_sequence->scope()->num_context_variables() : 0;
- if (FLAG_enable_type_checks || (num_context_variables > 0)) {
- CodeGenerator::VisitSequenceNode(node_sequence);
- return;
- }
- for (int i = 0; i < node_sequence->length(); i++) {
- AstNode* child_node = node_sequence->NodeAt(i);
- state()->set_root_node(child_node);
- child_node->Visit(this);
- }
- if (node_sequence->label() != NULL) {
- __ Bind(node_sequence->label()->break_label());
- }
- classes_for_locals_->Clear();
-}
-
-
-void OptimizingCodeGenerator::VisitStoreInstanceFieldNode(
- StoreInstanceFieldNode* node) {
- if (FLAG_enable_type_checks) {
- CodeGenerator::VisitStoreInstanceFieldNode(node);
- return;
- }
- VisitLoadTwo(node->instance(), node->value(), EDX, EAX);
- __ StoreIntoObject(EDX, FieldAddress(EDX, node->field().Offset()), EAX);
- ASSERT(!IsResultNeeded(node));
-}
-
-
-void OptimizingCodeGenerator::VisitCatchClauseNode(CatchClauseNode* node) {
- // TODO(srdjan): Set classes for locals.
- classes_for_locals_->Clear();
- CodeGenerator::VisitCatchClauseNode(node);
-}
-
-
-void OptimizingCodeGenerator::VisitTryCatchNode(TryCatchNode* node) {
- // TODO(srdjan): Set classes for locals.
- classes_for_locals_->Clear();
- CodeGenerator::VisitTryCatchNode(node);
-}
-
-
-void OptimizingCodeGenerator::VisitUnaryOpNode(UnaryOpNode* node) {
- // TODO(srdjan): Test in checked mode if value is Boolean, throw error
- // otherwise.
- if (FLAG_enable_type_checks && node->kind() == Token::kNOT) {
- CodeGenerator::VisitUnaryOpNode(node);
- return;
- }
- // TODO(srdjan): Jump directly to labels instead of returning a boolean.
- if (node->kind() == Token::kNOT) {
- // Only a true bool returns false, everything else is true.
- CodeGenInfo info(node->operand());
- VisitLoadOne(node->operand(), EDX);
- Label done;
- __ LoadObject(EAX, Bool::ZoneHandle(Bool::True()));
- __ cmpl(EDX, EAX);
- __ j(NOT_EQUAL, &done, Assembler::kNearJump);
- __ LoadObject(EAX, Bool::ZoneHandle(Bool::False()));
- __ Bind(&done);
- HandleResult(node, EAX);
- return;
- }
-
- if ((node->kind() == Token::kSUB) || (node->kind() == Token::kBIT_NOT)) {
- if (NodeHasClassAt(node, smi_class_, 0)) {
- const ICData& ic_data = node->ic_data();
- ASSERT(ic_data.num_args_tested() == 1);
- GenerateSmiUnaryOp(node);
- return;
- }
- }
- if (node->kind() == Token::kSUB) {
- if (NodeHasClassAt(node, double_class_, 0)) {
- const ICData& ic_data = node->ic_data();
- ASSERT(ic_data.num_args_tested() == 1);
- GenerateDoubleUnaryOp(node);
- return;
- }
- }
- // TODO(srdjan): Implement unary kSUB (negate) Mint.
- CodeGenerator::VisitUnaryOpNode(node);
-}
-
-
-} // namespace dart
-
-#endif // defined TARGET_ARCH_IA32

Powered by Google App Engine
This is Rietveld 408576698