Dart VM: Simplify code generation for equality operators.
By inserting the necessary checks for null inside the callee at the AST level, the code generation of == operations can be greatly simplified. This is a performance-neutral change and a step for allowing generic inlining of arbitrary == methods. So far we could only inline them for a common set of types in the flow graph optimizer. R=srdjan@google.com Review URL: https://codereview.chromium.org//24203004 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@28084 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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<intptr_t>(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<intptr_t>(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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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<intptr_t>(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<intptr_t>(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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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<int32_t>(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<int32_t>(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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+44
-49
@@ -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();
|
||||
|
||||
@@ -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_; }
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
];
|
||||
Reference in New Issue
Block a user