|
@ -1258,8 +1258,6 @@ void CodeGenerator::GenericBinaryOperation(BinaryOperation* expr, |
|
|
Result answer; |
|
|
Result answer; |
|
|
if (left_is_string) { |
|
|
if (left_is_string) { |
|
|
if (right_is_string) { |
|
|
if (right_is_string) { |
|
|
// TODO(lrn): if both are constant strings
|
|
|
|
|
|
// -- do a compile time cons, if allocation during codegen is allowed.
|
|
|
|
|
|
StringAddStub stub(NO_STRING_CHECK_IN_STUB); |
|
|
StringAddStub stub(NO_STRING_CHECK_IN_STUB); |
|
|
answer = frame_->CallStub(&stub, 2); |
|
|
answer = frame_->CallStub(&stub, 2); |
|
|
} else { |
|
|
} else { |
|
@ -2043,8 +2041,8 @@ Result CodeGenerator::ConstantSmiBinaryOperation( |
|
|
if (!operand->type_info().IsSmi()) { |
|
|
if (!operand->type_info().IsSmi()) { |
|
|
__ test(operand->reg(), Immediate(kSmiTagMask)); |
|
|
__ test(operand->reg(), Immediate(kSmiTagMask)); |
|
|
deferred->Branch(not_zero); |
|
|
deferred->Branch(not_zero); |
|
|
} else { |
|
|
} else if (FLAG_debug_code) { |
|
|
if (FLAG_debug_code) __ AbortIfNotSmi(operand->reg()); |
|
|
__ AbortIfNotSmi(operand->reg()); |
|
|
} |
|
|
} |
|
|
deferred->BindExit(); |
|
|
deferred->BindExit(); |
|
|
answer = *operand; |
|
|
answer = *operand; |
|
@ -2082,8 +2080,8 @@ Result CodeGenerator::ConstantSmiBinaryOperation( |
|
|
if (!operand->type_info().IsSmi()) { |
|
|
if (!operand->type_info().IsSmi()) { |
|
|
__ test(answer.reg(), Immediate(kSmiTagMask)); |
|
|
__ test(answer.reg(), Immediate(kSmiTagMask)); |
|
|
deferred->Branch(not_zero); |
|
|
deferred->Branch(not_zero); |
|
|
} else { |
|
|
} else if (FLAG_debug_code) { |
|
|
if (FLAG_debug_code) __ AbortIfNotSmi(operand->reg()); |
|
|
__ AbortIfNotSmi(operand->reg()); |
|
|
} |
|
|
} |
|
|
deferred->BindExit(); |
|
|
deferred->BindExit(); |
|
|
operand->Unuse(); |
|
|
operand->Unuse(); |
|
@ -2117,7 +2115,9 @@ Result CodeGenerator::ConstantSmiBinaryOperation( |
|
|
} |
|
|
} |
|
|
deferred->BindExit(); |
|
|
deferred->BindExit(); |
|
|
} else { |
|
|
} else { |
|
|
if (FLAG_debug_code) __ AbortIfNotSmi(operand->reg()); |
|
|
if (FLAG_debug_code) { |
|
|
|
|
|
__ AbortIfNotSmi(operand->reg()); |
|
|
|
|
|
} |
|
|
if (shift_value > 0) { |
|
|
if (shift_value > 0) { |
|
|
__ sar(operand->reg(), shift_value); |
|
|
__ sar(operand->reg(), shift_value); |
|
|
__ and_(operand->reg(), ~kSmiTagMask); |
|
|
__ and_(operand->reg(), ~kSmiTagMask); |
|
@ -2149,8 +2149,8 @@ Result CodeGenerator::ConstantSmiBinaryOperation( |
|
|
if (!operand->type_info().IsSmi()) { |
|
|
if (!operand->type_info().IsSmi()) { |
|
|
__ test(operand->reg(), Immediate(kSmiTagMask)); |
|
|
__ test(operand->reg(), Immediate(kSmiTagMask)); |
|
|
deferred->Branch(not_zero); |
|
|
deferred->Branch(not_zero); |
|
|
} else { |
|
|
} else if (FLAG_debug_code) { |
|
|
if (FLAG_debug_code) __ AbortIfNotSmi(operand->reg()); |
|
|
__ AbortIfNotSmi(operand->reg()); |
|
|
} |
|
|
} |
|
|
__ mov(answer.reg(), operand->reg()); |
|
|
__ mov(answer.reg(), operand->reg()); |
|
|
__ SmiUntag(answer.reg()); |
|
|
__ SmiUntag(answer.reg()); |
|
@ -2168,12 +2168,12 @@ Result CodeGenerator::ConstantSmiBinaryOperation( |
|
|
|
|
|
|
|
|
case Token::SHL: |
|
|
case Token::SHL: |
|
|
if (reversed) { |
|
|
if (reversed) { |
|
|
|
|
|
// Move operand into ecx and also into a second register.
|
|
|
|
|
|
// If operand is already in a register, take advantage of that.
|
|
|
|
|
|
// This lets us modify ecx, but still bail out to deferred code.
|
|
|
Result right; |
|
|
Result right; |
|
|
Result right_copy_in_ecx; |
|
|
Result right_copy_in_ecx; |
|
|
|
|
|
TypeInfo right_type_info = operand->type_info(); |
|
|
// Make sure to get a copy of the right operand into ecx. This
|
|
|
|
|
|
// allows us to modify it without having to restore it in the
|
|
|
|
|
|
// deferred code.
|
|
|
|
|
|
operand->ToRegister(); |
|
|
operand->ToRegister(); |
|
|
if (operand->reg().is(ecx)) { |
|
|
if (operand->reg().is(ecx)) { |
|
|
right = allocator()->Allocate(); |
|
|
right = allocator()->Allocate(); |
|
@ -2193,14 +2193,14 @@ Result CodeGenerator::ConstantSmiBinaryOperation( |
|
|
answer.reg(), |
|
|
answer.reg(), |
|
|
smi_value, |
|
|
smi_value, |
|
|
right.reg(), |
|
|
right.reg(), |
|
|
right.type_info(), |
|
|
right_type_info, |
|
|
overwrite_mode); |
|
|
overwrite_mode); |
|
|
__ mov(answer.reg(), Immediate(int_value)); |
|
|
__ mov(answer.reg(), Immediate(int_value)); |
|
|
__ sar(ecx, kSmiTagSize); |
|
|
__ sar(ecx, kSmiTagSize); |
|
|
if (!right.type_info().IsSmi()) { |
|
|
if (!right_type_info.IsSmi()) { |
|
|
deferred->Branch(carry); |
|
|
deferred->Branch(carry); |
|
|
} else { |
|
|
} else if (FLAG_debug_code) { |
|
|
if (FLAG_debug_code) __ AbortIfNotSmi(right.reg()); |
|
|
__ AbortIfNotSmi(right.reg()); |
|
|
} |
|
|
} |
|
|
__ shl_cl(answer.reg()); |
|
|
__ shl_cl(answer.reg()); |
|
|
__ cmp(answer.reg(), 0xc0000000); |
|
|
__ cmp(answer.reg(), 0xc0000000); |
|
@ -2241,8 +2241,8 @@ Result CodeGenerator::ConstantSmiBinaryOperation( |
|
|
if (!operand->type_info().IsSmi()) { |
|
|
if (!operand->type_info().IsSmi()) { |
|
|
__ test(operand->reg(), Immediate(kSmiTagMask)); |
|
|
__ test(operand->reg(), Immediate(kSmiTagMask)); |
|
|
deferred->Branch(not_zero); |
|
|
deferred->Branch(not_zero); |
|
|
} else { |
|
|
} else if (FLAG_debug_code) { |
|
|
if (FLAG_debug_code) __ AbortIfNotSmi(operand->reg()); |
|
|
__ AbortIfNotSmi(operand->reg()); |
|
|
} |
|
|
} |
|
|
__ mov(answer.reg(), operand->reg()); |
|
|
__ mov(answer.reg(), operand->reg()); |
|
|
ASSERT(kSmiTag == 0); // adjust code if not the case
|
|
|
ASSERT(kSmiTag == 0); // adjust code if not the case
|
|
@ -2285,8 +2285,8 @@ Result CodeGenerator::ConstantSmiBinaryOperation( |
|
|
if (!operand->type_info().IsSmi()) { |
|
|
if (!operand->type_info().IsSmi()) { |
|
|
__ test(operand->reg(), Immediate(kSmiTagMask)); |
|
|
__ test(operand->reg(), Immediate(kSmiTagMask)); |
|
|
deferred->Branch(not_zero); |
|
|
deferred->Branch(not_zero); |
|
|
} else { |
|
|
} else if (FLAG_debug_code) { |
|
|
if (FLAG_debug_code) __ AbortIfNotSmi(operand->reg()); |
|
|
__ AbortIfNotSmi(operand->reg()); |
|
|
} |
|
|
} |
|
|
if (op == Token::BIT_AND) { |
|
|
if (op == Token::BIT_AND) { |
|
|
__ and_(Operand(operand->reg()), Immediate(value)); |
|
|
__ and_(Operand(operand->reg()), Immediate(value)); |
|
@ -2339,6 +2339,7 @@ Result CodeGenerator::ConstantSmiBinaryOperation( |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
// Generate inline code for mod of powers of 2 and negative powers of 2.
|
|
|
// Generate inline code for mod of powers of 2 and negative powers of 2.
|
|
|
case Token::MOD: |
|
|
case Token::MOD: |
|
|
if (!reversed && |
|
|
if (!reversed && |
|
@ -2693,7 +2694,7 @@ void CodeGenerator::Comparison(AstNode* node, |
|
|
if (cc == equal) { |
|
|
if (cc == equal) { |
|
|
Label comparison_done; |
|
|
Label comparison_done; |
|
|
__ cmp(FieldOperand(left_side.reg(), String::kLengthOffset), |
|
|
__ cmp(FieldOperand(left_side.reg(), String::kLengthOffset), |
|
|
Immediate(1)); |
|
|
Immediate(Smi::FromInt(1))); |
|
|
__ j(not_equal, &comparison_done); |
|
|
__ j(not_equal, &comparison_done); |
|
|
uint8_t char_value = |
|
|
uint8_t char_value = |
|
|
static_cast<uint8_t>(String::cast(*right_val)->Get(0)); |
|
|
static_cast<uint8_t>(String::cast(*right_val)->Get(0)); |
|
@ -2703,6 +2704,7 @@ void CodeGenerator::Comparison(AstNode* node, |
|
|
} else { |
|
|
} else { |
|
|
__ mov(temp2.reg(), |
|
|
__ mov(temp2.reg(), |
|
|
FieldOperand(left_side.reg(), String::kLengthOffset)); |
|
|
FieldOperand(left_side.reg(), String::kLengthOffset)); |
|
|
|
|
|
__ SmiUntag(temp2.reg()); |
|
|
__ sub(Operand(temp2.reg()), Immediate(1)); |
|
|
__ sub(Operand(temp2.reg()), Immediate(1)); |
|
|
Label comparison; |
|
|
Label comparison; |
|
|
// If the length is 0 then the subtraction gave -1 which compares less
|
|
|
// If the length is 0 then the subtraction gave -1 which compares less
|
|
@ -2722,7 +2724,7 @@ void CodeGenerator::Comparison(AstNode* node, |
|
|
// If the first character is the same then the long string sorts after
|
|
|
// If the first character is the same then the long string sorts after
|
|
|
// the short one.
|
|
|
// the short one.
|
|
|
__ cmp(FieldOperand(left_side.reg(), String::kLengthOffset), |
|
|
__ cmp(FieldOperand(left_side.reg(), String::kLengthOffset), |
|
|
Immediate(1)); |
|
|
Immediate(Smi::FromInt(1))); |
|
|
__ bind(&characters_were_different); |
|
|
__ bind(&characters_were_different); |
|
|
} |
|
|
} |
|
|
temp2.Unuse(); |
|
|
temp2.Unuse(); |
|
@ -5367,10 +5369,13 @@ void CodeGenerator::EmitSlotAssignment(Assignment* node) { |
|
|
|
|
|
|
|
|
// Evaluate the right-hand side.
|
|
|
// Evaluate the right-hand side.
|
|
|
if (node->is_compound()) { |
|
|
if (node->is_compound()) { |
|
|
|
|
|
// For a compound assignment the right-hand side is a binary operation
|
|
|
|
|
|
// between the current property value and the actual right-hand side.
|
|
|
Result result = LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF); |
|
|
Result result = LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF); |
|
|
frame()->Push(&result); |
|
|
frame()->Push(&result); |
|
|
Load(node->value()); |
|
|
Load(node->value()); |
|
|
|
|
|
|
|
|
|
|
|
// Perform the binary operation.
|
|
|
bool overwrite_value = |
|
|
bool overwrite_value = |
|
|
(node->value()->AsBinaryOperation() != NULL && |
|
|
(node->value()->AsBinaryOperation() != NULL && |
|
|
node->value()->AsBinaryOperation()->ResultOverwriteAllowed()); |
|
|
node->value()->AsBinaryOperation()->ResultOverwriteAllowed()); |
|
@ -5380,6 +5385,7 @@ void CodeGenerator::EmitSlotAssignment(Assignment* node) { |
|
|
GenericBinaryOperation(&expr, |
|
|
GenericBinaryOperation(&expr, |
|
|
overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE); |
|
|
overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE); |
|
|
} else { |
|
|
} else { |
|
|
|
|
|
// For non-compound assignment just load the right-hand side.
|
|
|
Load(node->value()); |
|
|
Load(node->value()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -5402,7 +5408,9 @@ void CodeGenerator::EmitNamedPropertyAssignment(Assignment* node) { |
|
|
Property* prop = node->target()->AsProperty(); |
|
|
Property* prop = node->target()->AsProperty(); |
|
|
ASSERT(var == NULL || (prop == NULL && var->is_global())); |
|
|
ASSERT(var == NULL || (prop == NULL && var->is_global())); |
|
|
|
|
|
|
|
|
// Initialize name and evaluate the receiver subexpression if necessary.
|
|
|
// Initialize name and evaluate the receiver sub-expression if necessary. If
|
|
|
|
|
|
// the receiver is trivial it is not placed on the stack at this point, but
|
|
|
|
|
|
// loaded whenever actually needed.
|
|
|
Handle<String> name; |
|
|
Handle<String> name; |
|
|
bool is_trivial_receiver = false; |
|
|
bool is_trivial_receiver = false; |
|
|
if (var != NULL) { |
|
|
if (var != NULL) { |
|
@ -5416,10 +5424,13 @@ void CodeGenerator::EmitNamedPropertyAssignment(Assignment* node) { |
|
|
if (!is_trivial_receiver) Load(prop->obj()); |
|
|
if (!is_trivial_receiver) Load(prop->obj()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Change to slow case in the beginning of an initialization block to
|
|
|
|
|
|
// avoid the quadratic behavior of repeatedly adding fast properties.
|
|
|
if (node->starts_initialization_block()) { |
|
|
if (node->starts_initialization_block()) { |
|
|
|
|
|
// Initialization block consists of assignments of the form expr.x = ..., so
|
|
|
|
|
|
// this will never be an assignment to a variable, so there must be a
|
|
|
|
|
|
// receiver object.
|
|
|
ASSERT_EQ(NULL, var); |
|
|
ASSERT_EQ(NULL, var); |
|
|
// Change to slow case in the beginning of an initialization block to
|
|
|
|
|
|
// avoid the quadratic behavior of repeatedly adding fast properties.
|
|
|
|
|
|
if (is_trivial_receiver) { |
|
|
if (is_trivial_receiver) { |
|
|
frame()->Push(prop->obj()); |
|
|
frame()->Push(prop->obj()); |
|
|
} else { |
|
|
} else { |
|
@ -5428,14 +5439,21 @@ void CodeGenerator::EmitNamedPropertyAssignment(Assignment* node) { |
|
|
Result ignored = frame()->CallRuntime(Runtime::kToSlowProperties, 1); |
|
|
Result ignored = frame()->CallRuntime(Runtime::kToSlowProperties, 1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Change to fast case at the end of an initialization block. To prepare for
|
|
|
|
|
|
// that add an extra copy of the receiver to the frame, so that it can be
|
|
|
|
|
|
// converted back to fast case after the assignment.
|
|
|
if (node->ends_initialization_block() && !is_trivial_receiver) { |
|
|
if (node->ends_initialization_block() && !is_trivial_receiver) { |
|
|
// Add an extra copy of the receiver to the frame, so that it can be
|
|
|
|
|
|
// converted back to fast case after the assignment.
|
|
|
|
|
|
frame()->Dup(); |
|
|
frame()->Dup(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Stack layout:
|
|
|
|
|
|
// [tos] : receiver (only materialized if non-trivial)
|
|
|
|
|
|
// [tos+1] : receiver if at the end of an initialization block
|
|
|
|
|
|
|
|
|
// Evaluate the right-hand side.
|
|
|
// Evaluate the right-hand side.
|
|
|
if (node->is_compound()) { |
|
|
if (node->is_compound()) { |
|
|
|
|
|
// For a compound assignment the right-hand side is a binary operation
|
|
|
|
|
|
// between the current property value and the actual right-hand side.
|
|
|
if (is_trivial_receiver) { |
|
|
if (is_trivial_receiver) { |
|
|
frame()->Push(prop->obj()); |
|
|
frame()->Push(prop->obj()); |
|
|
} else if (var != NULL) { |
|
|
} else if (var != NULL) { |
|
@ -5459,9 +5477,15 @@ void CodeGenerator::EmitNamedPropertyAssignment(Assignment* node) { |
|
|
GenericBinaryOperation(&expr, |
|
|
GenericBinaryOperation(&expr, |
|
|
overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE); |
|
|
overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE); |
|
|
} else { |
|
|
} else { |
|
|
|
|
|
// For non-compound assignment just load the right-hand side.
|
|
|
Load(node->value()); |
|
|
Load(node->value()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Stack layout:
|
|
|
|
|
|
// [tos] : value
|
|
|
|
|
|
// [tos+1] : receiver (only materialized if non-trivial)
|
|
|
|
|
|
// [tos+2] : receiver if at the end of an initialization block
|
|
|
|
|
|
|
|
|
// Perform the assignment. It is safe to ignore constants here.
|
|
|
// Perform the assignment. It is safe to ignore constants here.
|
|
|
ASSERT(var == NULL || var->mode() != Variable::CONST); |
|
|
ASSERT(var == NULL || var->mode() != Variable::CONST); |
|
|
ASSERT_NE(Token::INIT_CONST, node->op()); |
|
|
ASSERT_NE(Token::INIT_CONST, node->op()); |
|
@ -5475,6 +5499,10 @@ void CodeGenerator::EmitNamedPropertyAssignment(Assignment* node) { |
|
|
Result answer = EmitNamedStore(name, is_contextual); |
|
|
Result answer = EmitNamedStore(name, is_contextual); |
|
|
frame()->Push(&answer); |
|
|
frame()->Push(&answer); |
|
|
|
|
|
|
|
|
|
|
|
// Stack layout:
|
|
|
|
|
|
// [tos] : result
|
|
|
|
|
|
// [tos+1] : receiver if at the end of an initialization block
|
|
|
|
|
|
|
|
|
if (node->ends_initialization_block()) { |
|
|
if (node->ends_initialization_block()) { |
|
|
ASSERT_EQ(NULL, var); |
|
|
ASSERT_EQ(NULL, var); |
|
|
// The argument to the runtime call is the receiver.
|
|
|
// The argument to the runtime call is the receiver.
|
|
@ -5491,6 +5519,9 @@ void CodeGenerator::EmitNamedPropertyAssignment(Assignment* node) { |
|
|
Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1); |
|
|
Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Stack layout:
|
|
|
|
|
|
// [tos] : result
|
|
|
|
|
|
|
|
|
ASSERT_EQ(frame()->height(), original_height + 1); |
|
|
ASSERT_EQ(frame()->height(), original_height + 1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -5499,38 +5530,47 @@ void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) { |
|
|
#ifdef DEBUG |
|
|
#ifdef DEBUG |
|
|
int original_height = frame()->height(); |
|
|
int original_height = frame()->height(); |
|
|
#endif |
|
|
#endif |
|
|
Comment cmnt(masm_, "[ Named Property Assignment"); |
|
|
Comment cmnt(masm_, "[ Keyed Property Assignment"); |
|
|
Property* prop = node->target()->AsProperty(); |
|
|
Property* prop = node->target()->AsProperty(); |
|
|
ASSERT_NOT_NULL(prop); |
|
|
ASSERT_NOT_NULL(prop); |
|
|
|
|
|
|
|
|
// Evaluate the receiver subexpression.
|
|
|
// Evaluate the receiver subexpression.
|
|
|
Load(prop->obj()); |
|
|
Load(prop->obj()); |
|
|
|
|
|
|
|
|
|
|
|
// Change to slow case in the beginning of an initialization block to
|
|
|
|
|
|
// avoid the quadratic behavior of repeatedly adding fast properties.
|
|
|
if (node->starts_initialization_block()) { |
|
|
if (node->starts_initialization_block()) { |
|
|
// Change to slow case in the beginning of an initialization block to
|
|
|
|
|
|
// avoid the quadratic behavior of repeatedly adding fast properties.
|
|
|
|
|
|
frame_->Dup(); |
|
|
frame_->Dup(); |
|
|
Result ignored = frame_->CallRuntime(Runtime::kToSlowProperties, 1); |
|
|
Result ignored = frame_->CallRuntime(Runtime::kToSlowProperties, 1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Change to fast case at the end of an initialization block. To prepare for
|
|
|
|
|
|
// that add an extra copy of the receiver to the frame, so that it can be
|
|
|
|
|
|
// converted back to fast case after the assignment.
|
|
|
if (node->ends_initialization_block()) { |
|
|
if (node->ends_initialization_block()) { |
|
|
// Add an extra copy of the receiver to the frame, so that it can be
|
|
|
|
|
|
// converted back to fast case after the assignment.
|
|
|
|
|
|
frame_->Dup(); |
|
|
frame_->Dup(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Evaluate the key subexpression.
|
|
|
// Evaluate the key subexpression.
|
|
|
Load(prop->key()); |
|
|
Load(prop->key()); |
|
|
|
|
|
|
|
|
|
|
|
// Stack layout:
|
|
|
|
|
|
// [tos] : key
|
|
|
|
|
|
// [tos+1] : receiver
|
|
|
|
|
|
// [tos+2] : receiver if at the end of an initialization block
|
|
|
|
|
|
|
|
|
// Evaluate the right-hand side.
|
|
|
// Evaluate the right-hand side.
|
|
|
if (node->is_compound()) { |
|
|
if (node->is_compound()) { |
|
|
// Duplicate receiver and key.
|
|
|
// For a compound assignment the right-hand side is a binary operation
|
|
|
|
|
|
// between the current property value and the actual right-hand side.
|
|
|
|
|
|
// Duplicate receiver and key for loading the current property value.
|
|
|
frame()->PushElementAt(1); |
|
|
frame()->PushElementAt(1); |
|
|
frame()->PushElementAt(1); |
|
|
frame()->PushElementAt(1); |
|
|
Result value = EmitKeyedLoad(); |
|
|
Result value = EmitKeyedLoad(); |
|
|
frame()->Push(&value); |
|
|
frame()->Push(&value); |
|
|
Load(node->value()); |
|
|
Load(node->value()); |
|
|
|
|
|
|
|
|
|
|
|
// Perform the binary operation.
|
|
|
bool overwrite_value = |
|
|
bool overwrite_value = |
|
|
(node->value()->AsBinaryOperation() != NULL && |
|
|
(node->value()->AsBinaryOperation() != NULL && |
|
|
node->value()->AsBinaryOperation()->ResultOverwriteAllowed()); |
|
|
node->value()->AsBinaryOperation()->ResultOverwriteAllowed()); |
|
@ -5539,15 +5579,27 @@ void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) { |
|
|
GenericBinaryOperation(&expr, |
|
|
GenericBinaryOperation(&expr, |
|
|
overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE); |
|
|
overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE); |
|
|
} else { |
|
|
} else { |
|
|
|
|
|
// For non-compound assignment just load the right-hand side.
|
|
|
Load(node->value()); |
|
|
Load(node->value()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Stack layout:
|
|
|
|
|
|
// [tos] : value
|
|
|
|
|
|
// [tos+1] : key
|
|
|
|
|
|
// [tos+2] : receiver
|
|
|
|
|
|
// [tos+3] : receiver if at the end of an initialization block
|
|
|
|
|
|
|
|
|
// Perform the assignment. It is safe to ignore constants here.
|
|
|
// Perform the assignment. It is safe to ignore constants here.
|
|
|
ASSERT(node->op() != Token::INIT_CONST); |
|
|
ASSERT(node->op() != Token::INIT_CONST); |
|
|
CodeForSourcePosition(node->position()); |
|
|
CodeForSourcePosition(node->position()); |
|
|
Result answer = EmitKeyedStore(prop->key()->type()); |
|
|
Result answer = EmitKeyedStore(prop->key()->type()); |
|
|
frame()->Push(&answer); |
|
|
frame()->Push(&answer); |
|
|
|
|
|
|
|
|
|
|
|
// Stack layout:
|
|
|
|
|
|
// [tos] : result
|
|
|
|
|
|
// [tos+1] : receiver if at the end of an initialization block
|
|
|
|
|
|
|
|
|
|
|
|
// Change to fast case at the end of an initialization block.
|
|
|
if (node->ends_initialization_block()) { |
|
|
if (node->ends_initialization_block()) { |
|
|
// The argument to the runtime call is the extra copy of the receiver,
|
|
|
// The argument to the runtime call is the extra copy of the receiver,
|
|
|
// which is below the value of the assignment. Swap the receiver and
|
|
|
// which is below the value of the assignment. Swap the receiver and
|
|
@ -5559,6 +5611,9 @@ void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) { |
|
|
Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1); |
|
|
Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Stack layout:
|
|
|
|
|
|
// [tos] : result
|
|
|
|
|
|
|
|
|
ASSERT(frame()->height() == original_height + 1); |
|
|
ASSERT(frame()->height() == original_height + 1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -5950,6 +6005,7 @@ void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) { |
|
|
result.reg(), |
|
|
result.reg(), |
|
|
&slow_case, |
|
|
&slow_case, |
|
|
&slow_case, |
|
|
&slow_case, |
|
|
|
|
|
&slow_case, |
|
|
&slow_case); |
|
|
&slow_case); |
|
|
__ jmp(&exit); |
|
|
__ jmp(&exit); |
|
|
|
|
|
|
|
@ -6607,6 +6663,121 @@ void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DeferredSwapElements: public DeferredCode { |
|
|
|
|
|
public: |
|
|
|
|
|
DeferredSwapElements(Register object, Register index1, Register index2) |
|
|
|
|
|
: object_(object), index1_(index1), index2_(index2) { |
|
|
|
|
|
set_comment("[ DeferredSwapElements"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
virtual void Generate(); |
|
|
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
|
Register object_, index1_, index2_; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void DeferredSwapElements::Generate() { |
|
|
|
|
|
__ push(object_); |
|
|
|
|
|
__ push(index1_); |
|
|
|
|
|
__ push(index2_); |
|
|
|
|
|
__ CallRuntime(Runtime::kSwapElements, 3); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) { |
|
|
|
|
|
// Note: this code assumes that indices are passed are within
|
|
|
|
|
|
// elements' bounds and refer to valid (not holes) values.
|
|
|
|
|
|
Comment cmnt(masm_, "[ GenerateSwapElements"); |
|
|
|
|
|
|
|
|
|
|
|
ASSERT_EQ(3, args->length()); |
|
|
|
|
|
|
|
|
|
|
|
Load(args->at(0)); |
|
|
|
|
|
Load(args->at(1)); |
|
|
|
|
|
Load(args->at(2)); |
|
|
|
|
|
|
|
|
|
|
|
Result index2 = frame_->Pop(); |
|
|
|
|
|
index2.ToRegister(); |
|
|
|
|
|
|
|
|
|
|
|
Result index1 = frame_->Pop(); |
|
|
|
|
|
index1.ToRegister(); |
|
|
|
|
|
|
|
|
|
|
|
Result object = frame_->Pop(); |
|
|
|
|
|
object.ToRegister(); |
|
|
|
|
|
|
|
|
|
|
|
Result tmp1 = allocator()->Allocate(); |
|
|
|
|
|
tmp1.ToRegister(); |
|
|
|
|
|
Result tmp2 = allocator()->Allocate(); |
|
|
|
|
|
tmp2.ToRegister(); |
|
|
|
|
|
|
|
|
|
|
|
frame_->Spill(object.reg()); |
|
|
|
|
|
frame_->Spill(index1.reg()); |
|
|
|
|
|
frame_->Spill(index2.reg()); |
|
|
|
|
|
|
|
|
|
|
|
DeferredSwapElements* deferred = new DeferredSwapElements(object.reg(), |
|
|
|
|
|
index1.reg(), |
|
|
|
|
|
index2.reg()); |
|
|
|
|
|
|
|
|
|
|
|
// Fetch the map and check if array is in fast case.
|
|
|
|
|
|
// Check that object doesn't require security checks and
|
|
|
|
|
|
// has no indexed interceptor.
|
|
|
|
|
|
__ CmpObjectType(object.reg(), FIRST_JS_OBJECT_TYPE, tmp1.reg()); |
|
|
|
|
|
deferred->Branch(less); |
|
|
|
|
|
__ movzx_b(tmp1.reg(), FieldOperand(tmp1.reg(), Map::kBitFieldOffset)); |
|
|
|
|
|
__ test(tmp1.reg(), Immediate(KeyedLoadIC::kSlowCaseBitFieldMask)); |
|
|
|
|
|
deferred->Branch(not_zero); |
|
|
|
|
|
|
|
|
|
|
|
// Check the object's elements are in fast case.
|
|
|
|
|
|
__ mov(tmp1.reg(), FieldOperand(object.reg(), JSObject::kElementsOffset)); |
|
|
|
|
|
__ cmp(FieldOperand(tmp1.reg(), HeapObject::kMapOffset), |
|
|
|
|
|
Immediate(Factory::fixed_array_map())); |
|
|
|
|
|
deferred->Branch(not_equal); |
|
|
|
|
|
|
|
|
|
|
|
// Smi-tagging is equivalent to multiplying by 2.
|
|
|
|
|
|
STATIC_ASSERT(kSmiTag == 0); |
|
|
|
|
|
STATIC_ASSERT(kSmiTagSize == 1); |
|
|
|
|
|
|
|
|
|
|
|
// Check that both indices are smis.
|
|
|
|
|
|
__ mov(tmp2.reg(), index1.reg()); |
|
|
|
|
|
__ or_(tmp2.reg(), Operand(index2.reg())); |
|
|
|
|
|
__ test(tmp2.reg(), Immediate(kSmiTagMask)); |
|
|
|
|
|
deferred->Branch(not_zero); |
|
|
|
|
|
|
|
|
|
|
|
// Bring addresses into index1 and index2.
|
|
|
|
|
|
__ lea(index1.reg(), FieldOperand(tmp1.reg(), |
|
|
|
|
|
index1.reg(), |
|
|
|
|
|
times_half_pointer_size, // index1 is Smi
|
|
|
|
|
|
FixedArray::kHeaderSize)); |
|
|
|
|
|
__ lea(index2.reg(), FieldOperand(tmp1.reg(), |
|
|
|
|
|
index2.reg(), |
|
|
|
|
|
times_half_pointer_size, // index2 is Smi
|
|
|
|
|
|
FixedArray::kHeaderSize)); |
|
|
|
|
|
|
|
|
|
|
|
// Swap elements.
|
|
|
|
|
|
__ mov(object.reg(), Operand(index1.reg(), 0)); |
|
|
|
|
|
__ mov(tmp2.reg(), Operand(index2.reg(), 0)); |
|
|
|
|
|
__ mov(Operand(index2.reg(), 0), object.reg()); |
|
|
|
|
|
__ mov(Operand(index1.reg(), 0), tmp2.reg()); |
|
|
|
|
|
|
|
|
|
|
|
Label done; |
|
|
|
|
|
__ InNewSpace(tmp1.reg(), tmp2.reg(), equal, &done); |
|
|
|
|
|
// Possible optimization: do a check that both values are Smis
|
|
|
|
|
|
// (or them and test against Smi mask.)
|
|
|
|
|
|
|
|
|
|
|
|
__ mov(tmp2.reg(), tmp1.reg()); |
|
|
|
|
|
RecordWriteStub recordWrite1(tmp2.reg(), index1.reg(), object.reg()); |
|
|
|
|
|
__ CallStub(&recordWrite1); |
|
|
|
|
|
|
|
|
|
|
|
RecordWriteStub recordWrite2(tmp1.reg(), index2.reg(), object.reg()); |
|
|
|
|
|
__ CallStub(&recordWrite2); |
|
|
|
|
|
|
|
|
|
|
|
__ bind(&done); |
|
|
|
|
|
|
|
|
|
|
|
deferred->BindExit(); |
|
|
|
|
|
frame_->Push(Factory::undefined_value()); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CodeGenerator::GenerateCallFunction(ZoneList<Expression*>* args) { |
|
|
void CodeGenerator::GenerateCallFunction(ZoneList<Expression*>* args) { |
|
|
Comment cmnt(masm_, "[ GenerateCallFunction"); |
|
|
Comment cmnt(masm_, "[ GenerateCallFunction"); |
|
|
|
|
|
|
|
@ -6623,10 +6794,10 @@ void CodeGenerator::GenerateCallFunction(ZoneList<Expression*>* args) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Generates the Math.pow method - only handles special cases and branches to
|
|
|
// Generates the Math.pow method. Only handles special cases and
|
|
|
// the runtime system if not.Please note - this function assumes that
|
|
|
// branches to the runtime system for everything else. Please note
|
|
|
// the callsite has executed ToNumber on both arguments and that the
|
|
|
// that this function assumes that the callsite has executed ToNumber
|
|
|
// arguments are not the same identifier.
|
|
|
// on both arguments.
|
|
|
void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) { |
|
|
void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) { |
|
|
ASSERT(args->length() == 2); |
|
|
ASSERT(args->length() == 2); |
|
|
Load(args->at(0)); |
|
|
Load(args->at(0)); |
|
@ -6649,8 +6820,6 @@ void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) { |
|
|
|
|
|
|
|
|
Result answer = allocator()->Allocate(); |
|
|
Result answer = allocator()->Allocate(); |
|
|
ASSERT(answer.is_valid()); |
|
|
ASSERT(answer.is_valid()); |
|
|
// We can safely assume that the base and exponent is not in the same
|
|
|
|
|
|
// register since we only call this from one callsite (math.js).
|
|
|
|
|
|
ASSERT(!exponent.reg().is(base.reg())); |
|
|
ASSERT(!exponent.reg().is(base.reg())); |
|
|
JumpTarget call_runtime; |
|
|
JumpTarget call_runtime; |
|
|
|
|
|
|
|
@ -6699,7 +6868,6 @@ void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) { |
|
|
Label while_true; |
|
|
Label while_true; |
|
|
Label no_multiply; |
|
|
Label no_multiply; |
|
|
|
|
|
|
|
|
// Label allocate_and_return;
|
|
|
|
|
|
__ bind(&while_true); |
|
|
__ bind(&while_true); |
|
|
__ shr(exponent.reg(), 1); |
|
|
__ shr(exponent.reg(), 1); |
|
|
__ j(not_carry, &no_multiply); |
|
|
__ j(not_carry, &no_multiply); |
|
@ -8390,6 +8558,8 @@ Result CodeGenerator::EmitKeyedStore(StaticType* key_type) { |
|
|
|
|
|
|
|
|
Result tmp = allocator_->Allocate(); |
|
|
Result tmp = allocator_->Allocate(); |
|
|
ASSERT(tmp.is_valid()); |
|
|
ASSERT(tmp.is_valid()); |
|
|
|
|
|
Result tmp2 = allocator_->Allocate(); |
|
|
|
|
|
ASSERT(tmp2.is_valid()); |
|
|
|
|
|
|
|
|
// Determine whether the value is a constant before putting it in a
|
|
|
// Determine whether the value is a constant before putting it in a
|
|
|
// register.
|
|
|
// register.
|
|
@ -8406,12 +8576,9 @@ Result CodeGenerator::EmitKeyedStore(StaticType* key_type) { |
|
|
receiver.reg(), |
|
|
receiver.reg(), |
|
|
tmp.reg()); |
|
|
tmp.reg()); |
|
|
|
|
|
|
|
|
// Check that the value is a smi if it is not a constant. We can skip
|
|
|
// Check that the receiver is not a smi.
|
|
|
// the write barrier for smis and constants.
|
|
|
__ test(receiver.reg(), Immediate(kSmiTagMask)); |
|
|
if (!value_is_constant) { |
|
|
deferred->Branch(zero); |
|
|
__ test(result.reg(), Immediate(kSmiTagMask)); |
|
|
|
|
|
deferred->Branch(not_zero); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Check that the key is a smi.
|
|
|
// Check that the key is a smi.
|
|
|
if (!key.is_smi()) { |
|
|
if (!key.is_smi()) { |
|
@ -8421,10 +8588,6 @@ Result CodeGenerator::EmitKeyedStore(StaticType* key_type) { |
|
|
if (FLAG_debug_code) __ AbortIfNotSmi(key.reg()); |
|
|
if (FLAG_debug_code) __ AbortIfNotSmi(key.reg()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Check that the receiver is not a smi.
|
|
|
|
|
|
__ test(receiver.reg(), Immediate(kSmiTagMask)); |
|
|
|
|
|
deferred->Branch(zero); |
|
|
|
|
|
|
|
|
|
|
|
// Check that the receiver is a JSArray.
|
|
|
// Check that the receiver is a JSArray.
|
|
|
__ CmpObjectType(receiver.reg(), JS_ARRAY_TYPE, tmp.reg()); |
|
|
__ CmpObjectType(receiver.reg(), JS_ARRAY_TYPE, tmp.reg()); |
|
|
deferred->Branch(not_equal); |
|
|
deferred->Branch(not_equal); |
|
@ -8438,7 +8601,19 @@ Result CodeGenerator::EmitKeyedStore(StaticType* key_type) { |
|
|
// Get the elements array from the receiver and check that it is not a
|
|
|
// Get the elements array from the receiver and check that it is not a
|
|
|
// dictionary.
|
|
|
// dictionary.
|
|
|
__ mov(tmp.reg(), |
|
|
__ mov(tmp.reg(), |
|
|
FieldOperand(receiver.reg(), JSObject::kElementsOffset)); |
|
|
FieldOperand(receiver.reg(), JSArray::kElementsOffset)); |
|
|
|
|
|
|
|
|
|
|
|
// Check whether it is possible to omit the write barrier. If the elements
|
|
|
|
|
|
// array is in new space or the value written is a smi we can safely update
|
|
|
|
|
|
// the elements array without updating the remembered set.
|
|
|
|
|
|
Label in_new_space; |
|
|
|
|
|
__ InNewSpace(tmp.reg(), tmp2.reg(), equal, &in_new_space); |
|
|
|
|
|
if (!value_is_constant) { |
|
|
|
|
|
__ test(result.reg(), Immediate(kSmiTagMask)); |
|
|
|
|
|
deferred->Branch(not_zero); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
__ bind(&in_new_space); |
|
|
// Bind the deferred code patch site to be able to locate the fixed
|
|
|
// Bind the deferred code patch site to be able to locate the fixed
|
|
|
// array map comparison. When debugging, we patch this comparison to
|
|
|
// array map comparison. When debugging, we patch this comparison to
|
|
|
// always fail so that we will hit the IC call in the deferred code
|
|
|
// always fail so that we will hit the IC call in the deferred code
|
|
@ -8819,6 +8994,7 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { |
|
|
__ cmp(ecx, FIRST_NONSTRING_TYPE); |
|
|
__ cmp(ecx, FIRST_NONSTRING_TYPE); |
|
|
__ j(above_equal, ¬_string); |
|
|
__ j(above_equal, ¬_string); |
|
|
__ mov(edx, FieldOperand(eax, String::kLengthOffset)); |
|
|
__ mov(edx, FieldOperand(eax, String::kLengthOffset)); |
|
|
|
|
|
ASSERT(kSmiTag == 0); |
|
|
__ test(edx, Operand(edx)); |
|
|
__ test(edx, Operand(edx)); |
|
|
__ j(zero, &false_result); |
|
|
__ j(zero, &false_result); |
|
|
__ jmp(&true_result); |
|
|
__ jmp(&true_result); |
|
@ -10775,15 +10951,16 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { |
|
|
// Get the length of the string to ebx.
|
|
|
// Get the length of the string to ebx.
|
|
|
__ mov(ebx, FieldOperand(eax, String::kLengthOffset)); |
|
|
__ mov(ebx, FieldOperand(eax, String::kLengthOffset)); |
|
|
|
|
|
|
|
|
// ebx: Length of subject string
|
|
|
// ebx: Length of subject string as a smi
|
|
|
// ecx: RegExp data (FixedArray)
|
|
|
// ecx: RegExp data (FixedArray)
|
|
|
// edx: Number of capture registers
|
|
|
// edx: Number of capture registers
|
|
|
// Check that the third argument is a positive smi less than the subject
|
|
|
// Check that the third argument is a positive smi less than the subject
|
|
|
// string length. A negative value will be greater (unsigned comparison).
|
|
|
// string length. A negative value will be greater (unsigned comparison).
|
|
|
__ mov(eax, Operand(esp, kPreviousIndexOffset)); |
|
|
__ mov(eax, Operand(esp, kPreviousIndexOffset)); |
|
|
__ SmiUntag(eax); |
|
|
__ test(eax, Immediate(kSmiTagMask)); |
|
|
|
|
|
__ j(zero, &runtime); |
|
|
__ cmp(eax, Operand(ebx)); |
|
|
__ cmp(eax, Operand(ebx)); |
|
|
__ j(above, &runtime); |
|
|
__ j(above_equal, &runtime); |
|
|
|
|
|
|
|
|
// ecx: RegExp data (FixedArray)
|
|
|
// ecx: RegExp data (FixedArray)
|
|
|
// edx: Number of capture registers
|
|
|
// edx: Number of capture registers
|
|
@ -10906,6 +11083,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { |
|
|
__ test(edi, Operand(edi)); |
|
|
__ test(edi, Operand(edi)); |
|
|
__ mov(edi, FieldOperand(eax, String::kLengthOffset)); |
|
|
__ mov(edi, FieldOperand(eax, String::kLengthOffset)); |
|
|
__ j(zero, &setup_two_byte); |
|
|
__ j(zero, &setup_two_byte); |
|
|
|
|
|
__ SmiUntag(edi); |
|
|
__ lea(ecx, FieldOperand(eax, edi, times_1, SeqAsciiString::kHeaderSize)); |
|
|
__ lea(ecx, FieldOperand(eax, edi, times_1, SeqAsciiString::kHeaderSize)); |
|
|
__ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
|
|
|
__ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
|
|
|
__ lea(ecx, FieldOperand(eax, ebx, times_1, SeqAsciiString::kHeaderSize)); |
|
|
__ lea(ecx, FieldOperand(eax, ebx, times_1, SeqAsciiString::kHeaderSize)); |
|
@ -10913,7 +11091,8 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { |
|
|
__ jmp(&setup_rest); |
|
|
__ jmp(&setup_rest); |
|
|
|
|
|
|
|
|
__ bind(&setup_two_byte); |
|
|
__ bind(&setup_two_byte); |
|
|
__ lea(ecx, FieldOperand(eax, edi, times_2, SeqTwoByteString::kHeaderSize)); |
|
|
ASSERT(kSmiTag == 0 && kSmiTagSize == 1); // edi is smi (powered by 2).
|
|
|
|
|
|
__ lea(ecx, FieldOperand(eax, edi, times_1, SeqTwoByteString::kHeaderSize)); |
|
|
__ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
|
|
|
__ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
|
|
|
__ lea(ecx, FieldOperand(eax, ebx, times_2, SeqTwoByteString::kHeaderSize)); |
|
|
__ lea(ecx, FieldOperand(eax, ebx, times_2, SeqTwoByteString::kHeaderSize)); |
|
|
__ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
|
|
|
__ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
|
|
@ -12051,7 +12230,8 @@ void StringHelper::GenerateFastCharCodeAt(MacroAssembler* masm, |
|
|
Register scratch, |
|
|
Register scratch, |
|
|
Register result, |
|
|
Register result, |
|
|
Label* receiver_not_string, |
|
|
Label* receiver_not_string, |
|
|
Label* index_not_positive_smi, |
|
|
Label* index_not_smi, |
|
|
|
|
|
Label* index_out_of_range, |
|
|
Label* slow_case) { |
|
|
Label* slow_case) { |
|
|
Label not_a_flat_string; |
|
|
Label not_a_flat_string; |
|
|
Label try_again_with_new_string; |
|
|
Label try_again_with_new_string; |
|
@ -12070,25 +12250,20 @@ void StringHelper::GenerateFastCharCodeAt(MacroAssembler* masm, |
|
|
__ test(result, Immediate(kIsNotStringMask)); |
|
|
__ test(result, Immediate(kIsNotStringMask)); |
|
|
__ j(not_zero, receiver_not_string); |
|
|
__ j(not_zero, receiver_not_string); |
|
|
|
|
|
|
|
|
// If the index is negative or non-smi trigger the non-positive-smi
|
|
|
// If the index is non-smi trigger the non-smi case.
|
|
|
// case.
|
|
|
|
|
|
ASSERT(kSmiTag == 0); |
|
|
ASSERT(kSmiTag == 0); |
|
|
__ test(index, Immediate(kSmiTagMask | kSmiSignMask)); |
|
|
__ test(index, Immediate(kSmiTagMask)); |
|
|
__ j(not_zero, index_not_positive_smi); |
|
|
__ j(not_zero, index_not_smi); |
|
|
|
|
|
|
|
|
// Put untagged index into scratch register.
|
|
|
|
|
|
__ mov(scratch, index); |
|
|
|
|
|
__ SmiUntag(scratch); |
|
|
|
|
|
|
|
|
|
|
|
// Check for index out of range.
|
|
|
// Check for index out of range.
|
|
|
__ cmp(scratch, FieldOperand(object, String::kLengthOffset)); |
|
|
__ cmp(index, FieldOperand(object, String::kLengthOffset)); |
|
|
__ j(greater_equal, slow_case); |
|
|
__ j(above_equal, index_out_of_range); |
|
|
|
|
|
|
|
|
__ bind(&try_again_with_new_string); |
|
|
__ bind(&try_again_with_new_string); |
|
|
// ----------- S t a t e -------------
|
|
|
// ----------- S t a t e -------------
|
|
|
// -- object : string to access
|
|
|
// -- object : string to access
|
|
|
// -- result : instance type of the string
|
|
|
// -- result : instance type of the string
|
|
|
// -- scratch : positive smi index < length
|
|
|
// -- scratch : non-negative index < length
|
|
|
// -----------------------------------
|
|
|
// -----------------------------------
|
|
|
|
|
|
|
|
|
// We need special handling for non-flat strings.
|
|
|
// We need special handling for non-flat strings.
|
|
@ -12102,9 +12277,10 @@ void StringHelper::GenerateFastCharCodeAt(MacroAssembler* masm, |
|
|
__ j(not_zero, &ascii_string); |
|
|
__ j(not_zero, &ascii_string); |
|
|
|
|
|
|
|
|
// 2-byte string.
|
|
|
// 2-byte string.
|
|
|
// Load the 2-byte character code into the temp register.
|
|
|
// Load the 2-byte character code into the result register.
|
|
|
|
|
|
ASSERT(kSmiTag == 0 && kSmiTagSize == 1); // index is smi (powered by 2).
|
|
|
__ movzx_w(result, FieldOperand(object, |
|
|
__ movzx_w(result, FieldOperand(object, |
|
|
scratch, times_2, |
|
|
index, times_1, |
|
|
SeqTwoByteString::kHeaderSize)); |
|
|
SeqTwoByteString::kHeaderSize)); |
|
|
__ jmp(&got_char_code); |
|
|
__ jmp(&got_char_code); |
|
|
|
|
|
|
|
@ -12130,7 +12306,11 @@ void StringHelper::GenerateFastCharCodeAt(MacroAssembler* masm, |
|
|
|
|
|
|
|
|
// ASCII string.
|
|
|
// ASCII string.
|
|
|
__ bind(&ascii_string); |
|
|
__ bind(&ascii_string); |
|
|
// Load the byte into the temp register.
|
|
|
// Put untagged index into scratch register.
|
|
|
|
|
|
__ mov(scratch, index); |
|
|
|
|
|
__ SmiUntag(scratch); |
|
|
|
|
|
|
|
|
|
|
|
// Load the byte into the result register.
|
|
|
__ movzx_b(result, FieldOperand(object, |
|
|
__ movzx_b(result, FieldOperand(object, |
|
|
scratch, times_1, |
|
|
scratch, times_1, |
|
|
SeqAsciiString::kHeaderSize)); |
|
|
SeqAsciiString::kHeaderSize)); |
|
@ -12220,6 +12400,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { |
|
|
// Check if either of the strings are empty. In that case return the other.
|
|
|
// Check if either of the strings are empty. In that case return the other.
|
|
|
Label second_not_zero_length, both_not_zero_length; |
|
|
Label second_not_zero_length, both_not_zero_length; |
|
|
__ mov(ecx, FieldOperand(edx, String::kLengthOffset)); |
|
|
__ mov(ecx, FieldOperand(edx, String::kLengthOffset)); |
|
|
|
|
|
ASSERT(kSmiTag == 0); |
|
|
__ test(ecx, Operand(ecx)); |
|
|
__ test(ecx, Operand(ecx)); |
|
|
__ j(not_zero, &second_not_zero_length); |
|
|
__ j(not_zero, &second_not_zero_length); |
|
|
// Second string is empty, result is first string which is already in eax.
|
|
|
// Second string is empty, result is first string which is already in eax.
|
|
@ -12227,6 +12408,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { |
|
|
__ ret(2 * kPointerSize); |
|
|
__ ret(2 * kPointerSize); |
|
|
__ bind(&second_not_zero_length); |
|
|
__ bind(&second_not_zero_length); |
|
|
__ mov(ebx, FieldOperand(eax, String::kLengthOffset)); |
|
|
__ mov(ebx, FieldOperand(eax, String::kLengthOffset)); |
|
|
|
|
|
ASSERT(kSmiTag == 0); |
|
|
__ test(ebx, Operand(ebx)); |
|
|
__ test(ebx, Operand(ebx)); |
|
|
__ j(not_zero, &both_not_zero_length); |
|
|
__ j(not_zero, &both_not_zero_length); |
|
|
// First string is empty, result is second string which is in edx.
|
|
|
// First string is empty, result is second string which is in edx.
|
|
@ -12236,16 +12418,19 @@ void StringAddStub::Generate(MacroAssembler* masm) { |
|
|
|
|
|
|
|
|
// Both strings are non-empty.
|
|
|
// Both strings are non-empty.
|
|
|
// eax: first string
|
|
|
// eax: first string
|
|
|
// ebx: length of first string
|
|
|
// ebx: length of first string as a smi
|
|
|
// ecx: length of second string
|
|
|
// ecx: length of second string as a smi
|
|
|
// edx: second string
|
|
|
// edx: second string
|
|
|
// Look at the length of the result of adding the two strings.
|
|
|
// Look at the length of the result of adding the two strings.
|
|
|
Label string_add_flat_result, longer_than_two; |
|
|
Label string_add_flat_result, longer_than_two; |
|
|
__ bind(&both_not_zero_length); |
|
|
__ bind(&both_not_zero_length); |
|
|
__ add(ebx, Operand(ecx)); |
|
|
__ add(ebx, Operand(ecx)); |
|
|
|
|
|
ASSERT(Smi::kMaxValue == String::kMaxLength); |
|
|
|
|
|
// Handle exceptionally long strings in the runtime system.
|
|
|
|
|
|
__ j(overflow, &string_add_runtime); |
|
|
// Use the runtime system when adding two one character strings, as it
|
|
|
// Use the runtime system when adding two one character strings, as it
|
|
|
// contains optimizations for this specific case using the symbol table.
|
|
|
// contains optimizations for this specific case using the symbol table.
|
|
|
__ cmp(ebx, 2); |
|
|
__ cmp(Operand(ebx), Immediate(Smi::FromInt(2))); |
|
|
__ j(not_equal, &longer_than_two); |
|
|
__ j(not_equal, &longer_than_two); |
|
|
|
|
|
|
|
|
// Check that both strings are non-external ascii strings.
|
|
|
// Check that both strings are non-external ascii strings.
|
|
@ -12265,17 +12450,13 @@ void StringAddStub::Generate(MacroAssembler* masm) { |
|
|
__ ret(2 * kPointerSize); |
|
|
__ ret(2 * kPointerSize); |
|
|
|
|
|
|
|
|
__ bind(&make_two_character_string); |
|
|
__ bind(&make_two_character_string); |
|
|
__ Set(ebx, Immediate(2)); |
|
|
__ Set(ebx, Immediate(Smi::FromInt(2))); |
|
|
__ jmp(&make_flat_ascii_string); |
|
|
__ jmp(&make_flat_ascii_string); |
|
|
|
|
|
|
|
|
__ bind(&longer_than_two); |
|
|
__ bind(&longer_than_two); |
|
|
// Check if resulting string will be flat.
|
|
|
// Check if resulting string will be flat.
|
|
|
__ cmp(ebx, String::kMinNonFlatLength); |
|
|
__ cmp(Operand(ebx), Immediate(Smi::FromInt(String::kMinNonFlatLength))); |
|
|
__ j(below, &string_add_flat_result); |
|
|
__ j(below, &string_add_flat_result); |
|
|
// Handle exceptionally long strings in the runtime system.
|
|
|
|
|
|
ASSERT((String::kMaxLength & 0x80000000) == 0); |
|
|
|
|
|
__ cmp(ebx, String::kMaxLength); |
|
|
|
|
|
__ j(above, &string_add_runtime); |
|
|
|
|
|
|
|
|
|
|
|
// If result is not supposed to be flat allocate a cons string object. If both
|
|
|
// If result is not supposed to be flat allocate a cons string object. If both
|
|
|
// strings are ascii the result is an ascii cons string.
|
|
|
// strings are ascii the result is an ascii cons string.
|
|
@ -12292,6 +12473,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { |
|
|
__ AllocateAsciiConsString(ecx, edi, no_reg, &string_add_runtime); |
|
|
__ AllocateAsciiConsString(ecx, edi, no_reg, &string_add_runtime); |
|
|
__ bind(&allocated); |
|
|
__ bind(&allocated); |
|
|
// Fill the fields of the cons string.
|
|
|
// Fill the fields of the cons string.
|
|
|
|
|
|
if (FLAG_debug_code) __ AbortIfNotSmi(ebx); |
|
|
__ mov(FieldOperand(ecx, ConsString::kLengthOffset), ebx); |
|
|
__ mov(FieldOperand(ecx, ConsString::kLengthOffset), ebx); |
|
|
__ mov(FieldOperand(ecx, ConsString::kHashFieldOffset), |
|
|
__ mov(FieldOperand(ecx, ConsString::kHashFieldOffset), |
|
|
Immediate(String::kEmptyHashField)); |
|
|
Immediate(String::kEmptyHashField)); |
|
@ -12308,7 +12490,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { |
|
|
// Handle creating a flat result. First check that both strings are not
|
|
|
// Handle creating a flat result. First check that both strings are not
|
|
|
// external strings.
|
|
|
// external strings.
|
|
|
// eax: first string
|
|
|
// eax: first string
|
|
|
// ebx: length of resulting flat string
|
|
|
// ebx: length of resulting flat string as a smi
|
|
|
// edx: second string
|
|
|
// edx: second string
|
|
|
__ bind(&string_add_flat_result); |
|
|
__ bind(&string_add_flat_result); |
|
|
__ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); |
|
|
__ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); |
|
@ -12323,7 +12505,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { |
|
|
__ j(equal, &string_add_runtime); |
|
|
__ j(equal, &string_add_runtime); |
|
|
// Now check if both strings are ascii strings.
|
|
|
// Now check if both strings are ascii strings.
|
|
|
// eax: first string
|
|
|
// eax: first string
|
|
|
// ebx: length of resulting flat string
|
|
|
// ebx: length of resulting flat string as a smi
|
|
|
// edx: second string
|
|
|
// edx: second string
|
|
|
Label non_ascii_string_add_flat_result; |
|
|
Label non_ascii_string_add_flat_result; |
|
|
__ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); |
|
|
__ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); |
|
@ -12338,7 +12520,8 @@ void StringAddStub::Generate(MacroAssembler* masm) { |
|
|
|
|
|
|
|
|
__ bind(&make_flat_ascii_string); |
|
|
__ bind(&make_flat_ascii_string); |
|
|
// Both strings are ascii strings. As they are short they are both flat.
|
|
|
// Both strings are ascii strings. As they are short they are both flat.
|
|
|
// ebx: length of resulting flat string
|
|
|
// ebx: length of resulting flat string as a smi
|
|
|
|
|
|
__ SmiUntag(ebx); |
|
|
__ AllocateAsciiString(eax, ebx, ecx, edx, edi, &string_add_runtime); |
|
|
__ AllocateAsciiString(eax, ebx, ecx, edx, edi, &string_add_runtime); |
|
|
// eax: result string
|
|
|
// eax: result string
|
|
|
__ mov(ecx, eax); |
|
|
__ mov(ecx, eax); |
|
@ -12347,6 +12530,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { |
|
|
// Load first argument and locate first character.
|
|
|
// Load first argument and locate first character.
|
|
|
__ mov(edx, Operand(esp, 2 * kPointerSize)); |
|
|
__ mov(edx, Operand(esp, 2 * kPointerSize)); |
|
|
__ mov(edi, FieldOperand(edx, String::kLengthOffset)); |
|
|
__ mov(edi, FieldOperand(edx, String::kLengthOffset)); |
|
|
|
|
|
__ SmiUntag(edi); |
|
|
__ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
|
|
__ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
|
|
// eax: result string
|
|
|
// eax: result string
|
|
|
// ecx: first character of result
|
|
|
// ecx: first character of result
|
|
@ -12356,6 +12540,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { |
|
|
// Load second argument and locate first character.
|
|
|
// Load second argument and locate first character.
|
|
|
__ mov(edx, Operand(esp, 1 * kPointerSize)); |
|
|
__ mov(edx, Operand(esp, 1 * kPointerSize)); |
|
|
__ mov(edi, FieldOperand(edx, String::kLengthOffset)); |
|
|
__ mov(edi, FieldOperand(edx, String::kLengthOffset)); |
|
|
|
|
|
__ SmiUntag(edi); |
|
|
__ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
|
|
__ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
|
|
// eax: result string
|
|
|
// eax: result string
|
|
|
// ecx: next character of result
|
|
|
// ecx: next character of result
|
|
@ -12367,7 +12552,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { |
|
|
|
|
|
|
|
|
// Handle creating a flat two byte result.
|
|
|
// Handle creating a flat two byte result.
|
|
|
// eax: first string - known to be two byte
|
|
|
// eax: first string - known to be two byte
|
|
|
// ebx: length of resulting flat string
|
|
|
// ebx: length of resulting flat string as a smi
|
|
|
// edx: second string
|
|
|
// edx: second string
|
|
|
__ bind(&non_ascii_string_add_flat_result); |
|
|
__ bind(&non_ascii_string_add_flat_result); |
|
|
__ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); |
|
|
__ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); |
|
@ -12376,6 +12561,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { |
|
|
__ j(not_zero, &string_add_runtime); |
|
|
__ j(not_zero, &string_add_runtime); |
|
|
// Both strings are two byte strings. As they are short they are both
|
|
|
// Both strings are two byte strings. As they are short they are both
|
|
|
// flat.
|
|
|
// flat.
|
|
|
|
|
|
__ SmiUntag(ebx); |
|
|
__ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &string_add_runtime); |
|
|
__ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &string_add_runtime); |
|
|
// eax: result string
|
|
|
// eax: result string
|
|
|
__ mov(ecx, eax); |
|
|
__ mov(ecx, eax); |
|
@ -12385,6 +12571,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { |
|
|
// Load first argument and locate first character.
|
|
|
// Load first argument and locate first character.
|
|
|
__ mov(edx, Operand(esp, 2 * kPointerSize)); |
|
|
__ mov(edx, Operand(esp, 2 * kPointerSize)); |
|
|
__ mov(edi, FieldOperand(edx, String::kLengthOffset)); |
|
|
__ mov(edi, FieldOperand(edx, String::kLengthOffset)); |
|
|
|
|
|
__ SmiUntag(edi); |
|
|
__ add(Operand(edx), |
|
|
__ add(Operand(edx), |
|
|
Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); |
|
|
Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); |
|
|
// eax: result string
|
|
|
// eax: result string
|
|
@ -12395,6 +12582,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { |
|
|
// Load second argument and locate first character.
|
|
|
// Load second argument and locate first character.
|
|
|
__ mov(edx, Operand(esp, 1 * kPointerSize)); |
|
|
__ mov(edx, Operand(esp, 1 * kPointerSize)); |
|
|
__ mov(edi, FieldOperand(edx, String::kLengthOffset)); |
|
|
__ mov(edi, FieldOperand(edx, String::kLengthOffset)); |
|
|
|
|
|
__ SmiUntag(edi); |
|
|
__ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
|
|
__ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
|
|
// eax: result string
|
|
|
// eax: result string
|
|
|
// ecx: next character of result
|
|
|
// ecx: next character of result
|
|
@ -12579,7 +12767,8 @@ void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm, |
|
|
__ j(equal, not_found); |
|
|
__ j(equal, not_found); |
|
|
|
|
|
|
|
|
// If length is not 2 the string is not a candidate.
|
|
|
// If length is not 2 the string is not a candidate.
|
|
|
__ cmp(FieldOperand(candidate, String::kLengthOffset), Immediate(2)); |
|
|
__ cmp(FieldOperand(candidate, String::kLengthOffset), |
|
|
|
|
|
Immediate(Smi::FromInt(2))); |
|
|
__ j(not_equal, &next_probe[i]); |
|
|
__ j(not_equal, &next_probe[i]); |
|
|
|
|
|
|
|
|
// As we are out of registers save the mask on the stack and use that
|
|
|
// As we are out of registers save the mask on the stack and use that
|
|
@ -12848,6 +13037,7 @@ void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm, |
|
|
// Change index to run from -min_length to -1 by adding min_length
|
|
|
// Change index to run from -min_length to -1 by adding min_length
|
|
|
// to string start. This means that loop ends when index reaches zero,
|
|
|
// to string start. This means that loop ends when index reaches zero,
|
|
|
// which doesn't need an additional compare.
|
|
|
// which doesn't need an additional compare.
|
|
|
|
|
|
__ SmiUntag(min_length); |
|
|
__ lea(left, |
|
|
__ lea(left, |
|
|
FieldOperand(left, |
|
|
FieldOperand(left, |
|
|
min_length, times_1, |
|
|
min_length, times_1, |
|
|