| Index: runtime/vm/flow_graph_optimizer.cc
|
| ===================================================================
|
| --- runtime/vm/flow_graph_optimizer.cc (revision 29797)
|
| +++ runtime/vm/flow_graph_optimizer.cc (working copy)
|
| @@ -26,6 +26,9 @@
|
| DEFINE_FLAG(bool, load_cse, true, "Use redundant load elimination.");
|
| DEFINE_FLAG(int, max_polymorphic_checks, 4,
|
| "Maximum number of polymorphic check, otherwise it is megamorphic.");
|
| +DEFINE_FLAG(int, max_equality_polymorphic_checks, 32,
|
| + "Maximum number of polymorphic checks in equality operator,"
|
| + " otherwise use megamorphic dispatch.");
|
| DEFINE_FLAG(bool, remove_redundant_phis, true, "Remove redundant phis.");
|
| DEFINE_FLAG(bool, trace_constant_propagation, false,
|
| "Print constant propagation and useless code elimination.");
|
| @@ -85,9 +88,6 @@
|
| ComparisonInstr* compare = instr->AsBranch()->comparison();
|
| if (compare->IsStrictCompare()) {
|
| VisitStrictCompare(compare->AsStrictCompare());
|
| - } else if (compare->IsEqualityCompare()) {
|
| - StrictifyEqualityCompare(compare->AsEqualityCompare(),
|
| - instr->AsBranch());
|
| }
|
| }
|
| }
|
| @@ -1288,7 +1288,102 @@
|
|
|
| static bool SmiFitsInDouble() { return kSmiBits < 53; }
|
|
|
| +bool FlowGraphOptimizer::TryReplaceWithEqualityOp(InstanceCallInstr* call,
|
| + Token::Kind op_kind) {
|
| + const ICData& ic_data = *call->ic_data();
|
| + ASSERT(ic_data.num_args_tested() == 2);
|
|
|
| + ASSERT(call->ArgumentCount() == 2);
|
| + Definition* left = call->ArgumentAt(0);
|
| + Definition* right = call->ArgumentAt(1);
|
| +
|
| + intptr_t cid = kIllegalCid;
|
| + if (HasOnlyTwoOf(ic_data, kSmiCid)) {
|
| + InsertBefore(call,
|
| + new CheckSmiInstr(new Value(left), call->deopt_id()),
|
| + call->env(),
|
| + Definition::kEffect);
|
| + InsertBefore(call,
|
| + new CheckSmiInstr(new Value(right), call->deopt_id()),
|
| + call->env(),
|
| + Definition::kEffect);
|
| + cid = kSmiCid;
|
| + } else if (HasTwoMintOrSmi(ic_data) &&
|
| + FlowGraphCompiler::SupportsUnboxedMints()) {
|
| + cid = kMintCid;
|
| + } else if (HasTwoDoubleOrSmi(ic_data)) {
|
| + // Use double comparison.
|
| + if (SmiFitsInDouble()) {
|
| + cid = kDoubleCid;
|
| + } else {
|
| + if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) {
|
| + // We cannot use double comparison on two smis. Need polymorphic
|
| + // call.
|
| + return false;
|
| + } else {
|
| + InsertBefore(call,
|
| + new CheckEitherNonSmiInstr(new Value(left),
|
| + new Value(right),
|
| + call->deopt_id()),
|
| + call->env(),
|
| + Definition::kEffect);
|
| + cid = kDoubleCid;
|
| + }
|
| + }
|
| + } else {
|
| + // Check if ICDData contains checks with Smi/Null combinations. In that case
|
| + // we can still emit the optimized Smi equality operation but need to add
|
| + // checks for null or Smi.
|
| + GrowableArray<intptr_t> smi_or_null(2);
|
| + smi_or_null.Add(kSmiCid);
|
| + smi_or_null.Add(kNullCid);
|
| + if (ICDataHasOnlyReceiverArgumentClassIds(ic_data,
|
| + smi_or_null,
|
| + smi_or_null)) {
|
| + const ICData& unary_checks_0 =
|
| + ICData::ZoneHandle(call->ic_data()->AsUnaryClassChecks());
|
| + AddCheckClass(left,
|
| + unary_checks_0,
|
| + call->deopt_id(),
|
| + call->env(),
|
| + call);
|
| +
|
| + const ICData& unary_checks_1 =
|
| + ICData::ZoneHandle(call->ic_data()->AsUnaryClassChecksForArgNr(1));
|
| + AddCheckClass(right,
|
| + unary_checks_1,
|
| + call->deopt_id(),
|
| + call->env(),
|
| + call);
|
| + cid = kSmiCid;
|
| + } else {
|
| + // Shortcut for equality with null.
|
| + ConstantInstr* right_const = right->AsConstant();
|
| + ConstantInstr* left_const = left->AsConstant();
|
| + if ((right_const != NULL && right_const->value().IsNull()) ||
|
| + (left_const != NULL && left_const->value().IsNull())) {
|
| + StrictCompareInstr* comp = new StrictCompareInstr(call->token_pos(),
|
| + Token::kEQ_STRICT,
|
| + new Value(left),
|
| + new Value(right));
|
| + ReplaceCall(call, comp);
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + }
|
| + ASSERT(cid != kIllegalCid);
|
| + EqualityCompareInstr* comp = new EqualityCompareInstr(call->token_pos(),
|
| + op_kind,
|
| + new Value(left),
|
| + new Value(right),
|
| + cid,
|
| + call->deopt_id());
|
| + ReplaceCall(call, comp);
|
| + return true;
|
| +}
|
| +
|
| +
|
| bool FlowGraphOptimizer::TryReplaceWithRelationalOp(InstanceCallInstr* call,
|
| Token::Kind op_kind) {
|
| const ICData& ic_data = *call->ic_data();
|
| @@ -2933,6 +3028,18 @@
|
| }
|
|
|
|
|
| +static Definition* OriginalDefinition(Definition* defn) {
|
| + while (defn->IsRedefinition() || defn->IsAssertAssignable()) {
|
| + if (defn->IsRedefinition()) {
|
| + defn = defn->AsRedefinition()->value()->definition();
|
| + } else {
|
| + defn = defn->AsAssertAssignable()->value()->definition();
|
| + }
|
| + }
|
| + return defn;
|
| +}
|
| +
|
| +
|
| // TODO(srdjan): Use ICData to check if always true or false.
|
| void FlowGraphOptimizer::ReplaceWithInstanceOf(InstanceCallInstr* call) {
|
| ASSERT(Token::IsTypeTestOperator(call->token_kind()));
|
| @@ -2941,8 +3048,8 @@
|
| Definition* type_args = call->ArgumentAt(2);
|
| const AbstractType& type =
|
| AbstractType::Cast(call->ArgumentAt(3)->AsConstant()->value());
|
| - const bool negate =
|
| - Bool::Cast(call->ArgumentAt(4)->AsConstant()->value()).value();
|
| + const bool negate = Bool::Cast(
|
| + OriginalDefinition(call->ArgumentAt(4))->AsConstant()->value()).value();
|
| const ICData& unary_checks =
|
| ICData::ZoneHandle(call->ic_data()->AsUnaryClassChecks());
|
| if (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks) {
|
| @@ -3041,7 +3148,10 @@
|
| const ICData& unary_checks =
|
| ICData::ZoneHandle(instr->ic_data()->AsUnaryClassChecks());
|
|
|
| - if ((unary_checks.NumberOfChecks() > FLAG_max_polymorphic_checks) &&
|
| + intptr_t max_checks = (op_kind == Token::kEQ)
|
| + ? FLAG_max_equality_polymorphic_checks
|
| + : FLAG_max_polymorphic_checks;
|
| + if ((unary_checks.NumberOfChecks() > max_checks) &&
|
| InstanceCallNeedsClassCheck(instr)) {
|
| // Too many checks, it will be megamorphic which needs unary checks.
|
| instr->set_ic_data(&unary_checks);
|
| @@ -3055,6 +3165,10 @@
|
| return;
|
| }
|
|
|
| + if (op_kind == Token::kEQ && TryReplaceWithEqualityOp(instr, op_kind)) {
|
| + return;
|
| + }
|
| +
|
| if (Token::IsRelationalOperator(op_kind) &&
|
| TryReplaceWithRelationalOp(instr, op_kind)) {
|
| return;
|
| @@ -3280,222 +3394,6 @@
|
| }
|
|
|
|
|
| -bool FlowGraphOptimizer::CanStrictifyEqualityCompare(
|
| - EqualityCompareInstr* compare) {
|
| - // If one of the inputs is null this is a strict comparison.
|
| - if (compare->left()->BindsToConstantNull() ||
|
| - compare->right()->BindsToConstantNull()) {
|
| - return true;
|
| - }
|
| -
|
| - if (compare->left()->Type()->IsNone()) {
|
| - return false; // We might be running prior to any type propagation passes.
|
| - }
|
| -
|
| - // Try resolving target function using propagated cid for the receiver.
|
| - // If receiver is either null or has default equality operator then
|
| - // we can convert such comparison to a strict one.
|
| - const intptr_t receiver_cid =
|
| - compare->left()->Type()->ToNullableCid();
|
| -
|
| - if (receiver_cid == kDynamicCid) {
|
| - return false;
|
| - }
|
| -
|
| - const Class& receiver_class = Class::Handle(
|
| - Isolate::Current()->class_table()->At(receiver_cid));
|
| -
|
| - // Resolve equality operator.
|
| - const intptr_t kNumArgs = 2;
|
| - ArgumentsDescriptor args_desc(
|
| - Array::Handle(ArgumentsDescriptor::New(kNumArgs)));
|
| - const Function& function = Function::Handle(
|
| - Resolver::ResolveDynamicForReceiverClass(
|
| - receiver_class,
|
| - Symbols::EqualOperator(),
|
| - args_desc));
|
| -
|
| - if (function.IsNull()) {
|
| - return false;
|
| - }
|
| -
|
| - // Default equality operator declared on the Object class just calls
|
| - // identical.
|
| - return (Class::Handle(function.Owner()).id() == kInstanceCid);
|
| -}
|
| -
|
| -
|
| -template <typename T>
|
| -bool FlowGraphOptimizer::StrictifyEqualityCompare(
|
| - EqualityCompareInstr* compare,
|
| - T current_instruction) const {
|
| - if (CanStrictifyEqualityCompare(compare)) {
|
| - Token::Kind strict_kind = (compare->kind() == Token::kEQ) ?
|
| - Token::kEQ_STRICT : Token::kNE_STRICT;
|
| - StrictCompareInstr* strict_comp =
|
| - new StrictCompareInstr(compare->token_pos(),
|
| - strict_kind,
|
| - compare->left()->CopyWithType(),
|
| - compare->right()->CopyWithType());
|
| - // Numbers override equality and are therefore not part of this conversion.
|
| - strict_comp->set_needs_number_check(false);
|
| - current_instruction->ReplaceWith(strict_comp, current_iterator());
|
| - return true;
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -
|
| -// Returns true if we converted EqualityCompare to StrictCompare.
|
| -template <typename T>
|
| -bool FlowGraphOptimizer::StrictifyEqualityCompareWithICData(
|
| - EqualityCompareInstr* compare,
|
| - const ICData& unary_ic_data,
|
| - T current_instruction) {
|
| - ASSERT(unary_ic_data.num_args_tested() == 1);
|
| - if (unary_ic_data.NumberOfChecks() <= FLAG_max_polymorphic_checks) {
|
| - // If possible classes do not override Object's equality then replace
|
| - // with strict equality.
|
| - Function& target = Function::Handle();
|
| - Class& targets_class = Class::Handle();
|
| - for (intptr_t i = 0; i < unary_ic_data.NumberOfChecks(); i++) {
|
| - intptr_t cid = kIllegalCid;
|
| - unary_ic_data.GetOneClassCheckAt(i, &cid, &target);
|
| - targets_class = target.Owner();
|
| - if (targets_class.id() != kInstanceCid) {
|
| - // Overriden equality operator.
|
| - return false;
|
| - }
|
| - }
|
| - AddCheckClass(compare->left()->definition(),
|
| - unary_ic_data,
|
| - compare->deopt_id(),
|
| - current_instruction->env(),
|
| - current_instruction);
|
| - ASSERT((compare->kind() == Token::kEQ) || (compare->kind() == Token::kNE));
|
| - Token::Kind strict_kind = (compare->kind() == Token::kEQ) ?
|
| - Token::kEQ_STRICT : Token::kNE_STRICT;
|
| - StrictCompareInstr* strict_comp =
|
| - new StrictCompareInstr(compare->token_pos(),
|
| - strict_kind,
|
| - compare->left()->Copy(),
|
| - compare->right()->Copy());
|
| - // Numbers override equality and are therefore not part of this conversion.
|
| - strict_comp->set_needs_number_check(false);
|
| - current_instruction->ReplaceWith(strict_comp, current_iterator());
|
| - return true;
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -
|
| -template <typename T>
|
| -void FlowGraphOptimizer::HandleEqualityCompare(EqualityCompareInstr* comp,
|
| - T current_instruction) {
|
| - if (StrictifyEqualityCompare(comp, current_instruction)) {
|
| - // Based on input types, equality converted to strict-equality.
|
| - return;
|
| - }
|
| -
|
| - if (!comp->HasICData() || (comp->ic_data()->NumberOfChecks() == 0)) {
|
| - return;
|
| - }
|
| -
|
| - const ICData& ic_data = *comp->ic_data();
|
| - ASSERT(ic_data.num_args_tested() == 2);
|
| - ASSERT(comp->operation_cid() == kIllegalCid);
|
| - if (HasOnlyTwoOf(ic_data, kSmiCid)) {
|
| - InsertBefore(current_instruction,
|
| - new CheckSmiInstr(comp->left()->Copy(), comp->deopt_id()),
|
| - current_instruction->env(),
|
| - Definition::kEffect);
|
| - InsertBefore(current_instruction,
|
| - new CheckSmiInstr(comp->right()->Copy(), comp->deopt_id()),
|
| - current_instruction->env(),
|
| - Definition::kEffect);
|
| - comp->set_operation_cid(kSmiCid);
|
| - } else if (HasTwoMintOrSmi(ic_data) &&
|
| - FlowGraphCompiler::SupportsUnboxedMints()) {
|
| - comp->set_operation_cid(kMintCid);
|
| - } else if (HasTwoDoubleOrSmi(ic_data)) {
|
| - // Use double comparison.
|
| - if (SmiFitsInDouble()) {
|
| - comp->set_operation_cid(kDoubleCid);
|
| - } else {
|
| - if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) {
|
| - // We cannot use double comparison on two smis.
|
| - ASSERT(comp->operation_cid() == kIllegalCid);
|
| - } else {
|
| - InsertBefore(current_instruction,
|
| - new CheckEitherNonSmiInstr(comp->left()->Copy(),
|
| - comp->right()->Copy(),
|
| - comp->deopt_id()),
|
| - current_instruction->env(),
|
| - Definition::kEffect);
|
| - comp->set_operation_cid(kDoubleCid);
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (comp->operation_cid() != kIllegalCid) {
|
| - // Done.
|
| - return;
|
| - }
|
| -
|
| - const ICData& unary_checks_0 =
|
| - ICData::ZoneHandle(comp->ic_data()->AsUnaryClassChecks());
|
| - if (StrictifyEqualityCompareWithICData(
|
| - comp, unary_checks_0, current_instruction)) {
|
| - // Based on ICData, equality converted to strict-equality.
|
| - return;
|
| - }
|
| -
|
| - // Check if ICDData contains checks with Smi/Null combinations. In that case
|
| - // we can still emit the optimized Smi equality operation but need to add
|
| - // checks for null or Smi.
|
| - // TODO(srdjan): Add it for Double and Mint.
|
| - GrowableArray<intptr_t> smi_or_null(2);
|
| - smi_or_null.Add(kSmiCid);
|
| - smi_or_null.Add(kNullCid);
|
| - if (ICDataHasOnlyReceiverArgumentClassIds(ic_data,
|
| - smi_or_null,
|
| - smi_or_null)) {
|
| - AddCheckClass(comp->left()->definition(),
|
| - unary_checks_0,
|
| - comp->deopt_id(),
|
| - current_instruction->env(),
|
| - current_instruction);
|
| -
|
| - const ICData& unary_checks_1 =
|
| - ICData::ZoneHandle(comp->ic_data()->AsUnaryClassChecksForArgNr(1));
|
| - AddCheckClass(comp->right()->definition(),
|
| - unary_checks_1,
|
| - comp->deopt_id(),
|
| - current_instruction->env(),
|
| - current_instruction);
|
| - comp->set_operation_cid(kSmiCid);
|
| - }
|
| -}
|
| -
|
| -
|
| -
|
| -
|
| -void FlowGraphOptimizer::VisitEqualityCompare(EqualityCompareInstr* instr) {
|
| - HandleEqualityCompare(instr, instr);
|
| -}
|
| -
|
| -
|
| -void FlowGraphOptimizer::VisitBranch(BranchInstr* instr) {
|
| - ComparisonInstr* comparison = instr->comparison();
|
| - if (comparison->IsEqualityCompare()) {
|
| - HandleEqualityCompare(comparison->AsEqualityCompare(), instr);
|
| - } else {
|
| - ASSERT(comparison->IsStrictCompare());
|
| - // Nothing to do.
|
| - }
|
| -}
|
| -
|
| -
|
| // Range analysis for smi values.
|
| class RangeAnalysis : public ValueObject {
|
| public:
|
| @@ -4601,17 +4499,6 @@
|
| static Place* Wrap(const Place& place);
|
|
|
| private:
|
| - static Definition* OriginalDefinition(Definition* defn) {
|
| - while (defn->IsRedefinition() || defn->IsAssertAssignable()) {
|
| - if (defn->IsRedefinition()) {
|
| - defn = defn->AsRedefinition()->value()->definition();
|
| - } else {
|
| - defn = defn->AsAssertAssignable()->value()->definition();
|
| - }
|
| - }
|
| - return defn;
|
| - }
|
| -
|
| bool SameField(Place* other) const {
|
| return (kind_ == kField) ? (field().raw() == other->field().raw())
|
| : (offset_in_bytes_ == other->offset_in_bytes_);
|
| @@ -6454,10 +6341,8 @@
|
| const Object& right = instr->right()->definition()->constant_value();
|
|
|
| if (instr->left()->definition() == instr->right()->definition()) {
|
| - // Fold x == x, and x != x to true/false for numbers and checked strict
|
| - // comparisons.
|
| - if (instr->IsCheckedStrictEqual() ||
|
| - RawObject::IsIntegerClassId(instr->operation_cid())) {
|
| + // Fold x == x, and x != x to true/false for numbers comparisons.
|
| + if (RawObject::IsIntegerClassId(instr->operation_cid())) {
|
| return SetValue(instr, Bool::Get(instr->kind() == Token::kEQ));
|
| }
|
| }
|
| @@ -7428,9 +7313,8 @@
|
| comparison->kind(),
|
| left,
|
| right,
|
| - Object::null_array());
|
| - new_equality_compare->set_ic_data(equality_compare->ic_data());
|
| - new_equality_compare->set_operation_cid(equality_compare->operation_cid());
|
| + equality_compare->operation_cid(),
|
| + equality_compare->deopt_id());
|
| new_comparison = new_equality_compare;
|
| } else {
|
| ASSERT(comparison->IsRelationalOp());
|
|
|