Index: src/ia32/lithium-codegen-ia32.cc |
=================================================================== |
--- src/ia32/lithium-codegen-ia32.cc (revision 9531) |
+++ src/ia32/lithium-codegen-ia32.cc (working copy) |
@@ -70,6 +70,17 @@ |
ASSERT(is_unused()); |
status_ = GENERATING; |
CpuFeatures::Scope scope(SSE2); |
+ |
+ CodeStub::GenerateFPStubs(); |
+ |
+ // 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); |
+ |
+ dynamic_frame_alignment_ = chunk()->num_double_slots() > 2 || |
+ info()->osr_ast_id() != AstNode::kNoNumber; |
+ |
return GeneratePrologue() && |
GenerateBody() && |
GenerateDeferredCode() && |
@@ -144,6 +155,29 @@ |
__ bind(&ok); |
} |
+ if (dynamic_frame_alignment_) { |
+ Label do_not_pad, align_loop; |
+ STATIC_ASSERT(kDoubleSize == 2 * kPointerSize); |
+ // Align esp to a multiple of 2 * kPointerSize. |
+ __ test(esp, Immediate(kPointerSize)); |
+ __ j(zero, &do_not_pad, Label::kNear); |
+ __ push(Immediate(0)); |
+ __ mov(ebx, esp); |
+ // Copy arguments, receiver, and return address. |
+ __ mov(ecx, Immediate(scope()->num_parameters() + 2)); |
+ |
+ __ bind(&align_loop); |
+ __ mov(eax, Operand(ebx, 1 * kPointerSize)); |
+ __ mov(Operand(ebx, 0), eax); |
+ __ add(Operand(ebx), Immediate(kPointerSize)); |
+ __ dec(ecx); |
+ __ j(not_zero, &align_loop, Label::kNear); |
+ __ mov(Operand(ebx, 0), |
+ Immediate(isolate()->factory()->frame_alignment_marker())); |
+ |
+ __ bind(&do_not_pad); |
+ } |
+ |
__ push(ebp); // Caller's frame pointer. |
__ mov(ebp, esp); |
__ push(esi); // Callee's context. |
@@ -204,11 +238,12 @@ |
// Store it in the context. |
int context_offset = Context::SlotOffset(var->index()); |
__ mov(Operand(esi, context_offset), eax); |
- // Update the write barrier. This clobbers all involved |
- // registers, so we have to use a third register to avoid |
- // clobbering esi. |
- __ mov(ecx, esi); |
- __ RecordWrite(ecx, context_offset, eax, ebx); |
+ // Update the write barrier. This clobbers eax and ebx. |
+ __ RecordWriteContextSlot(esi, |
+ context_offset, |
+ eax, |
+ ebx, |
+ kDontSaveFPRegs); |
} |
} |
Comment(";;; End allocate local context"); |
@@ -260,6 +295,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()); |
} |
@@ -481,14 +519,18 @@ |
int argc, |
LInstruction* instr, |
LOperand* context) { |
- ASSERT(context->IsRegister() || context->IsStackSlot()); |
if (context->IsRegister()) { |
if (!ToRegister(context).is(esi)) { |
__ mov(esi, ToRegister(context)); |
} |
+ } else if (context->IsStackSlot()) { |
+ __ mov(esi, ToOperand(context)); |
+ } else if (context->IsConstantOperand()) { |
+ Handle<Object> literal = |
+ chunk_->LookupLiteral(LConstantOperand::cast(context)); |
+ LoadHeapObject(esi, Handle<Context>::cast(literal)); |
} else { |
- // Context is stack slot. |
- __ mov(esi, ToOperand(context)); |
+ UNREACHABLE(); |
} |
__ CallRuntimeSaveDoubles(id); |
@@ -669,7 +711,7 @@ |
int arguments, |
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); |
for (int i = 0; i < operands->length(); i++) { |
@@ -1200,8 +1242,13 @@ |
void LCodeGen::DoConstantT(LConstantT* instr) { |
- ASSERT(instr->result()->IsRegister()); |
- __ Set(ToRegister(instr->result()), Immediate(instr->value())); |
+ Register reg = ToRegister(instr->result()); |
+ Handle<Object> handle = instr->value(); |
+ if (handle->IsHeapObject()) { |
+ LoadHeapObject(reg, Handle<HeapObject>::cast(handle)); |
+ } else { |
+ __ Set(reg, Immediate(handle)); |
+ } |
} |
@@ -1577,23 +1624,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()); |
- // TODO(fsc): If the expression is known to be a smi, then it's |
- // definitely not null. Jump to the false block. |
+ // 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()) { |
+ EmitGoto(false_block); |
+ return; |
+ } |
int true_block = chunk_->LookupDestination(instr->true_block_id()); |
- int false_block = chunk_->LookupDestination(instr->false_block_id()); |
- |
- __ cmp(reg, factory()->null_value()); |
- if (instr->is_strict()) { |
+ Handle<Object> nil_value = instr->nil() == kNullValue ? |
+ factory()->null_value() : |
+ factory()->undefined_value(); |
+ __ cmp(reg, nil_value); |
+ if (instr->kind() == kStrictEquality) { |
EmitBranch(true_block, false_block, equal); |
} else { |
+ Handle<Object> other_nil_value = instr->nil() == kNullValue ? |
+ factory()->undefined_value() : |
+ factory()->null_value(); |
Label* true_label = chunk_->GetAssemblyLabel(true_block); |
Label* false_label = chunk_->GetAssemblyLabel(false_block); |
__ j(equal, true_label); |
- __ cmp(reg, factory()->undefined_value()); |
+ __ cmp(reg, other_nil_value); |
__ j(equal, true_label); |
__ JumpIfSmi(reg, false_label); |
// Check for undetectable objects by looking in the bit field in |
@@ -1745,28 +1802,36 @@ |
ASSERT(!input.is(temp)); |
ASSERT(!temp.is(temp2)); // But input and temp2 may be the same register. |
__ 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. |
+ __ mov(temp, FieldOperand(input, HeapObject::kMapOffset)); |
+ __ mov(temp2, FieldOperand(temp, Map::kInstanceTypeOffset)); |
+ __ sub(Operand(temp2), Immediate(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE)); |
+ __ cmpb(Operand(temp2), |
+ 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. |
__ mov(temp, FieldOperand(temp, Map::kConstructorOffset)); |
- |
- // 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); |
- |
// Objects with a non-function constructor have class 'Object'. |
__ CmpObjectType(temp, JS_FUNCTION_TYPE, temp2); |
if (class_name->IsEqualTo(CStrVector("Object"))) { |
@@ -1851,9 +1916,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_; |
@@ -1991,6 +2055,17 @@ |
} |
__ mov(esp, ebp); |
__ pop(ebp); |
+ if (dynamic_frame_alignment_) { |
+ Label aligned; |
+ // Frame alignment marker (padding) is below arguments, |
+ // and receiver, so its return-address-relative offset is |
+ // (num_arguments + 2) words. |
+ __ cmp(Operand(esp, (GetParameterCount() + 2) * kPointerSize), |
+ Immediate(factory()->frame_alignment_marker())); |
+ __ j(not_equal, &aligned); |
+ __ Ret((GetParameterCount() + 2) * kPointerSize, ecx); |
+ __ bind(&aligned); |
+ } |
__ Ret((GetParameterCount() + 1) * kPointerSize, ecx); |
} |
@@ -1998,7 +2073,7 @@ |
void LCodeGen::DoLoadGlobalCell(LLoadGlobalCell* instr) { |
Register result = ToRegister(instr->result()); |
__ mov(result, Operand::Cell(instr->hydrogen()->cell())); |
- if (instr->hydrogen()->check_hole_value()) { |
+ if (instr->hydrogen()->RequiresHoleCheck()) { |
__ cmp(result, factory()->the_hole_value()); |
DeoptimizeIf(equal, instr->environment()); |
} |
@@ -2019,20 +2094,34 @@ |
void LCodeGen::DoStoreGlobalCell(LStoreGlobalCell* instr) { |
+ Register object = ToRegister(instr->TempAt(0)); |
+ Register address = ToRegister(instr->TempAt(1)); |
Register value = ToRegister(instr->InputAt(0)); |
- Operand cell_operand = Operand::Cell(instr->hydrogen()->cell()); |
+ ASSERT(!value.is(object)); |
+ Handle<JSGlobalPropertyCell> cell_handle(instr->hydrogen()->cell()); |
+ int offset = JSGlobalPropertyCell::kValueOffset; |
+ __ mov(object, Immediate(cell_handle)); |
+ |
// 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. |
- if (instr->hydrogen()->check_hole_value()) { |
- __ cmp(cell_operand, factory()->the_hole_value()); |
+ if (instr->hydrogen()->RequiresHoleCheck()) { |
+ __ cmp(FieldOperand(object, offset), factory()->the_hole_value()); |
DeoptimizeIf(equal, instr->environment()); |
} |
// Store the value. |
- __ mov(cell_operand, value); |
+ __ mov(FieldOperand(object, offset), value); |
+ |
+ // Cells are always in the remembered set. |
+ __ RecordWriteField(object, |
+ offset, |
+ value, |
+ address, |
+ kSaveFPRegs, |
+ OMIT_REMEMBERED_SET); |
} |
@@ -2063,7 +2152,7 @@ |
if (instr->needs_write_barrier()) { |
Register temp = ToRegister(instr->TempAt(0)); |
int offset = Context::SlotOffset(instr->slot_index()); |
- __ RecordWrite(context, offset, value, temp); |
+ __ RecordWriteContextSlot(context, offset, value, temp, kSaveFPRegs); |
} |
} |
@@ -2280,16 +2369,14 @@ |
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); |
- __ cmp(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); |
+ __ cmp(hole_check_operand, Immediate(kHoleNanUpper32)); |
+ DeoptimizeIf(equal, instr->environment()); |
Operand double_load_operand = BuildFastArrayOperand( |
instr->elements(), instr->key(), FAST_DOUBLE_ELEMENTS, |
@@ -2359,6 +2446,7 @@ |
break; |
case EXTERNAL_FLOAT_ELEMENTS: |
case EXTERNAL_DOUBLE_ELEMENTS: |
+ case FAST_SMI_ONLY_ELEMENTS: |
case FAST_ELEMENTS: |
case FAST_DOUBLE_ELEMENTS: |
case DICTIONARY_ELEMENTS: |
@@ -2680,6 +2768,7 @@ |
virtual void Generate() { |
codegen()->DoDeferredMathAbsTaggedHeapNumber(instr_); |
} |
+ virtual LInstruction* instr() { return instr_; } |
private: |
LUnaryMathOperation* instr_; |
}; |
@@ -3005,7 +3094,7 @@ |
ASSERT(ToRegister(instr->result()).is(eax)); |
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); |
__ Drop(1); |
} |
@@ -3062,7 +3151,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)); |
@@ -3071,7 +3160,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); |
} |
} |
} |
@@ -3130,6 +3219,7 @@ |
break; |
case EXTERNAL_FLOAT_ELEMENTS: |
case EXTERNAL_DOUBLE_ELEMENTS: |
+ case FAST_SMI_ONLY_ELEMENTS: |
case FAST_ELEMENTS: |
case FAST_DOUBLE_ELEMENTS: |
case DICTIONARY_ELEMENTS: |
@@ -3146,6 +3236,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()) { |
+ __ test(value, Immediate(kSmiTagMask)); |
+ DeoptimizeIf(not_zero, instr->environment()); |
+ } |
+ |
// Do the store. |
if (instr->key()->IsConstantOperand()) { |
ASSERT(!instr->hydrogen()->NeedsWriteBarrier()); |
@@ -3168,7 +3265,7 @@ |
key, |
times_pointer_size, |
FixedArray::kHeaderSize)); |
- __ RecordWrite(elements, key, value); |
+ __ RecordWrite(elements, key, value, kSaveFPRegs); |
} |
} |
@@ -3212,6 +3309,7 @@ |
DeferredStringCharCodeAt(LCodeGen* codegen, LStringCharCodeAt* instr) |
: LDeferredCode(codegen), instr_(instr) { } |
virtual void Generate() { codegen()->DoDeferredStringCharCodeAt(instr_); } |
+ virtual LInstruction* instr() { return instr_; } |
private: |
LStringCharCodeAt* instr_; |
}; |
@@ -3334,6 +3432,7 @@ |
DeferredStringCharFromCode(LCodeGen* codegen, LStringCharFromCode* instr) |
: LDeferredCode(codegen), instr_(instr) { } |
virtual void Generate() { codegen()->DoDeferredStringCharFromCode(instr_); } |
+ virtual LInstruction* instr() { return instr_; } |
private: |
LStringCharFromCode* instr_; |
}; |
@@ -3413,6 +3512,7 @@ |
DeferredNumberTagI(LCodeGen* codegen, LNumberTagI* instr) |
: LDeferredCode(codegen), instr_(instr) { } |
virtual void Generate() { codegen()->DoDeferredNumberTagI(instr_); } |
+ virtual LInstruction* instr() { return instr_; } |
private: |
LNumberTagI* instr_; |
}; |
@@ -3480,6 +3580,7 @@ |
DeferredNumberTagD(LCodeGen* codegen, LNumberTagD* instr) |
: LDeferredCode(codegen), instr_(instr) { } |
virtual void Generate() { codegen()->DoDeferredNumberTagD(instr_); } |
+ virtual LInstruction* instr() { return instr_; } |
private: |
LNumberTagD* instr_; |
}; |
@@ -3581,16 +3682,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)); |
@@ -3672,6 +3763,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())); |
@@ -3882,9 +3983,16 @@ |
void LCodeGen::DoCheckFunction(LCheckFunction* instr) { |
- ASSERT(instr->InputAt(0)->IsRegister()); |
- Operand operand = ToOperand(instr->InputAt(0)); |
- __ cmp(operand, instr->hydrogen()->target()); |
+ Handle<JSFunction> target = instr->hydrogen()->target(); |
+ if (isolate()->heap()->InNewSpace(*target)) { |
+ Register reg = ToRegister(instr->value()); |
+ Handle<JSGlobalPropertyCell> cell = |
+ isolate()->factory()->NewJSGlobalPropertyCell(target); |
+ __ cmp(reg, Operand::Cell(cell)); |
+ } else { |
+ Operand operand = ToOperand(instr->value()); |
+ __ cmp(operand, instr->hydrogen()->target()); |
+ } |
DeoptimizeIf(not_equal, instr->environment()); |
} |
@@ -4188,10 +4296,12 @@ |
final_branch_condition = not_zero; |
} else if (type_name->Equals(heap()->function_symbol())) { |
- STATIC_ASSERT(LAST_TYPE == LAST_CALLABLE_SPEC_OBJECT_TYPE); |
+ 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); |
@@ -4303,6 +4413,7 @@ |
DeferredStackCheck(LCodeGen* codegen, LStackCheck* instr) |
: LDeferredCode(codegen), instr_(instr) { } |
virtual void Generate() { codegen()->DoDeferredStackCheck(instr_); } |
+ virtual LInstruction* instr() { return instr_; } |
private: |
LStackCheck* instr_; |
}; |