Index: src/x64/full-codegen-x64.cc |
=================================================================== |
--- src/x64/full-codegen-x64.cc (revision 9531) |
+++ src/x64/full-codegen-x64.cc (working copy) |
@@ -147,6 +147,11 @@ |
__ bind(&ok); |
} |
+ // Open a frame scope to indicate that there is a frame on the stack. The |
+ // MANUAL indicates that the scope shouldn't actually generate code to set up |
+ // the frame (that is done below). |
+ FrameScope frame_scope(masm_, StackFrame::MANUAL); |
+ |
__ push(rbp); // Caller's frame pointer. |
__ movq(rbp, rsp); |
__ push(rsi); // Callee's context. |
@@ -195,11 +200,9 @@ |
// Store it in the context. |
int context_offset = Context::SlotOffset(var->index()); |
__ movq(Operand(rsi, context_offset), rax); |
- // Update the write barrier. This clobbers all involved |
- // registers, so we have use a third register to avoid |
- // clobbering rsi. |
- __ movq(rcx, rsi); |
- __ RecordWrite(rcx, context_offset, rax, rbx); |
+ // Update the write barrier. This clobbers rax and rbx. |
+ __ RecordWriteContextSlot( |
+ rsi, context_offset, rax, rbx, kDontSaveFPRegs); |
} |
} |
} |
@@ -638,10 +641,11 @@ |
ASSERT(!scratch1.is(src)); |
MemOperand location = VarOperand(var, scratch0); |
__ movq(location, src); |
+ |
// Emit the write barrier code if the location is in the heap. |
if (var->IsContextSlot()) { |
int offset = Context::SlotOffset(var->index()); |
- __ RecordWrite(scratch0, offset, src, scratch1); |
+ __ RecordWriteContextSlot(scratch0, offset, src, scratch1, kDontSaveFPRegs); |
} |
} |
@@ -715,8 +719,14 @@ |
VisitForAccumulatorValue(function); |
__ movq(ContextOperand(rsi, variable->index()), result_register()); |
int offset = Context::SlotOffset(variable->index()); |
- __ movq(rbx, rsi); |
- __ RecordWrite(rbx, offset, result_register(), rcx); |
+ // We know that we have written a function, which is not a smi. |
+ __ RecordWriteContextSlot(rsi, |
+ offset, |
+ result_register(), |
+ rcx, |
+ kDontSaveFPRegs, |
+ EMIT_REMEMBERED_SET, |
+ OMIT_SMI_CHECK); |
PrepareForBailoutForId(proxy->id(), NO_REGISTERS); |
} else if (mode == Variable::CONST || mode == Variable::LET) { |
Comment cmnt(masm_, "[ Declaration"); |
@@ -1445,13 +1455,25 @@ |
VisitForAccumulatorValue(subexpr); |
// Store the subexpression value in the array's elements. |
- __ movq(rbx, Operand(rsp, 0)); // Copy of array literal. |
- __ movq(rbx, FieldOperand(rbx, JSObject::kElementsOffset)); |
+ __ movq(r8, Operand(rsp, 0)); // Copy of array literal. |
+ __ movq(rbx, FieldOperand(r8, JSObject::kElementsOffset)); |
int offset = FixedArray::kHeaderSize + (i * kPointerSize); |
__ movq(FieldOperand(rbx, offset), result_register()); |
+ Label no_map_change; |
+ __ JumpIfSmi(result_register(), &no_map_change); |
// Update the write barrier for the array store. |
- __ RecordWrite(rbx, offset, result_register(), rcx); |
+ __ RecordWriteField(rbx, offset, result_register(), rcx, |
+ kDontSaveFPRegs, |
+ EMIT_REMEMBERED_SET, |
+ OMIT_SMI_CHECK); |
+ if (FLAG_smi_only_arrays) { |
+ __ movq(rdi, FieldOperand(rbx, JSObject::kMapOffset)); |
+ __ CheckFastSmiOnlyElements(rdi, &no_map_change, Label::kNear); |
+ __ push(r8); |
+ __ CallRuntime(Runtime::kNonSmiElementStored, 1); |
+ } |
+ __ bind(&no_map_change); |
PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS); |
} |
@@ -1777,7 +1799,8 @@ |
__ movq(location, rax); |
if (var->IsContextSlot()) { |
__ movq(rdx, rax); |
- __ RecordWrite(rcx, Context::SlotOffset(var->index()), rdx, rbx); |
+ __ RecordWriteContextSlot( |
+ rcx, Context::SlotOffset(var->index()), rdx, rbx, kDontSaveFPRegs); |
} |
} |
@@ -1795,7 +1818,8 @@ |
__ movq(location, rax); |
if (var->IsContextSlot()) { |
__ movq(rdx, rax); |
- __ RecordWrite(rcx, Context::SlotOffset(var->index()), rdx, rbx); |
+ __ RecordWriteContextSlot( |
+ rcx, Context::SlotOffset(var->index()), rdx, rbx, kDontSaveFPRegs); |
} |
} else { |
ASSERT(var->IsLookupSlot()); |
@@ -2545,20 +2569,24 @@ |
// Check that the object is a JS object but take special care of JS |
// functions to make sure they have 'Function' as their class. |
+ // Assume that there are only two callable types, and one of them is at |
+ // either end of the type range for JS object types. Saves extra comparisons. |
+ STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); |
__ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rax); |
// Map is now in rax. |
__ j(below, &null); |
+ STATIC_ASSERT(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE == |
+ FIRST_SPEC_OBJECT_TYPE + 1); |
+ __ j(equal, &function); |
- // As long as LAST_CALLABLE_SPEC_OBJECT_TYPE is the last instance type, and |
- // FIRST_CALLABLE_SPEC_OBJECT_TYPE comes right after |
- // LAST_NONCALLABLE_SPEC_OBJECT_TYPE, we can avoid checking for the latter. |
- STATIC_ASSERT(LAST_TYPE == LAST_CALLABLE_SPEC_OBJECT_TYPE); |
- STATIC_ASSERT(FIRST_CALLABLE_SPEC_OBJECT_TYPE == |
- LAST_NONCALLABLE_SPEC_OBJECT_TYPE + 1); |
- __ CmpInstanceType(rax, FIRST_CALLABLE_SPEC_OBJECT_TYPE); |
- __ j(above_equal, &function); |
+ __ CmpInstanceType(rax, LAST_SPEC_OBJECT_TYPE); |
+ STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == |
+ LAST_SPEC_OBJECT_TYPE - 1); |
+ __ j(equal, &function); |
+ // Assume that there is no larger type. |
+ STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == LAST_TYPE - 1); |
- // Check if the constructor in the map is a function. |
+ // Check if the constructor in the map is a JS function. |
__ movq(rax, FieldOperand(rax, Map::kConstructorOffset)); |
__ CmpObjectType(rax, JS_FUNCTION_TYPE, rbx); |
__ j(not_equal, &non_function_constructor); |
@@ -2726,7 +2754,7 @@ |
// Update the write barrier. Save the value as it will be |
// overwritten by the write barrier code and is needed afterward. |
__ movq(rdx, rax); |
- __ RecordWrite(rbx, JSValue::kValueOffset, rdx, rcx); |
+ __ RecordWriteField(rbx, JSValue::kValueOffset, rdx, rcx, kDontSaveFPRegs); |
__ bind(&done); |
context()->Plug(rax); |
@@ -3010,14 +3038,33 @@ |
__ movq(Operand(index_2, 0), object); |
__ movq(Operand(index_1, 0), temp); |
- Label new_space; |
- __ InNewSpace(elements, temp, equal, &new_space); |
+ Label no_remembered_set; |
+ __ CheckPageFlag(elements, |
+ temp, |
+ 1 << MemoryChunk::SCAN_ON_SCAVENGE, |
+ not_zero, |
+ &no_remembered_set, |
+ Label::kNear); |
+ // Possible optimization: do a check that both values are Smis |
+ // (or them and test against Smi mask.) |
- __ movq(object, elements); |
- __ RecordWriteHelper(object, index_1, temp); |
- __ RecordWriteHelper(elements, index_2, temp); |
+ // We are swapping two objects in an array and the incremental marker never |
+ // pauses in the middle of scanning a single object. Therefore the |
+ // incremental marker is not disturbed, so we don't need to call the |
+ // RecordWrite stub that notifies the incremental marker. |
+ __ RememberedSetHelper(elements, |
+ index_1, |
+ temp, |
+ kDontSaveFPRegs, |
+ MacroAssembler::kFallThroughAtEnd); |
+ __ RememberedSetHelper(elements, |
+ index_2, |
+ temp, |
+ kDontSaveFPRegs, |
+ MacroAssembler::kFallThroughAtEnd); |
- __ bind(&new_space); |
+ __ bind(&no_remembered_set); |
+ |
// We are done. Drop elements from the stack, and return undefined. |
__ addq(rsp, Immediate(3 * kPointerSize)); |
__ LoadRoot(rax, Heap::kUndefinedValueRootIndex); |
@@ -3833,10 +3880,14 @@ |
void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, |
- Handle<String> check, |
- Label* if_true, |
- Label* if_false, |
- Label* fall_through) { |
+ Handle<String> check) { |
+ Label materialize_true, materialize_false; |
+ Label* if_true = NULL; |
+ Label* if_false = NULL; |
+ Label* fall_through = NULL; |
+ context()->PrepareTest(&materialize_true, &materialize_false, |
+ &if_true, &if_false, &fall_through); |
+ |
{ AccumulatorValueContext context(this); |
VisitForTypeofValue(expr); |
} |
@@ -3875,9 +3926,11 @@ |
Split(not_zero, if_true, if_false, fall_through); |
} else if (check->Equals(isolate()->heap()->function_symbol())) { |
__ JumpIfSmi(rax, if_false); |
- STATIC_ASSERT(LAST_CALLABLE_SPEC_OBJECT_TYPE == LAST_TYPE); |
- __ CmpObjectType(rax, FIRST_CALLABLE_SPEC_OBJECT_TYPE, rdx); |
- Split(above_equal, if_true, if_false, fall_through); |
+ STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); |
+ __ CmpObjectType(rax, JS_FUNCTION_TYPE, rdx); |
+ __ j(equal, if_true); |
+ __ CmpInstanceType(rdx, JS_FUNCTION_PROXY_TYPE); |
+ Split(equal, if_true, if_false, fall_through); |
} else if (check->Equals(isolate()->heap()->object_symbol())) { |
__ JumpIfSmi(rax, if_false); |
if (!FLAG_harmony_typeof) { |
@@ -3895,25 +3948,18 @@ |
} else { |
if (if_false != fall_through) __ jmp(if_false); |
} |
+ context()->Plug(if_true, if_false); |
} |
-void FullCodeGenerator::EmitLiteralCompareUndefined(Expression* expr, |
- Label* if_true, |
- Label* if_false, |
- Label* fall_through) { |
- VisitForAccumulatorValue(expr); |
- PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); |
- |
- __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); |
- Split(equal, if_true, if_false, fall_through); |
-} |
- |
- |
void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { |
Comment cmnt(masm_, "[ CompareOperation"); |
SetSourcePosition(expr->position()); |
+ // First we try a fast inlined version of the compare when one of |
+ // the operands is a literal. |
+ if (TryLiteralCompare(expr)) return; |
+ |
// Always perform the comparison for its control flow. Pack the result |
// into the expression's context after the comparison is performed. |
Label materialize_true, materialize_false; |
@@ -3923,13 +3969,6 @@ |
context()->PrepareTest(&materialize_true, &materialize_false, |
&if_true, &if_false, &fall_through); |
- // First we try a fast inlined version of the compare when one of |
- // the operands is a literal. |
- if (TryLiteralCompare(expr, if_true, if_false, fall_through)) { |
- context()->Plug(if_true, if_false); |
- return; |
- } |
- |
Token::Value op = expr->op(); |
VisitForStackValue(expr->left()); |
switch (op) { |
@@ -3957,7 +3996,6 @@ |
Condition cc = no_condition; |
switch (op) { |
case Token::EQ_STRICT: |
- // Fall through. |
case Token::EQ: |
cc = equal; |
__ pop(rdx); |
@@ -4018,8 +4056,9 @@ |
} |
-void FullCodeGenerator::VisitCompareToNull(CompareToNull* expr) { |
- Comment cmnt(masm_, "[ CompareToNull"); |
+void FullCodeGenerator::EmitLiteralCompareNil(CompareOperation* expr, |
+ Expression* sub_expr, |
+ NilValue nil) { |
Label materialize_true, materialize_false; |
Label* if_true = NULL; |
Label* if_false = NULL; |
@@ -4027,14 +4066,20 @@ |
context()->PrepareTest(&materialize_true, &materialize_false, |
&if_true, &if_false, &fall_through); |
- VisitForAccumulatorValue(expr->expression()); |
+ VisitForAccumulatorValue(sub_expr); |
PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); |
- __ CompareRoot(rax, Heap::kNullValueRootIndex); |
- if (expr->is_strict()) { |
+ Heap::RootListIndex nil_value = nil == kNullValue ? |
+ Heap::kNullValueRootIndex : |
+ Heap::kUndefinedValueRootIndex; |
+ __ CompareRoot(rax, nil_value); |
+ if (expr->op() == Token::EQ_STRICT) { |
Split(equal, if_true, if_false, fall_through); |
} else { |
+ Heap::RootListIndex other_nil_value = nil == kNullValue ? |
+ Heap::kUndefinedValueRootIndex : |
+ Heap::kNullValueRootIndex; |
__ j(equal, if_true); |
- __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); |
+ __ CompareRoot(rax, other_nil_value); |
__ j(equal, if_true); |
__ JumpIfSmi(rax, if_false); |
// It can be an undetectable object. |