| Index: src/x64/lithium-codegen-x64.cc
|
| ===================================================================
|
| --- src/x64/lithium-codegen-x64.cc (revision 9531)
|
| +++ src/x64/lithium-codegen-x64.cc (working copy)
|
| @@ -81,6 +81,12 @@
|
| HPhase phase("Code generation", chunk());
|
| ASSERT(is_unused());
|
| status_ = GENERATING;
|
| +
|
| + // 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 in GeneratePrologue).
|
| + FrameScope frame_scope(masm_, StackFrame::MANUAL);
|
| +
|
| return GeneratePrologue() &&
|
| GenerateBody() &&
|
| GenerateDeferredCode() &&
|
| @@ -217,11 +223,8 @@
|
| // 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, kSaveFPRegs);
|
| }
|
| }
|
| Comment(";;; End allocate local context");
|
| @@ -280,6 +283,9 @@
|
| for (int i = 0; !is_aborted() && i < deferred_.length(); i++) {
|
| LDeferredCode* code = deferred_[i];
|
| __ bind(code->entry());
|
| + Comment(";;; Deferred code @%d: %s.",
|
| + code->instruction_index(),
|
| + code->instr()->Mnemonic());
|
| code->Generate();
|
| __ jmp(code->exit());
|
| }
|
| @@ -667,7 +673,7 @@
|
| int deoptimization_index) {
|
| ASSERT(kind == expected_safepoint_kind_);
|
|
|
| - const ZoneList<LOperand*>* operands = pointers->operands();
|
| + const ZoneList<LOperand*>* operands = pointers->GetNormalizedOperands();
|
|
|
| Safepoint safepoint = safepoints_.DefineSafepoint(masm(),
|
| kind, arguments, deoptimization_index);
|
| @@ -1577,30 +1583,33 @@
|
| }
|
|
|
|
|
| -void LCodeGen::DoIsNullAndBranch(LIsNullAndBranch* instr) {
|
| +void LCodeGen::DoIsNilAndBranch(LIsNilAndBranch* instr) {
|
| Register reg = ToRegister(instr->InputAt(0));
|
| -
|
| int false_block = chunk_->LookupDestination(instr->false_block_id());
|
|
|
| + // If the expression is known to be untagged or a smi, then it's definitely
|
| + // not null, and it can't be a an undetectable object.
|
| if (instr->hydrogen()->representation().IsSpecialization() ||
|
| instr->hydrogen()->type().IsSmi()) {
|
| - // If the expression is known to untagged or smi, then it's definitely
|
| - // not null, and it can't be a an undetectable object.
|
| - // Jump directly to the false block.
|
| EmitGoto(false_block);
|
| return;
|
| }
|
|
|
| int true_block = chunk_->LookupDestination(instr->true_block_id());
|
| -
|
| - __ CompareRoot(reg, Heap::kNullValueRootIndex);
|
| - if (instr->is_strict()) {
|
| + Heap::RootListIndex nil_value = instr->nil() == kNullValue ?
|
| + Heap::kNullValueRootIndex :
|
| + Heap::kUndefinedValueRootIndex;
|
| + __ CompareRoot(reg, nil_value);
|
| + if (instr->kind() == kStrictEquality) {
|
| EmitBranch(true_block, false_block, equal);
|
| } else {
|
| + Heap::RootListIndex other_nil_value = instr->nil() == kNullValue ?
|
| + Heap::kUndefinedValueRootIndex :
|
| + Heap::kNullValueRootIndex;
|
| Label* true_label = chunk_->GetAssemblyLabel(true_block);
|
| Label* false_label = chunk_->GetAssemblyLabel(false_block);
|
| __ j(equal, true_label);
|
| - __ CompareRoot(reg, Heap::kUndefinedValueRootIndex);
|
| + __ CompareRoot(reg, other_nil_value);
|
| __ j(equal, true_label);
|
| __ JumpIfSmi(reg, false_label);
|
| // Check for undetectable objects by looking in the bit field in
|
| @@ -1752,30 +1761,40 @@
|
| Label* is_false,
|
| Handle<String> class_name,
|
| Register input,
|
| - Register temp) {
|
| + Register temp,
|
| + Register scratch) {
|
| __ JumpIfSmi(input, is_false);
|
| - __ CmpObjectType(input, FIRST_SPEC_OBJECT_TYPE, temp);
|
| - __ j(below, is_false);
|
|
|
| - // Map is now in temp.
|
| - // Functions have class 'Function'.
|
| - __ CmpInstanceType(temp, FIRST_CALLABLE_SPEC_OBJECT_TYPE);
|
| if (class_name->IsEqualTo(CStrVector("Function"))) {
|
| - __ j(above_equal, is_true);
|
| + // Assuming the following assertions, we can use the same compares to test
|
| + // for both being a function type and being in the object type range.
|
| + STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2);
|
| + STATIC_ASSERT(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE ==
|
| + FIRST_SPEC_OBJECT_TYPE + 1);
|
| + STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE ==
|
| + LAST_SPEC_OBJECT_TYPE - 1);
|
| + STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE);
|
| + __ CmpObjectType(input, FIRST_SPEC_OBJECT_TYPE, temp);
|
| + __ j(below, is_false);
|
| + __ j(equal, is_true);
|
| + __ CmpInstanceType(temp, LAST_SPEC_OBJECT_TYPE);
|
| + __ j(equal, is_true);
|
| } else {
|
| - __ j(above_equal, is_false);
|
| + // Faster code path to avoid two compares: subtract lower bound from the
|
| + // actual type and do a signed compare with the width of the type range.
|
| + __ movq(temp, FieldOperand(input, HeapObject::kMapOffset));
|
| + __ movq(scratch, FieldOperand(temp, Map::kInstanceTypeOffset));
|
| + __ subb(scratch, Immediate(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE));
|
| + __ cmpb(scratch,
|
| + Immediate(static_cast<int8_t>(LAST_NONCALLABLE_SPEC_OBJECT_TYPE -
|
| + FIRST_NONCALLABLE_SPEC_OBJECT_TYPE)));
|
| + __ j(above, is_false);
|
| }
|
|
|
| + // Now we are in the FIRST-LAST_NONCALLABLE_SPEC_OBJECT_TYPE range.
|
| // Check if the constructor in the map is a function.
|
| __ movq(temp, FieldOperand(temp, Map::kConstructorOffset));
|
|
|
| - // As long as LAST_CALLABLE_SPEC_OBJECT_TYPE is the last 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);
|
| -
|
| // Objects with a non-function constructor have class 'Object'.
|
| __ CmpObjectType(temp, JS_FUNCTION_TYPE, kScratchRegister);
|
| if (class_name->IsEqualTo(CStrVector("Object"))) {
|
| @@ -1804,6 +1823,7 @@
|
| void LCodeGen::DoClassOfTestAndBranch(LClassOfTestAndBranch* instr) {
|
| Register input = ToRegister(instr->InputAt(0));
|
| Register temp = ToRegister(instr->TempAt(0));
|
| + Register temp2 = ToRegister(instr->TempAt(1));
|
| Handle<String> class_name = instr->hydrogen()->class_name();
|
|
|
| int true_block = chunk_->LookupDestination(instr->true_block_id());
|
| @@ -1812,7 +1832,7 @@
|
| Label* true_label = chunk_->GetAssemblyLabel(true_block);
|
| Label* false_label = chunk_->GetAssemblyLabel(false_block);
|
|
|
| - EmitClassOfTest(true_label, false_label, class_name, input, temp);
|
| + EmitClassOfTest(true_label, false_label, class_name, input, temp, temp2);
|
|
|
| EmitBranch(true_block, false_block, equal);
|
| }
|
| @@ -1853,9 +1873,8 @@
|
| virtual void Generate() {
|
| codegen()->DoDeferredLInstanceOfKnownGlobal(instr_, &map_check_);
|
| }
|
| -
|
| + virtual LInstruction* instr() { return instr_; }
|
| Label* map_check() { return &map_check_; }
|
| -
|
| private:
|
| LInstanceOfKnownGlobal* instr_;
|
| Label map_check_;
|
| @@ -1996,7 +2015,7 @@
|
| __ movq(result, instr->hydrogen()->cell(), RelocInfo::GLOBAL_PROPERTY_CELL);
|
| __ movq(result, Operand(result, 0));
|
| }
|
| - if (instr->hydrogen()->check_hole_value()) {
|
| + if (instr->hydrogen()->RequiresHoleCheck()) {
|
| __ CompareRoot(result, Heap::kTheHoleValueRootIndex);
|
| DeoptimizeIf(equal, instr->environment());
|
| }
|
| @@ -2016,25 +2035,39 @@
|
|
|
|
|
| void LCodeGen::DoStoreGlobalCell(LStoreGlobalCell* instr) {
|
| + Register object = ToRegister(instr->TempAt(0));
|
| + Register address = ToRegister(instr->TempAt(1));
|
| Register value = ToRegister(instr->InputAt(0));
|
| - Register temp = ToRegister(instr->TempAt(0));
|
| - ASSERT(!value.is(temp));
|
| - bool check_hole = instr->hydrogen()->check_hole_value();
|
| - if (!check_hole && value.is(rax)) {
|
| - __ store_rax(instr->hydrogen()->cell().location(),
|
| - RelocInfo::GLOBAL_PROPERTY_CELL);
|
| - return;
|
| - }
|
| + ASSERT(!value.is(object));
|
| + Handle<JSGlobalPropertyCell> cell_handle(instr->hydrogen()->cell());
|
| +
|
| + __ movq(address, cell_handle, RelocInfo::GLOBAL_PROPERTY_CELL);
|
| +
|
| // If the cell we are storing to contains the hole it could have
|
| // been deleted from the property dictionary. In that case, we need
|
| // to update the property details in the property dictionary to mark
|
| // it as no longer deleted. We deoptimize in that case.
|
| - __ movq(temp, instr->hydrogen()->cell(), RelocInfo::GLOBAL_PROPERTY_CELL);
|
| - if (check_hole) {
|
| - __ CompareRoot(Operand(temp, 0), Heap::kTheHoleValueRootIndex);
|
| + if (instr->hydrogen()->RequiresHoleCheck()) {
|
| + __ CompareRoot(Operand(address, 0), Heap::kTheHoleValueRootIndex);
|
| DeoptimizeIf(equal, instr->environment());
|
| }
|
| - __ movq(Operand(temp, 0), value);
|
| +
|
| + // Store the value.
|
| + __ movq(Operand(address, 0), value);
|
| +
|
| + Label smi_store;
|
| + __ JumpIfSmi(value, &smi_store, Label::kNear);
|
| +
|
| + int offset = JSGlobalPropertyCell::kValueOffset - kHeapObjectTag;
|
| + __ lea(object, Operand(address, -offset));
|
| + // Cells are always in the remembered set.
|
| + __ RecordWrite(object,
|
| + address,
|
| + value,
|
| + kSaveFPRegs,
|
| + OMIT_REMEMBERED_SET,
|
| + OMIT_SMI_CHECK);
|
| + __ bind(&smi_store);
|
| }
|
|
|
|
|
| @@ -2064,7 +2097,7 @@
|
| if (instr->needs_write_barrier()) {
|
| int offset = Context::SlotOffset(instr->slot_index());
|
| Register scratch = ToRegister(instr->TempAt(0));
|
| - __ RecordWrite(context, offset, value, scratch);
|
| + __ RecordWriteContextSlot(context, offset, value, scratch, kSaveFPRegs);
|
| }
|
| }
|
|
|
| @@ -2283,17 +2316,15 @@
|
| LLoadKeyedFastDoubleElement* instr) {
|
| XMMRegister result(ToDoubleRegister(instr->result()));
|
|
|
| - if (instr->hydrogen()->RequiresHoleCheck()) {
|
| - int offset = FixedDoubleArray::kHeaderSize - kHeapObjectTag +
|
| - sizeof(kHoleNanLower32);
|
| - Operand hole_check_operand = BuildFastArrayOperand(
|
| - instr->elements(),
|
| - instr->key(),
|
| - FAST_DOUBLE_ELEMENTS,
|
| - offset);
|
| - __ cmpl(hole_check_operand, Immediate(kHoleNanUpper32));
|
| - DeoptimizeIf(equal, instr->environment());
|
| - }
|
| + int offset = FixedDoubleArray::kHeaderSize - kHeapObjectTag +
|
| + sizeof(kHoleNanLower32);
|
| + Operand hole_check_operand = BuildFastArrayOperand(
|
| + instr->elements(),
|
| + instr->key(),
|
| + FAST_DOUBLE_ELEMENTS,
|
| + offset);
|
| + __ cmpl(hole_check_operand, Immediate(kHoleNanUpper32));
|
| + DeoptimizeIf(equal, instr->environment());
|
|
|
| Operand double_load_operand = BuildFastArrayOperand(
|
| instr->elements(), instr->key(), FAST_DOUBLE_ELEMENTS,
|
| @@ -2365,6 +2396,7 @@
|
| case EXTERNAL_FLOAT_ELEMENTS:
|
| case EXTERNAL_DOUBLE_ELEMENTS:
|
| case FAST_ELEMENTS:
|
| + case FAST_SMI_ONLY_ELEMENTS:
|
| case FAST_DOUBLE_ELEMENTS:
|
| case DICTIONARY_ELEMENTS:
|
| case NON_STRICT_ARGUMENTS_ELEMENTS:
|
| @@ -2681,6 +2713,7 @@
|
| virtual void Generate() {
|
| codegen()->DoDeferredMathAbsTaggedHeapNumber(instr_);
|
| }
|
| + virtual LInstruction* instr() { return instr_; }
|
| private:
|
| LUnaryMathOperation* instr_;
|
| };
|
| @@ -2977,7 +3010,7 @@
|
| ASSERT(ToRegister(instr->result()).is(rax));
|
|
|
| int arity = instr->arity();
|
| - CallFunctionStub stub(arity, RECEIVER_MIGHT_BE_IMPLICIT);
|
| + CallFunctionStub stub(arity, NO_CALL_FUNCTION_FLAGS);
|
| CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
|
| __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
|
| __ Drop(1);
|
| @@ -3033,7 +3066,7 @@
|
| if (instr->needs_write_barrier()) {
|
| Register temp = ToRegister(instr->TempAt(0));
|
| // Update the write barrier for the object for in-object properties.
|
| - __ RecordWrite(object, offset, value, temp);
|
| + __ RecordWriteField(object, offset, value, temp, kSaveFPRegs);
|
| }
|
| } else {
|
| Register temp = ToRegister(instr->TempAt(0));
|
| @@ -3042,7 +3075,7 @@
|
| if (instr->needs_write_barrier()) {
|
| // Update the write barrier for the properties array.
|
| // object is used as a scratch register.
|
| - __ RecordWrite(temp, offset, value, object);
|
| + __ RecordWriteField(temp, offset, value, object, kSaveFPRegs);
|
| }
|
| }
|
| }
|
| @@ -3090,6 +3123,7 @@
|
| case EXTERNAL_FLOAT_ELEMENTS:
|
| case EXTERNAL_DOUBLE_ELEMENTS:
|
| case FAST_ELEMENTS:
|
| + case FAST_SMI_ONLY_ELEMENTS:
|
| case FAST_DOUBLE_ELEMENTS:
|
| case DICTIONARY_ELEMENTS:
|
| case NON_STRICT_ARGUMENTS_ELEMENTS:
|
| @@ -3125,6 +3159,13 @@
|
| Register elements = ToRegister(instr->object());
|
| Register key = instr->key()->IsRegister() ? ToRegister(instr->key()) : no_reg;
|
|
|
| + // This instruction cannot handle the FAST_SMI_ONLY_ELEMENTS -> FAST_ELEMENTS
|
| + // conversion, so it deopts in that case.
|
| + if (instr->hydrogen()->ValueNeedsSmiCheck()) {
|
| + Condition cc = masm()->CheckSmi(value);
|
| + DeoptimizeIf(NegateCondition(cc), instr->environment());
|
| + }
|
| +
|
| // Do the store.
|
| if (instr->key()->IsConstantOperand()) {
|
| ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
|
| @@ -3146,7 +3187,7 @@
|
| key,
|
| times_pointer_size,
|
| FixedArray::kHeaderSize));
|
| - __ RecordWrite(elements, key, value);
|
| + __ RecordWrite(elements, key, value, kSaveFPRegs);
|
| }
|
| }
|
|
|
| @@ -3196,6 +3237,7 @@
|
| DeferredStringCharCodeAt(LCodeGen* codegen, LStringCharCodeAt* instr)
|
| : LDeferredCode(codegen), instr_(instr) { }
|
| virtual void Generate() { codegen()->DoDeferredStringCharCodeAt(instr_); }
|
| + virtual LInstruction* instr() { return instr_; }
|
| private:
|
| LStringCharCodeAt* instr_;
|
| };
|
| @@ -3316,6 +3358,7 @@
|
| DeferredStringCharFromCode(LCodeGen* codegen, LStringCharFromCode* instr)
|
| : LDeferredCode(codegen), instr_(instr) { }
|
| virtual void Generate() { codegen()->DoDeferredStringCharFromCode(instr_); }
|
| + virtual LInstruction* instr() { return instr_; }
|
| private:
|
| LStringCharFromCode* instr_;
|
| };
|
| @@ -3392,6 +3435,7 @@
|
| DeferredNumberTagD(LCodeGen* codegen, LNumberTagD* instr)
|
| : LDeferredCode(codegen), instr_(instr) { }
|
| virtual void Generate() { codegen()->DoDeferredNumberTagD(instr_); }
|
| + virtual LInstruction* instr() { return instr_; }
|
| private:
|
| LNumberTagD* instr_;
|
| };
|
| @@ -3487,16 +3531,6 @@
|
| }
|
|
|
|
|
| -class DeferredTaggedToI: public LDeferredCode {
|
| - public:
|
| - DeferredTaggedToI(LCodeGen* codegen, LTaggedToI* instr)
|
| - : LDeferredCode(codegen), instr_(instr) { }
|
| - virtual void Generate() { codegen()->DoDeferredTaggedToI(instr_); }
|
| - private:
|
| - LTaggedToI* instr_;
|
| -};
|
| -
|
| -
|
| void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
|
| Label done, heap_number;
|
| Register input_reg = ToRegister(instr->InputAt(0));
|
| @@ -3545,6 +3579,16 @@
|
|
|
|
|
| void LCodeGen::DoTaggedToI(LTaggedToI* instr) {
|
| + class DeferredTaggedToI: public LDeferredCode {
|
| + public:
|
| + DeferredTaggedToI(LCodeGen* codegen, LTaggedToI* instr)
|
| + : LDeferredCode(codegen), instr_(instr) { }
|
| + virtual void Generate() { codegen()->DoDeferredTaggedToI(instr_); }
|
| + virtual LInstruction* instr() { return instr_; }
|
| + private:
|
| + LTaggedToI* instr_;
|
| + };
|
| +
|
| LOperand* input = instr->InputAt(0);
|
| ASSERT(input->IsRegister());
|
| ASSERT(input->Equals(instr->result()));
|
| @@ -3981,9 +4025,12 @@
|
| final_branch_condition = not_zero;
|
|
|
| } else if (type_name->Equals(heap()->function_symbol())) {
|
| + STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2);
|
| __ JumpIfSmi(input, false_label);
|
| - __ CmpObjectType(input, FIRST_CALLABLE_SPEC_OBJECT_TYPE, input);
|
| - final_branch_condition = above_equal;
|
| + __ CmpObjectType(input, JS_FUNCTION_TYPE, input);
|
| + __ j(equal, true_label);
|
| + __ CmpInstanceType(input, JS_FUNCTION_PROXY_TYPE);
|
| + final_branch_condition = equal;
|
|
|
| } else if (type_name->Equals(heap()->object_symbol())) {
|
| __ JumpIfSmi(input, false_label);
|
| @@ -4109,6 +4156,7 @@
|
| DeferredStackCheck(LCodeGen* codegen, LStackCheck* instr)
|
| : LDeferredCode(codegen), instr_(instr) { }
|
| virtual void Generate() { codegen()->DoDeferredStackCheck(instr_); }
|
| + virtual LInstruction* instr() { return instr_; }
|
| private:
|
| LStackCheck* instr_;
|
| };
|
|
|