diff --git a/runtime/lib/function.cc b/runtime/lib/function.cc index 8c418921696..82e6df12343 100644 --- a/runtime/lib/function.cc +++ b/runtime/lib/function.cc @@ -33,6 +33,7 @@ DEFINE_NATIVE_ENTRY(FunctionImpl_equals, 2) { isolate, arguments->NativeArgAt(0)); ASSERT(receiver.IsClosure()); GET_NATIVE_ARGUMENT(Instance, other, arguments->NativeArgAt(1)); + ASSERT(!other.IsNull()); if (receiver.raw() == other.raw()) return Bool::True().raw(); if (other.IsClosure()) { const Function& func_a = Function::Handle(Closure::function(receiver)); diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc index 66930edc04f..c085f5cf405 100644 --- a/runtime/vm/debugger.cc +++ b/runtime/vm/debugger.cc @@ -1595,6 +1595,9 @@ void Debugger::SingleStepCallback() { if (!IsDebuggable(func)) { return; } + if (frame->TokenPos() == Scanner::kDummyTokenIndex) { + return; + } if (FLAG_verbose_debug) { OS::Print(">>> single step break at %s:%" Pd " (func %s token %" Pd ")\n", diff --git a/runtime/vm/intermediate_language_arm.cc b/runtime/vm/intermediate_language_arm.cc index 00df26bf9fa..95e4732397f 100644 --- a/runtime/vm/intermediate_language_arm.cc +++ b/runtime/vm/intermediate_language_arm.cc @@ -361,14 +361,6 @@ static void EmitEqualityAsInstanceCall(FlowGraphCompiler* compiler, const Array& kNoArgumentNames = Object::null_array(); const int kNumArgumentsChecked = 2; - Label check_identity; - __ LoadImmediate(IP, reinterpret_cast(Object::null())); - __ ldm(IA, SP, (1 << R0) | (1 << R1)); - __ cmp(R1, ShifterOperand(IP)); - __ b(&check_identity, EQ); - __ cmp(R0, ShifterOperand(IP)); - __ b(&check_identity, EQ); - ICData& equality_ic_data = ICData::ZoneHandle(); if (compiler->is_optimizing() && FLAG_propagate_ic_data) { ASSERT(!original_ic_data.IsNull()); @@ -395,42 +387,12 @@ static void EmitEqualityAsInstanceCall(FlowGraphCompiler* compiler, kNoArgumentNames, locs, equality_ic_data); - Label check_ne; - __ b(&check_ne); - - __ Bind(&check_identity); - Label equality_done; - if (compiler->is_optimizing()) { - // No need to update IC data. - __ PopList((1 << R0) | (1 << R1)); - __ cmp(R0, ShifterOperand(R1)); - __ LoadObject(R0, Bool::Get(kind != Token::kEQ), NE); - __ LoadObject(R0, Bool::Get(kind == Token::kEQ), EQ); - if (kind == Token::kNE) { - // Skip not-equal result conversion. - __ b(&equality_done); - } - } else { - // Call stub, load IC data in register. The stub will update ICData if - // necessary. - Register ic_data_reg = locs->temp(0).reg(); - ASSERT(ic_data_reg == R5); // Stub depends on it. - __ LoadObject(ic_data_reg, equality_ic_data); - // Pass left in R1 and right in R0. - compiler->GenerateCall(token_pos, - &StubCode::EqualityWithNullArgLabel(), - PcDescriptors::kRuntimeCall, - locs); - __ Drop(2); - } - __ Bind(&check_ne); if (kind == Token::kNE) { // Negate the condition: true label returns false and vice versa. __ CompareObject(R0, Bool::True()); __ LoadObject(R0, Bool::True(), NE); __ LoadObject(R0, Bool::False(), EQ); } - __ Bind(&equality_done); } @@ -612,35 +574,11 @@ static void EmitGenericEqualityCompare(FlowGraphCompiler* compiler, ASSERT(!ic_data.IsNull() && (ic_data.NumberOfChecks() > 0)); Register left = locs->in(0).reg(); Register right = locs->in(1).reg(); - Label done, identity_compare, non_null_compare; - __ LoadImmediate(IP, reinterpret_cast(Object::null())); - __ cmp(right, ShifterOperand(IP)); - __ b(&identity_compare, EQ); - __ cmp(left, ShifterOperand(IP)); - __ b(&non_null_compare, NE); - // Comparison with NULL is "===". - __ Bind(&identity_compare); - __ cmp(left, ShifterOperand(right)); - Condition cond = TokenKindToSmiCondition(kind); - if (branch != NULL) { - branch->EmitBranchOnCondition(compiler, cond); - } else { - Register result = locs->out().reg(); - Label load_true; - __ b(&load_true, cond); - __ LoadObject(result, Bool::False()); - __ b(&done); - __ Bind(&load_true); - __ LoadObject(result, Bool::True()); - } - __ b(&done); - __ Bind(&non_null_compare); // Receiver is not null. ASSERT(left == R1); ASSERT(right == R0); __ PushList((1 << R0) | (1 << R1)); EmitEqualityAsPolymorphicCall(compiler, ic_data, locs, branch, kind, deopt_id, token_pos); - __ Bind(&done); } diff --git a/runtime/vm/intermediate_language_ia32.cc b/runtime/vm/intermediate_language_ia32.cc index 279051c5086..e7c587ebea2 100644 --- a/runtime/vm/intermediate_language_ia32.cc +++ b/runtime/vm/intermediate_language_ia32.cc @@ -310,14 +310,6 @@ static void EmitEqualityAsInstanceCall(FlowGraphCompiler* compiler, const Array& kNoArgumentNames = Object::null_array(); const int kNumArgumentsChecked = 2; - const Immediate& raw_null = - Immediate(reinterpret_cast(Object::null())); - Label check_identity; - __ cmpl(Address(ESP, 0 * kWordSize), raw_null); - __ j(EQUAL, &check_identity); - __ cmpl(Address(ESP, 1 * kWordSize), raw_null); - __ j(EQUAL, &check_identity); - ICData& equality_ic_data = ICData::ZoneHandle(); if (compiler->is_optimizing() && FLAG_propagate_ic_data) { ASSERT(!original_ic_data.IsNull()); @@ -344,39 +336,6 @@ static void EmitEqualityAsInstanceCall(FlowGraphCompiler* compiler, kNoArgumentNames, locs, equality_ic_data); - Label check_ne; - __ jmp(&check_ne); - - __ Bind(&check_identity); - Label equality_done; - if (compiler->is_optimizing()) { - // No need to update IC data. - Label is_true; - __ popl(EAX); - __ popl(EDX); - __ cmpl(EAX, EDX); - __ j(EQUAL, &is_true); - __ LoadObject(EAX, Bool::Get(kind != Token::kEQ)); - __ jmp(&equality_done); - __ Bind(&is_true); - __ LoadObject(EAX, Bool::Get(kind == Token::kEQ)); - if (kind == Token::kNE) { - // Skip not-equal result conversion. - __ jmp(&equality_done); - } - } else { - // Call stub, load IC data in register. The stub will update ICData if - // necessary. - Register ic_data_reg = locs->temp(0).reg(); - ASSERT(ic_data_reg == ECX); // Stub depends on it. - __ LoadObject(ic_data_reg, equality_ic_data); - compiler->GenerateCall(token_pos, - &StubCode::EqualityWithNullArgLabel(), - PcDescriptors::kRuntimeCall, - locs); - __ Drop(2); - } - __ Bind(&check_ne); if (kind == Token::kNE) { Label true_label, done; // Negate the condition: true label returns false and vice versa. @@ -388,7 +347,6 @@ static void EmitEqualityAsInstanceCall(FlowGraphCompiler* compiler, __ LoadObject(EAX, Bool::False()); __ Bind(&done); } - __ Bind(&equality_done); } @@ -562,35 +520,10 @@ static void EmitGenericEqualityCompare(FlowGraphCompiler* compiler, ASSERT(!ic_data.IsNull() && (ic_data.NumberOfChecks() > 0)); Register left = locs->in(0).reg(); Register right = locs->in(1).reg(); - const Immediate& raw_null = - Immediate(reinterpret_cast(Object::null())); - Label done, identity_compare, non_null_compare; - __ cmpl(right, raw_null); - __ j(EQUAL, &identity_compare, Assembler::kNearJump); - __ cmpl(left, raw_null); - __ j(NOT_EQUAL, &non_null_compare, Assembler::kNearJump); - // Comparison with NULL is "===". - __ Bind(&identity_compare); - __ cmpl(left, right); - Condition cond = TokenKindToSmiCondition(kind); - if (branch != NULL) { - branch->EmitBranchOnCondition(compiler, cond); - } else { - Register result = locs->out().reg(); - Label load_true; - __ j(cond, &load_true, Assembler::kNearJump); - __ LoadObject(result, Bool::False()); - __ jmp(&done); - __ Bind(&load_true); - __ LoadObject(result, Bool::True()); - } - __ jmp(&done); - __ Bind(&non_null_compare); // Receiver is not null. __ pushl(left); __ pushl(right); EmitEqualityAsPolymorphicCall(compiler, ic_data, locs, branch, kind, deopt_id, token_pos); - __ Bind(&done); } diff --git a/runtime/vm/intermediate_language_mips.cc b/runtime/vm/intermediate_language_mips.cc index a1d5b188cc9..9c72ba1877f 100644 --- a/runtime/vm/intermediate_language_mips.cc +++ b/runtime/vm/intermediate_language_mips.cc @@ -356,12 +356,6 @@ static void EmitEqualityAsInstanceCall(FlowGraphCompiler* compiler, __ TraceSimMsg("EmitEqualityAsInstanceCall"); __ Comment("EmitEqualityAsInstanceCall"); - Label check_identity; - __ lw(A1, Address(SP, 1 * kWordSize)); - __ lw(A0, Address(SP, 0 * kWordSize)); - __ LoadImmediate(CMPRES1, reinterpret_cast(Object::null())); - __ beq(A1, CMPRES1, &check_identity); - __ beq(A0, CMPRES1, &check_identity); ICData& equality_ic_data = ICData::ZoneHandle(); if (compiler->is_optimizing() && FLAG_propagate_ic_data) { @@ -389,40 +383,6 @@ static void EmitEqualityAsInstanceCall(FlowGraphCompiler* compiler, kNoArgumentNames, locs, equality_ic_data); - Label check_ne; - __ b(&check_ne); - - __ Bind(&check_identity); - Label equality_done; - if (compiler->is_optimizing()) { - // No need to update IC data. - Label is_true; - __ lw(A1, Address(SP, 1 * kWordSize)); - __ lw(A0, Address(SP, 0 * kWordSize)); - __ addiu(SP, SP, Immediate(2 * kWordSize)); - __ beq(A1, A0, &is_true); - __ LoadObject(V0, Bool::Get(kind != Token::kEQ)); - __ b(&equality_done); - __ Bind(&is_true); - __ LoadObject(V0, Bool::Get(kind == Token::kEQ)); - if (kind == Token::kNE) { - // Skip not-equal result conversion. - __ b(&equality_done); - } - } else { - // Call stub, load IC data in register. The stub will update ICData if - // necessary. - Register ic_data_reg = locs->temp(0).reg(); - ASSERT(ic_data_reg == T0); // Stub depends on it. - __ LoadObject(ic_data_reg, equality_ic_data); - // Pass left in A1 and right in A0. - compiler->GenerateCall(token_pos, - &StubCode::EqualityWithNullArgLabel(), - PcDescriptors::kRuntimeCall, - locs); - __ Drop(2); - } - __ Bind(&check_ne); if (kind == Token::kNE) { Label true_label, done; // Negate the condition: true label returns false and vice versa. @@ -433,7 +393,6 @@ static void EmitEqualityAsInstanceCall(FlowGraphCompiler* compiler, __ LoadObject(V0, Bool::False()); __ Bind(&done); } - __ Bind(&equality_done); } @@ -649,31 +608,8 @@ static void EmitGenericEqualityCompare(FlowGraphCompiler* compiler, ASSERT(!ic_data.IsNull() && (ic_data.NumberOfChecks() > 0)); Register left = locs->in(0).reg(); Register right = locs->in(1).reg(); - Label done, identity_compare, non_null_compare; __ TraceSimMsg("EmitGenericEqualityCompare"); __ Comment("EmitGenericEqualityCompare"); - __ LoadImmediate(CMPRES1, reinterpret_cast(Object::null())); - __ beq(right, CMPRES1, &identity_compare); - __ bne(left, CMPRES1, &non_null_compare); - - // Comparison with NULL is "===". - __ Bind(&identity_compare); - Condition cond = TokenKindToSmiCondition(kind); - __ slt(CMPRES1, left, right); - __ slt(CMPRES2, right, left); - if (branch != NULL) { - branch->EmitBranchOnCondition(compiler, cond); - } else { - Register result = locs->out().reg(); - Label load_true; - EmitBranchAfterCompare(compiler, cond, &load_true); - __ LoadObject(result, Bool::False()); - __ b(&done); - __ Bind(&load_true); - __ LoadObject(result, Bool::True()); - } - __ b(&done); - __ Bind(&non_null_compare); // Receiver is not null. ASSERT(left == A1); ASSERT(right == A0); __ addiu(SP, SP, Immediate(-2 * kWordSize)); @@ -681,7 +617,6 @@ static void EmitGenericEqualityCompare(FlowGraphCompiler* compiler, __ sw(A0, Address(SP, 0 * kWordSize)); EmitEqualityAsPolymorphicCall(compiler, ic_data, locs, branch, kind, deopt_id, token_pos); - __ Bind(&done); } diff --git a/runtime/vm/intermediate_language_x64.cc b/runtime/vm/intermediate_language_x64.cc index 9349fd331ae..4d2ee9df452 100644 --- a/runtime/vm/intermediate_language_x64.cc +++ b/runtime/vm/intermediate_language_x64.cc @@ -453,13 +453,6 @@ static void EmitEqualityAsInstanceCall(FlowGraphCompiler* compiler, const Array& kNoArgumentNames = Object::null_array(); const int kNumArgumentsChecked = 2; - Label check_identity; - __ LoadObject(TMP, Object::null_object(), PP); - __ cmpq(Address(RSP, 0 * kWordSize), TMP); - __ j(EQUAL, &check_identity); - __ cmpq(Address(RSP, 1 * kWordSize), TMP); - __ j(EQUAL, &check_identity); - ICData& equality_ic_data = ICData::ZoneHandle(original_ic_data.raw()); if (compiler->is_optimizing() && FLAG_propagate_ic_data) { ASSERT(!original_ic_data.IsNull()); @@ -486,39 +479,6 @@ static void EmitEqualityAsInstanceCall(FlowGraphCompiler* compiler, kNoArgumentNames, locs, equality_ic_data); - Label check_ne; - __ jmp(&check_ne); - - __ Bind(&check_identity); - Label equality_done; - if (compiler->is_optimizing()) { - // No need to update IC data. - Label is_true; - __ popq(RAX); - __ popq(RDX); - __ cmpq(RAX, RDX); - __ j(EQUAL, &is_true); - __ LoadObject(RAX, Bool::Get(kind != Token::kEQ), PP); - __ jmp(&equality_done); - __ Bind(&is_true); - __ LoadObject(RAX, Bool::Get(kind == Token::kEQ), PP); - if (kind == Token::kNE) { - // Skip not-equal result conversion. - __ jmp(&equality_done); - } - } else { - // Call stub, load IC data in register. The stub will update ICData if - // necessary. - Register ic_data_reg = locs->temp(0).reg(); - ASSERT(ic_data_reg == RBX); // Stub depends on it. - __ LoadObject(ic_data_reg, equality_ic_data, PP); - compiler->GenerateCall(token_pos, - &StubCode::EqualityWithNullArgLabel(), - PcDescriptors::kRuntimeCall, - locs); - __ Drop(2); - } - __ Bind(&check_ne); if (kind == Token::kNE) { Label true_label, done; // Negate the condition: true label returns false and vice versa. @@ -530,7 +490,6 @@ static void EmitEqualityAsInstanceCall(FlowGraphCompiler* compiler, __ LoadObject(RAX, Bool::False(), PP); __ Bind(&done); } - __ Bind(&equality_done); } @@ -704,34 +663,10 @@ static void EmitGenericEqualityCompare(FlowGraphCompiler* compiler, ASSERT(!ic_data.IsNull() && (ic_data.NumberOfChecks() > 0)); Register left = locs->in(0).reg(); Register right = locs->in(1).reg(); - - Label done, identity_compare, non_null_compare; - __ CompareObject(right, Object::null_object(), PP); - __ j(EQUAL, &identity_compare, Assembler::kNearJump); - __ CompareObject(left, Object::null_object(), PP); - __ j(NOT_EQUAL, &non_null_compare, Assembler::kNearJump); - // Comparison with NULL is "===". - __ Bind(&identity_compare); - __ cmpq(left, right); - Condition cond = TokenKindToSmiCondition(kind); - if (branch != NULL) { - branch->EmitBranchOnCondition(compiler, cond); - } else { - Register result = locs->out().reg(); - Label load_true; - __ j(cond, &load_true, Assembler::kNearJump); - __ LoadObject(result, Bool::False(), PP); - __ jmp(&done); - __ Bind(&load_true); - __ LoadObject(result, Bool::True(), PP); - } - __ jmp(&done); - __ Bind(&non_null_compare); // Receiver is not null. __ pushq(left); __ pushq(right); EmitEqualityAsPolymorphicCall(compiler, ic_data, locs, branch, kind, deopt_id, token_pos); - __ Bind(&done); } diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc index a61685afd7c..192940c7194 100644 --- a/runtime/vm/parser.cc +++ b/runtime/vm/parser.cc @@ -1923,55 +1923,7 @@ AstNode* Parser::ParseSuperOperator() { op_arguments = BuildNoSuchMethodArguments( operator_pos, operator_function_name, *op_arguments); } - if (super_operator.name() == Symbols::EqualOperator().raw()) { - // Expand super.== call to match correct == semantics into: - // Let t1 = left, t2 = right { - // (t1 === null || t2 === null) ? t1 === t2 - // : static_call(super.==, t1, t2) - // } - // Normal == calls are not expanded at the AST level to produce - // more compact code and enable more optimization opportunities. - ASSERT(!is_no_such_method); // == is always found. - EnsureExpressionTemp(); // Needed for ConditionalExprNode. - LetNode* result = new LetNode(operator_pos); - AstNode* left = - new LoadLocalNode(operator_pos, - result->AddInitializer(op_arguments->NodeAt(0))); - AstNode* right = - new LoadLocalNode(operator_pos, - result->AddInitializer(op_arguments->NodeAt(1))); - LiteralNode* null_operand = - new LiteralNode(operator_pos, Instance::ZoneHandle()); - ComparisonNode* is_left_null = new ComparisonNode(operator_pos, - Token::kEQ_STRICT, - left, - null_operand); - ComparisonNode* is_right_null = new ComparisonNode(operator_pos, - Token::kEQ_STRICT, - right, - null_operand); - BinaryOpNode* null_check = new BinaryOpNode(operator_pos, - Token::kOR, - is_left_null, - is_right_null); - ArgumentListNode* new_arguments = new ArgumentListNode(operator_pos); - new_arguments->Add(left); - new_arguments->Add(right); - StaticCallNode* call = new StaticCallNode(operator_pos, - super_operator, - new_arguments); - ComparisonNode* strict_eq = new ComparisonNode(operator_pos, - Token::kEQ_STRICT, - left, - right); - result->AddNode(new ConditionalExprNode(operator_pos, - null_check, - strict_eq, - call)); - super_op = result; - } else { - super_op = new StaticCallNode(operator_pos, super_operator, op_arguments); - } + super_op = new StaticCallNode(operator_pos, super_operator, op_arguments); if (negate_result) { super_op = new UnaryOpNode(operator_pos, Token::kNOT, super_op); } @@ -2879,17 +2831,35 @@ SequenceNode* Parser::ParseFunc(const Function& func, intptr_t end_token_pos = 0; if (CurrentToken() == Token::kLBRACE) { ConsumeToken(); + if (String::Handle(func.name()).Equals(Symbols::EqualOperator())) { + const Class& owner = Class::Handle(func.Owner()); + if (!owner.IsObjectClass()) { + AddEqualityNullCheck(); + } + } ParseStatementSequence(); end_token_pos = TokenPos(); ExpectToken(Token::kRBRACE); } else if (CurrentToken() == Token::kARROW) { ConsumeToken(); + if (String::Handle(func.name()).Equals(Symbols::EqualOperator())) { + const Class& owner = Class::Handle(func.Owner()); + if (!owner.IsObjectClass()) { + AddEqualityNullCheck(); + } + } const intptr_t expr_pos = TokenPos(); AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades); ASSERT(expr != NULL); current_block_->statements->Add(new ReturnNode(expr_pos, expr)); end_token_pos = TokenPos(); } else if (IsLiteral("native")) { + if (String::Handle(func.name()).Equals(Symbols::EqualOperator())) { + const Class& owner = Class::Handle(func.Owner()); + if (!owner.IsObjectClass()) { + AddEqualityNullCheck(); + } + } ParseNativeFunctionBlock(¶ms, func); end_token_pos = TokenPos(); ExpectSemicolon(); @@ -2924,6 +2894,31 @@ SequenceNode* Parser::ParseFunc(const Function& func, } +void Parser::AddEqualityNullCheck() { + const intptr_t token_pos = Scanner::kDummyTokenIndex; + AstNode* argument = + new LoadLocalNode(token_pos, + current_block_->scope->parent()->VariableAt(1)); + LiteralNode* null_operand = + new LiteralNode(token_pos, Instance::ZoneHandle()); + ComparisonNode* check_arg = new ComparisonNode(token_pos, + Token::kEQ_STRICT, + argument, + null_operand); + ComparisonNode* result = new ComparisonNode(token_pos, + Token::kEQ_STRICT, + LoadReceiver(token_pos), + null_operand); + SequenceNode* arg_is_null = new SequenceNode(token_pos, NULL); + arg_is_null->Add(new ReturnNode(token_pos, result)); + IfNode* if_arg_null = new IfNode(token_pos, + check_arg, + arg_is_null, + NULL); + current_block_->statements->Add(if_arg_null); +} + + void Parser::SkipIf(Token::Kind token) { if (CurrentToken() == token) { ConsumeToken(); diff --git a/runtime/vm/parser.h b/runtime/vm/parser.h index 4abfda0b062..16b9322f55d 100644 --- a/runtime/vm/parser.h +++ b/runtime/vm/parser.h @@ -662,6 +662,8 @@ class Parser : public ValueObject { const Function& constructor, ArgumentListNode* arguments); + void AddEqualityNullCheck(); + RawInstance* TryCanonicalize(const Instance& instance, intptr_t token_pos); Isolate* isolate() const { return isolate_; } diff --git a/runtime/vm/stub_code.h b/runtime/vm/stub_code.h index 3e185ee0973..a8351cd0c8a 100644 --- a/runtime/vm/stub_code.h +++ b/runtime/vm/stub_code.h @@ -67,7 +67,6 @@ class RawCode; V(TwoArgsUnoptimizedStaticCall) \ V(OptimizeFunction) \ V(BreakpointDynamic) \ - V(EqualityWithNullArg) \ // class StubEntry is used to describe stub methods generated in dart to // abstract out common code executed from generated dart code. diff --git a/runtime/vm/stub_code_arm.cc b/runtime/vm/stub_code_arm.cc index df7487ccfc4..0c35d5bef5b 100644 --- a/runtime/vm/stub_code_arm.cc +++ b/runtime/vm/stub_code_arm.cc @@ -1966,92 +1966,6 @@ void StubCode::GenerateJumpToExceptionHandlerStub(Assembler* assembler) { } -// Implements equality operator when one of the arguments is null -// (identity check) and updates ICData if necessary. -// LR: return address. -// R1: left argument. -// R0: right argument. -// R5: ICData. -// R0: result. -// TODO(srdjan): Move to VM stubs once Boolean objects become VM objects. -void StubCode::GenerateEqualityWithNullArgStub(Assembler* assembler) { - __ EnterStubFrame(); - static const intptr_t kNumArgsTested = 2; -#if defined(DEBUG) - { Label ok; - __ ldr(IP, FieldAddress(R5, ICData::num_args_tested_offset())); - __ cmp(IP, ShifterOperand(kNumArgsTested)); - __ b(&ok, EQ); - __ Stop("Incorrect ICData for equality"); - __ Bind(&ok); - } -#endif // DEBUG - // Check IC data, update if needed. - // R5: IC data object (preserved). - __ ldr(R6, FieldAddress(R5, ICData::ic_data_offset())); - // R6: ic_data_array with check entries: classes and target functions. - __ AddImmediate(R6, Array::data_offset() - kHeapObjectTag); - // R6: points directly to the first ic data array element. - - Label get_class_id_as_smi, no_match, loop, found; - __ Bind(&loop); - // Check left. - __ mov(R2, ShifterOperand(R1)); - __ bl(&get_class_id_as_smi); - __ ldr(R3, Address(R6, 0 * kWordSize)); - __ cmp(R2, ShifterOperand(R3)); // Class id match? - __ b(&no_match, NE); - // Check right. - __ mov(R2, ShifterOperand(R0)); - __ bl(&get_class_id_as_smi); - __ ldr(R3, Address(R6, 1 * kWordSize)); - __ cmp(R2, ShifterOperand(R3)); // Class id match? - __ b(&found, EQ); - __ Bind(&no_match); - // Next check group. - __ AddImmediate(R6, kWordSize * ICData::TestEntryLengthFor(kNumArgsTested)); - __ CompareImmediate(R3, Smi::RawValue(kIllegalCid)); // Done? - __ b(&loop, NE); - Label update_ic_data; - __ b(&update_ic_data); - - __ Bind(&found); - const intptr_t count_offset = - ICData::CountIndexFor(kNumArgsTested) * kWordSize; - __ ldr(IP, Address(R6, count_offset)); - __ adds(IP, IP, ShifterOperand(Smi::RawValue(1))); - __ LoadImmediate(IP, Smi::RawValue(Smi::kMaxValue), VS); // If overflow. - __ str(IP, Address(R6, count_offset)); - - Label compute_result; - __ Bind(&compute_result); - __ cmp(R0, ShifterOperand(R1)); - __ LoadObject(R0, Bool::False(), NE); - __ LoadObject(R0, Bool::True(), EQ); - __ LeaveStubFrame(); - __ Ret(); - - __ Bind(&get_class_id_as_smi); - // Test if Smi -> load Smi class for comparison. - __ tst(R2, ShifterOperand(kSmiTagMask)); - __ mov(R2, ShifterOperand(Smi::RawValue(kSmiCid)), EQ); - __ bx(LR, EQ); - __ LoadClassId(R2, R2); - __ SmiTag(R2); - __ bx(LR); - - __ Bind(&update_ic_data); - // R5: ICData - __ PushList((1 << R0) | (1 << R1)); - __ PushObject(Symbols::EqualOperator()); // Target's name. - __ Push(R5); // ICData - __ CallRuntime(kUpdateICDataTwoArgsRuntimeEntry, 4); // Clobbers R4, R5. - __ Drop(2); - __ PopList((1 << R0) | (1 << R1)); - __ b(&compute_result); -} - - // Calls to the runtime to optimize the given function. // R6: function to be reoptimized. // R4: argument descriptor (preserved). diff --git a/runtime/vm/stub_code_ia32.cc b/runtime/vm/stub_code_ia32.cc index 16093d67736..4df92d7600f 100644 --- a/runtime/vm/stub_code_ia32.cc +++ b/runtime/vm/stub_code_ia32.cc @@ -2032,105 +2032,6 @@ void StubCode::GenerateJumpToExceptionHandlerStub(Assembler* assembler) { } -// Implements equality operator when one of the arguments is null -// (identity check) and updates ICData if necessary. -// TOS + 0: return address -// TOS + 1: right argument -// TOS + 2: left argument -// ECX: ICData. -// EAX: result. -// TODO(srdjan): Move to VM stubs once Boolean objects become VM objects. -void StubCode::GenerateEqualityWithNullArgStub(Assembler* assembler) { - static const intptr_t kNumArgsTested = 2; -#if defined(DEBUG) - { Label ok; - __ movl(EAX, FieldAddress(ECX, ICData::num_args_tested_offset())); - __ cmpl(EAX, Immediate(kNumArgsTested)); - __ j(EQUAL, &ok, Assembler::kNearJump); - __ Stop("Incorrect ICData for equality"); - __ Bind(&ok); - } -#endif // DEBUG - // Check IC data, update if needed. - // ECX: IC data object (preserved). - __ movl(EBX, FieldAddress(ECX, ICData::ic_data_offset())); - // EBX: ic_data_array with check entries: classes and target functions. - __ leal(EBX, FieldAddress(EBX, Array::data_offset())); - // EBX: points directly to the first ic data array element. - - Label get_class_id_as_smi, no_match, loop, compute_result, found; - __ Bind(&loop); - // Check left. - __ movl(EAX, Address(ESP, 2 * kWordSize)); - __ call(&get_class_id_as_smi); - __ movl(EDI, Address(EBX, 0 * kWordSize)); - __ cmpl(EAX, EDI); // Class id match? - __ j(NOT_EQUAL, &no_match, Assembler::kNearJump); - // Check right. - __ movl(EAX, Address(ESP, 1 * kWordSize)); - __ call(&get_class_id_as_smi); - __ movl(EDI, Address(EBX, 1 * kWordSize)); - __ cmpl(EAX, EDI); // Class id match? - __ j(EQUAL, &found, Assembler::kNearJump); - __ Bind(&no_match); - // Next check group. - __ addl(EBX, Immediate( - kWordSize * ICData::TestEntryLengthFor(kNumArgsTested))); - __ cmpl(EDI, Immediate(Smi::RawValue(kIllegalCid))); // Done? - __ j(NOT_EQUAL, &loop, Assembler::kNearJump); - Label update_ic_data; - __ jmp(&update_ic_data); - - __ Bind(&found); - const intptr_t count_offset = - ICData::CountIndexFor(kNumArgsTested) * kWordSize; - __ addl(Address(EBX, count_offset), Immediate(Smi::RawValue(1))); - __ j(NO_OVERFLOW, &compute_result); - __ movl(Address(EBX, count_offset), - Immediate(Smi::RawValue(Smi::kMaxValue))); - - __ Bind(&compute_result); - Label true_label; - __ movl(EAX, Address(ESP, 1 * kWordSize)); - __ cmpl(EAX, Address(ESP, 2 * kWordSize)); - __ j(EQUAL, &true_label, Assembler::kNearJump); - __ LoadObject(EAX, Bool::False()); - __ ret(); - __ Bind(&true_label); - __ LoadObject(EAX, Bool::True()); - __ ret(); - - __ Bind(&get_class_id_as_smi); - Label not_smi; - // Test if Smi -> load Smi class for comparison. - __ testl(EAX, Immediate(kSmiTagMask)); - __ j(NOT_ZERO, ¬_smi, Assembler::kNearJump); - __ movl(EAX, Immediate(Smi::RawValue(kSmiCid))); - __ ret(); - - __ Bind(¬_smi); - __ LoadClassId(EAX, EAX); - __ SmiTag(EAX); - __ ret(); - - __ Bind(&update_ic_data); - - // ECX: ICData - __ movl(EAX, Address(ESP, 1 * kWordSize)); - __ movl(EDI, Address(ESP, 2 * kWordSize)); - __ EnterStubFrame(); - __ pushl(EDI); // arg 0 - __ pushl(EAX); // arg 1 - __ PushObject(Symbols::EqualOperator()); // Target's name. - __ pushl(ECX); // ICData - __ CallRuntime(kUpdateICDataTwoArgsRuntimeEntry, 4); - __ Drop(4); - __ LeaveFrame(); - - __ jmp(&compute_result, Assembler::kNearJump); -} - - // Calls to the runtime to optimize the given function. // EDI: function to be reoptimized. // EDX: argument descriptor (preserved). diff --git a/runtime/vm/stub_code_mips.cc b/runtime/vm/stub_code_mips.cc index 8d1334b8004..0928f0a6921 100644 --- a/runtime/vm/stub_code_mips.cc +++ b/runtime/vm/stub_code_mips.cc @@ -2232,110 +2232,6 @@ void StubCode::GenerateJumpToExceptionHandlerStub(Assembler* assembler) { } -// Implements equality operator when one of the arguments is null -// (identity check) and updates ICData if necessary. -// RA: return address. -// A1: left argument. -// A0: right argument. -// T0: ICData. -// V0: result. -// TODO(srdjan): Move to VM stubs once Boolean objects become VM objects. -void StubCode::GenerateEqualityWithNullArgStub(Assembler* assembler) { - __ TraceSimMsg("EqualityWithNullArgStub"); - __ Comment("EqualityWithNullArgStub"); - __ EnterStubFrame(); - static const intptr_t kNumArgsTested = 2; -#if defined(DEBUG) - { Label ok; - __ lw(CMPRES1, FieldAddress(T0, ICData::num_args_tested_offset())); - __ BranchEqual(CMPRES1, kNumArgsTested, &ok); - __ Stop("Incorrect ICData for equality"); - __ Bind(&ok); - } -#endif // DEBUG - // Check IC data, update if needed. - // T0: IC data object (preserved). - __ lw(T6, FieldAddress(T0, ICData::ic_data_offset())); - // T6: ic_data_array with check entries: classes and target functions. - __ AddImmediate(T6, Array::data_offset() - kHeapObjectTag); - // T6: points directly to the first ic data array element. - - Label get_class_id_as_smi, no_match, loop, found; - __ Bind(&loop); - // Check left. - __ bal(&get_class_id_as_smi); - __ delay_slot()->mov(T2, A1); - __ lw(T3, Address(T6, 0 * kWordSize)); - __ bne(T2, T3, &no_match); // Class id match? - - // Check right. - __ bal(&get_class_id_as_smi); - __ delay_slot()->mov(T2, A0); - __ lw(T3, Address(T6, 1 * kWordSize)); - __ beq(T2, T3, &found); // Class id match? - __ Bind(&no_match); - // Next check group. - intptr_t entry_bytes = kWordSize * ICData::TestEntryLengthFor(kNumArgsTested); - if (Utils::IsInt(kImmBits, entry_bytes)) { - __ BranchNotEqual(T3, Smi::RawValue(kIllegalCid), &loop); // Done? - __ delay_slot()->addiu(T6, T6, Immediate(entry_bytes)); - } else { - __ AddImmediate(T6, entry_bytes); - __ BranchNotEqual(T3, Smi::RawValue(kIllegalCid), &loop); // Done? - } - - Label update_ic_data; - __ b(&update_ic_data); - - __ Bind(&found); - const intptr_t count_offset = - ICData::CountIndexFor(kNumArgsTested) * kWordSize; - Label no_overflow; - __ lw(T1, Address(T6, count_offset)); - __ AddImmediateDetectOverflow(T1, T1, Smi::RawValue(1), CMPRES, T5); - __ bgez(CMPRES, &no_overflow); - __ delay_slot()->sw(T1, Address(T6, count_offset)); - __ LoadImmediate(TMP1, Smi::RawValue(Smi::kMaxValue)); - __ sw(TMP1, Address(T6, count_offset)); // If overflow. - __ Bind(&no_overflow); - - Label compute_result; - __ Bind(&compute_result); - __ LoadObject(T4, Bool::True()); - __ LoadObject(T5, Bool::False()); - __ subu(CMPRES, A0, A1); - __ movz(V0, T4, CMPRES); - __ movn(V0, T5, CMPRES); - __ LeaveStubFrameAndReturn(); - - __ Bind(&get_class_id_as_smi); - // Test if Smi -> load Smi class for comparison. - Label not_smi; - __ andi(CMPRES, T2, Immediate(kSmiTagMask)); - __ bne(CMPRES, ZR, ¬_smi); - __ jr(RA); - __ delay_slot()->addiu(T2, ZR, Immediate(Smi::RawValue(kSmiCid))); - __ Bind(¬_smi); - __ LoadClassId(T2, T2); - __ jr(RA); - __ delay_slot()->SmiTag(T2); - - __ Bind(&update_ic_data); - // T0: ICData - __ addiu(SP, SP, Immediate(-4 * kWordSize)); - __ sw(A1, Address(SP, 3 * kWordSize)); - __ sw(A0, Address(SP, 2 * kWordSize)); - __ LoadObject(TMP1, Symbols::EqualOperator()); // Target's name. - __ sw(TMP1, Address(SP, 1 * kWordSize)); - __ sw(T0, Address(SP, 0 * kWordSize)); // ICData. - __ CallRuntime(kUpdateICDataTwoArgsRuntimeEntry, 4); - __ lw(A0, Address(SP, 2 * kWordSize)); - __ lw(A1, Address(SP, 3 * kWordSize)); - __ b(&compute_result); - __ delay_slot()->addiu(SP, SP, Immediate(4 * kWordSize)); -} - - // Calls to the runtime to optimize the given function. // T0: function to be reoptimized. // S4: argument descriptor (preserved). diff --git a/runtime/vm/stub_code_x64.cc b/runtime/vm/stub_code_x64.cc index 4c1d4634a43..d2d304fa17e 100644 --- a/runtime/vm/stub_code_x64.cc +++ b/runtime/vm/stub_code_x64.cc @@ -2003,104 +2003,6 @@ void StubCode::GenerateJumpToExceptionHandlerStub(Assembler* assembler) { } -// Implements equality operator when one of the arguments is null -// (identity check) and updates ICData if necessary. -// TOS + 0: return address -// TOS + 1: right argument -// TOS + 2: left argument -// RBX: ICData. -// RAX: result. -// TODO(srdjan): Move to VM stubs once Boolean objects become VM objects. -void StubCode::GenerateEqualityWithNullArgStub(Assembler* assembler) { - static const intptr_t kNumArgsTested = 2; -#if defined(DEBUG) - { Label ok; - __ movq(RCX, FieldAddress(RBX, ICData::num_args_tested_offset())); - __ cmpq(RCX, Immediate(kNumArgsTested)); - __ j(EQUAL, &ok, Assembler::kNearJump); - __ Stop("Incorrect ICData for equality"); - __ Bind(&ok); - } -#endif // DEBUG - // Check IC data, update if needed. - // RBX: IC data object (preserved). - __ movq(R12, FieldAddress(RBX, ICData::ic_data_offset())); - // R12: ic_data_array with check entries: classes and target functions. - __ leaq(R12, FieldAddress(R12, Array::data_offset())); - // R12: points directly to the first ic data array element. - - Label get_class_id_as_smi, no_match, loop, compute_result, found; - __ Bind(&loop); - // Check left. - __ movq(RAX, Address(RSP, 2 * kWordSize)); - __ call(&get_class_id_as_smi); - __ movq(R13, Address(R12, 0 * kWordSize)); - __ cmpq(RAX, R13); // Class id match? - __ j(NOT_EQUAL, &no_match, Assembler::kNearJump); - // Check right. - __ movq(RAX, Address(RSP, 1 * kWordSize)); - __ call(&get_class_id_as_smi); - __ movq(R13, Address(R12, 1 * kWordSize)); - __ cmpq(RAX, R13); // Class id match? - __ j(EQUAL, &found, Assembler::kNearJump); - __ Bind(&no_match); - // Next check group. - __ addq(R12, Immediate( - kWordSize * ICData::TestEntryLengthFor(kNumArgsTested))); - __ cmpq(R13, Immediate(Smi::RawValue(kIllegalCid))); // Done? - __ j(NOT_EQUAL, &loop, Assembler::kNearJump); - Label update_ic_data; - __ jmp(&update_ic_data); - - __ Bind(&found); - const intptr_t count_offset = - ICData::CountIndexFor(kNumArgsTested) * kWordSize; - __ addq(Address(R12, count_offset), Immediate(Smi::RawValue(1))); - __ j(NO_OVERFLOW, &compute_result); - __ movq(Address(R12, count_offset), - Immediate(Smi::RawValue(Smi::kMaxValue))); - - __ Bind(&compute_result); - Label true_label; - __ movq(RAX, Address(RSP, 1 * kWordSize)); - __ cmpq(RAX, Address(RSP, 2 * kWordSize)); - __ j(EQUAL, &true_label, Assembler::kNearJump); - __ LoadObject(RAX, Bool::False(), PP); - __ ret(); - __ Bind(&true_label); - __ LoadObject(RAX, Bool::True(), PP); - __ ret(); - - __ Bind(&get_class_id_as_smi); - Label not_smi; - // Test if Smi -> load Smi class for comparison. - __ testq(RAX, Immediate(kSmiTagMask)); - __ j(NOT_ZERO, ¬_smi, Assembler::kNearJump); - __ movq(RAX, Immediate(Smi::RawValue(kSmiCid))); - __ ret(); - - __ Bind(¬_smi); - __ LoadClassId(RAX, RAX); - __ SmiTag(RAX); - __ ret(); - - __ Bind(&update_ic_data); - - // RBX: ICData - __ movq(RAX, Address(RSP, 1 * kWordSize)); - __ movq(R13, Address(RSP, 2 * kWordSize)); - __ EnterStubFrameWithPP(); - __ pushq(R13); // arg 0 - __ pushq(RAX); // arg 1 - __ PushObject(Symbols::EqualOperator(), PP); // Target's name. - __ pushq(RBX); // ICData - __ CallRuntime(kUpdateICDataTwoArgsRuntimeEntry, 4); - __ Drop(4); - __ LeaveFrameWithPP(); - - __ jmp(&compute_result, Assembler::kNearJump); -} - // Calls to the runtime to optimize the given function. // RDI: function to be reoptimized. // R10: argument descriptor (preserved). diff --git a/tests/language/vm/function_equality_vm_test.dart b/tests/language/vm/function_equality_vm_test.dart new file mode 100644 index 00000000000..4077c662501 --- /dev/null +++ b/tests/language/vm/function_equality_vm_test.dart @@ -0,0 +1,19 @@ +// Copyright (c) 2013, 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. + +// Test function equality with null. + +import "package:expect/expect.dart"; + +class A { + foo() { } +} + +main() { + var a = new A(); + var f = a.foo; + Expect.isFalse(f == null); + Expect.isFalse(null == f); +} + diff --git a/tests/standalone/debugger/step_in_equals_test.dart b/tests/standalone/debugger/step_in_equals_test.dart new file mode 100644 index 00000000000..0e10e5dd883 --- /dev/null +++ b/tests/standalone/debugger/step_in_equals_test.dart @@ -0,0 +1,59 @@ +// Copyright (c) 2013, 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. + +import "debug_lib.dart"; + +class MyClass { + operator ==(other) { + print(other); + return true; // Breakpoint #3. + } +} + +main() { + if (RunScript(testScript)) return; + var a = new MyClass(); + var b = null; + var x = a == b; // Breakpoint #1. + print(x); + b = 123; + a == b; // Breakpoint #2. + print("after 2"); + if (a == null) { // Breakpoint #4. + throw "unreachable"; + } + print("after 4"); + if (null == a) { + throw "unreachable"; + } + print("ok"); +} + +var testScript = [ + MatchFrames(["main"]), + SetBreakpoint(18), + SetBreakpoint(21), + SetBreakpoint(10), + SetBreakpoint(23), + Resume(), + MatchFrames(["main"]), // At breakpoint #1. + StepInto(), + MatchFrames(["main"]), // Don't step into == method because of null. + Resume(), + MatchFrames(["main"]), // At breakpoint #2. + StepInto(), + StepInto(), + MatchFrames(["MyClass.==", "main"]), // At MyClass.== entry. + Resume(), + MatchFrames(["MyClass.==", "main"]), // At breakpoint #3. + Resume(), + MatchFrames(["main"]), // At breakpoint #4. + StepInto(), + MatchFrames(["main"]), // After breakpoint #4. + Step(), + MatchFrames(["main"]), // At null == a. + StepInto(), + MatchFrames(["main"]), // After null == a. + Resume() +];