| 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.
|
|
|