| Index: src/x64/lithium-codegen-x64.cc
|
| ===================================================================
|
| --- src/x64/lithium-codegen-x64.cc (revision 8618)
|
| +++ src/x64/lithium-codegen-x64.cc (working copy)
|
| @@ -144,7 +144,7 @@
|
| // when called as functions (without an explicit receiver
|
| // object). rcx is zero for method calls and non-zero for function
|
| // calls.
|
| - if (info_->is_strict_mode()) {
|
| + if (info_->is_strict_mode() || info_->is_native()) {
|
| Label ok;
|
| __ testq(rcx, rcx);
|
| __ j(zero, &ok, Label::kNear);
|
| @@ -197,7 +197,7 @@
|
| FastNewContextStub stub(heap_slots);
|
| __ CallStub(&stub);
|
| } else {
|
| - __ CallRuntime(Runtime::kNewContext, 1);
|
| + __ CallRuntime(Runtime::kNewFunctionContext, 1);
|
| }
|
| RecordSafepoint(Safepoint::kNoDeoptimizationIndex);
|
| // Context is returned in both rax and rsi. It replaces the context
|
| @@ -272,11 +272,25 @@
|
|
|
| bool LCodeGen::GenerateDeferredCode() {
|
| ASSERT(is_generating());
|
| - for (int i = 0; !is_aborted() && i < deferred_.length(); i++) {
|
| - LDeferredCode* code = deferred_[i];
|
| - __ bind(code->entry());
|
| - code->Generate();
|
| - __ jmp(code->exit());
|
| + if (deferred_.length() > 0) {
|
| + for (int i = 0; !is_aborted() && i < deferred_.length(); i++) {
|
| + LDeferredCode* code = deferred_[i];
|
| + __ bind(code->entry());
|
| + code->Generate();
|
| + __ jmp(code->exit());
|
| + }
|
| +
|
| + // Pad code to ensure that the last piece of deferred code have
|
| + // room for lazy bailout.
|
| + while ((masm()->pc_offset() - LastSafepointEnd())
|
| + < Deoptimizer::patch_size()) {
|
| + int padding = masm()->pc_offset() - LastSafepointEnd();
|
| + if (padding > 9) {
|
| + __ nop(9);
|
| + } else {
|
| + __ nop(padding);
|
| + }
|
| + }
|
| }
|
|
|
| // Deferred code is the last part of the instruction sequence. Mark
|
| @@ -689,7 +703,7 @@
|
|
|
|
|
| void LCodeGen::RecordPosition(int position) {
|
| - if (!FLAG_debug_info || position == RelocInfo::kNoPosition) return;
|
| + if (position == RelocInfo::kNoPosition) return;
|
| masm()->positions_recorder()->RecordPosition(position);
|
| }
|
|
|
| @@ -806,6 +820,8 @@
|
| if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
|
| __ j(not_zero, &done, Label::kNear);
|
| DeoptimizeIf(no_condition, instr->environment());
|
| + } else {
|
| + __ jmp(&done, Label::kNear);
|
| }
|
| __ bind(&positive_dividend);
|
| __ andl(dividend, Immediate(divisor - 1));
|
| @@ -1211,6 +1227,20 @@
|
| }
|
|
|
|
|
| +void LCodeGen::DoElementsKind(LElementsKind* instr) {
|
| + Register result = ToRegister(instr->result());
|
| + Register input = ToRegister(instr->InputAt(0));
|
| +
|
| + // Load map into |result|.
|
| + __ movq(result, FieldOperand(input, HeapObject::kMapOffset));
|
| + // Load the map's "bit field 2" into |result|. We only need the first byte.
|
| + __ movzxbq(result, FieldOperand(result, Map::kBitField2Offset));
|
| + // Retrieve elements_kind from bit field 2.
|
| + __ and_(result, Immediate(Map::kElementsKindMask));
|
| + __ shr(result, Immediate(Map::kElementsKindShift));
|
| +}
|
| +
|
| +
|
| void LCodeGen::DoValueOf(LValueOf* instr) {
|
| Register input = ToRegister(instr->InputAt(0));
|
| Register result = ToRegister(instr->result());
|
| @@ -1344,7 +1374,7 @@
|
| int true_block = chunk_->LookupDestination(instr->true_block_id());
|
| int false_block = chunk_->LookupDestination(instr->false_block_id());
|
|
|
| - Representation r = instr->hydrogen()->representation();
|
| + Representation r = instr->hydrogen()->value()->representation();
|
| if (r.IsInteger32()) {
|
| Register reg = ToRegister(instr->InputAt(0));
|
| __ testl(reg, reg);
|
| @@ -1357,7 +1387,7 @@
|
| } else {
|
| ASSERT(r.IsTagged());
|
| Register reg = ToRegister(instr->InputAt(0));
|
| - HType type = instr->hydrogen()->type();
|
| + HType type = instr->hydrogen()->value()->type();
|
| if (type.IsBoolean()) {
|
| __ CompareRoot(reg, Heap::kTrueValueRootIndex);
|
| EmitBranch(true_block, false_block, equal);
|
| @@ -1394,7 +1424,7 @@
|
| // The conversion stub doesn't cause garbage collections so it's
|
| // safe to not record a safepoint after the call.
|
| __ bind(&call_stub);
|
| - ToBooleanStub stub;
|
| + ToBooleanStub stub(rax);
|
| __ Pushad();
|
| __ push(reg);
|
| __ CallStub(&stub);
|
| @@ -1406,44 +1436,17 @@
|
| }
|
|
|
|
|
| -void LCodeGen::EmitGoto(int block, LDeferredCode* deferred_stack_check) {
|
| +void LCodeGen::EmitGoto(int block) {
|
| block = chunk_->LookupDestination(block);
|
| int next_block = GetNextEmittedBlock(current_block_);
|
| if (block != next_block) {
|
| - // Perform stack overflow check if this goto needs it before jumping.
|
| - if (deferred_stack_check != NULL) {
|
| - __ CompareRoot(rsp, Heap::kStackLimitRootIndex);
|
| - __ j(above_equal, chunk_->GetAssemblyLabel(block));
|
| - __ jmp(deferred_stack_check->entry());
|
| - deferred_stack_check->SetExit(chunk_->GetAssemblyLabel(block));
|
| - } else {
|
| - __ jmp(chunk_->GetAssemblyLabel(block));
|
| - }
|
| + __ jmp(chunk_->GetAssemblyLabel(block));
|
| }
|
| }
|
|
|
|
|
| -void LCodeGen::DoDeferredStackCheck(LGoto* instr) {
|
| - PushSafepointRegistersScope scope(this);
|
| - CallRuntimeFromDeferred(Runtime::kStackGuard, 0, instr);
|
| -}
|
| -
|
| -
|
| void LCodeGen::DoGoto(LGoto* instr) {
|
| - class DeferredStackCheck: public LDeferredCode {
|
| - public:
|
| - DeferredStackCheck(LCodeGen* codegen, LGoto* instr)
|
| - : LDeferredCode(codegen), instr_(instr) { }
|
| - virtual void Generate() { codegen()->DoDeferredStackCheck(instr_); }
|
| - private:
|
| - LGoto* instr_;
|
| - };
|
| -
|
| - DeferredStackCheck* deferred = NULL;
|
| - if (instr->include_stack_check()) {
|
| - deferred = new DeferredStackCheck(this, instr);
|
| - }
|
| - EmitGoto(instr->block_id(), deferred);
|
| + EmitGoto(instr->block_id());
|
| }
|
|
|
|
|
| @@ -1491,32 +1494,6 @@
|
| }
|
|
|
|
|
| -void LCodeGen::DoCmpID(LCmpID* instr) {
|
| - LOperand* left = instr->InputAt(0);
|
| - LOperand* right = instr->InputAt(1);
|
| - LOperand* result = instr->result();
|
| -
|
| - Label unordered;
|
| - if (instr->is_double()) {
|
| - // Don't base result on EFLAGS when a NaN is involved. Instead
|
| - // jump to the unordered case, which produces a false value.
|
| - __ ucomisd(ToDoubleRegister(left), ToDoubleRegister(right));
|
| - __ j(parity_even, &unordered, Label::kNear);
|
| - } else {
|
| - EmitCmpI(left, right);
|
| - }
|
| -
|
| - Label done;
|
| - Condition cc = TokenToCondition(instr->op(), instr->is_double());
|
| - __ LoadRoot(ToRegister(result), Heap::kTrueValueRootIndex);
|
| - __ j(cc, &done, Label::kNear);
|
| -
|
| - __ bind(&unordered);
|
| - __ LoadRoot(ToRegister(result), Heap::kFalseValueRootIndex);
|
| - __ bind(&done);
|
| -}
|
| -
|
| -
|
| void LCodeGen::DoCmpIDAndBranch(LCmpIDAndBranch* instr) {
|
| LOperand* left = instr->InputAt(0);
|
| LOperand* right = instr->InputAt(1);
|
| @@ -1537,25 +1514,9 @@
|
| }
|
|
|
|
|
| -void LCodeGen::DoCmpJSObjectEq(LCmpJSObjectEq* instr) {
|
| +void LCodeGen::DoCmpObjectEqAndBranch(LCmpObjectEqAndBranch* instr) {
|
| Register left = ToRegister(instr->InputAt(0));
|
| Register right = ToRegister(instr->InputAt(1));
|
| - Register result = ToRegister(instr->result());
|
| -
|
| - Label different, done;
|
| - __ cmpq(left, right);
|
| - __ j(not_equal, &different, Label::kNear);
|
| - __ LoadRoot(result, Heap::kTrueValueRootIndex);
|
| - __ jmp(&done, Label::kNear);
|
| - __ bind(&different);
|
| - __ LoadRoot(result, Heap::kFalseValueRootIndex);
|
| - __ bind(&done);
|
| -}
|
| -
|
| -
|
| -void LCodeGen::DoCmpJSObjectEqAndBranch(LCmpJSObjectEqAndBranch* instr) {
|
| - Register left = ToRegister(instr->InputAt(0));
|
| - Register right = ToRegister(instr->InputAt(1));
|
| int false_block = chunk_->LookupDestination(instr->false_block_id());
|
| int true_block = chunk_->LookupDestination(instr->true_block_id());
|
|
|
| @@ -1564,75 +1525,16 @@
|
| }
|
|
|
|
|
| -void LCodeGen::DoCmpSymbolEq(LCmpSymbolEq* instr) {
|
| +void LCodeGen::DoCmpConstantEqAndBranch(LCmpConstantEqAndBranch* instr) {
|
| Register left = ToRegister(instr->InputAt(0));
|
| - Register right = ToRegister(instr->InputAt(1));
|
| - Register result = ToRegister(instr->result());
|
| -
|
| - Label done;
|
| - __ cmpq(left, right);
|
| - __ LoadRoot(result, Heap::kFalseValueRootIndex);
|
| - __ j(not_equal, &done, Label::kNear);
|
| - __ LoadRoot(result, Heap::kTrueValueRootIndex);
|
| - __ bind(&done);
|
| -}
|
| -
|
| -
|
| -void LCodeGen::DoCmpSymbolEqAndBranch(LCmpSymbolEqAndBranch* instr) {
|
| - Register left = ToRegister(instr->InputAt(0));
|
| - Register right = ToRegister(instr->InputAt(1));
|
| - int false_block = chunk_->LookupDestination(instr->false_block_id());
|
| int true_block = chunk_->LookupDestination(instr->true_block_id());
|
| + int false_block = chunk_->LookupDestination(instr->false_block_id());
|
|
|
| - __ cmpq(left, right);
|
| + __ cmpq(left, Immediate(instr->hydrogen()->right()));
|
| EmitBranch(true_block, false_block, equal);
|
| }
|
|
|
|
|
| -void LCodeGen::DoIsNull(LIsNull* instr) {
|
| - Register reg = ToRegister(instr->InputAt(0));
|
| - Register result = ToRegister(instr->result());
|
| -
|
| - // If the expression is known to be a smi, then it's
|
| - // definitely not null. Materialize false.
|
| - // Consider adding other type and representation tests too.
|
| - if (instr->hydrogen()->value()->type().IsSmi()) {
|
| - __ LoadRoot(result, Heap::kFalseValueRootIndex);
|
| - return;
|
| - }
|
| -
|
| - __ CompareRoot(reg, Heap::kNullValueRootIndex);
|
| - if (instr->is_strict()) {
|
| - ASSERT(Heap::kTrueValueRootIndex >= 0);
|
| - __ movl(result, Immediate(Heap::kTrueValueRootIndex));
|
| - Label load;
|
| - __ j(equal, &load, Label::kNear);
|
| - __ Set(result, Heap::kFalseValueRootIndex);
|
| - __ bind(&load);
|
| - __ LoadRootIndexed(result, result, 0);
|
| - } else {
|
| - Label false_value, true_value, done;
|
| - __ j(equal, &true_value, Label::kNear);
|
| - __ CompareRoot(reg, Heap::kUndefinedValueRootIndex);
|
| - __ j(equal, &true_value, Label::kNear);
|
| - __ JumpIfSmi(reg, &false_value, Label::kNear);
|
| - // Check for undetectable objects by looking in the bit field in
|
| - // the map. The object has already been smi checked.
|
| - Register scratch = result;
|
| - __ movq(scratch, FieldOperand(reg, HeapObject::kMapOffset));
|
| - __ testb(FieldOperand(scratch, Map::kBitFieldOffset),
|
| - Immediate(1 << Map::kIsUndetectable));
|
| - __ j(not_zero, &true_value, Label::kNear);
|
| - __ bind(&false_value);
|
| - __ LoadRoot(result, Heap::kFalseValueRootIndex);
|
| - __ jmp(&done, Label::kNear);
|
| - __ bind(&true_value);
|
| - __ LoadRoot(result, Heap::kTrueValueRootIndex);
|
| - __ bind(&done);
|
| - }
|
| -}
|
| -
|
| -
|
| void LCodeGen::DoIsNullAndBranch(LIsNullAndBranch* instr) {
|
| Register reg = ToRegister(instr->InputAt(0));
|
|
|
| @@ -1688,32 +1590,13 @@
|
|
|
| __ movzxbl(kScratchRegister,
|
| FieldOperand(kScratchRegister, Map::kInstanceTypeOffset));
|
| - __ cmpb(kScratchRegister, Immediate(FIRST_JS_OBJECT_TYPE));
|
| + __ cmpb(kScratchRegister, Immediate(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE));
|
| __ j(below, is_not_object);
|
| - __ cmpb(kScratchRegister, Immediate(LAST_JS_OBJECT_TYPE));
|
| + __ cmpb(kScratchRegister, Immediate(LAST_NONCALLABLE_SPEC_OBJECT_TYPE));
|
| return below_equal;
|
| }
|
|
|
|
|
| -void LCodeGen::DoIsObject(LIsObject* instr) {
|
| - Register reg = ToRegister(instr->InputAt(0));
|
| - Register result = ToRegister(instr->result());
|
| - Label is_false, is_true, done;
|
| -
|
| - Condition true_cond = EmitIsObject(reg, &is_false, &is_true);
|
| - __ j(true_cond, &is_true);
|
| -
|
| - __ bind(&is_false);
|
| - __ LoadRoot(result, Heap::kFalseValueRootIndex);
|
| - __ jmp(&done);
|
| -
|
| - __ bind(&is_true);
|
| - __ LoadRoot(result, Heap::kTrueValueRootIndex);
|
| -
|
| - __ bind(&done);
|
| -}
|
| -
|
| -
|
| void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) {
|
| Register reg = ToRegister(instr->InputAt(0));
|
|
|
| @@ -1728,22 +1611,6 @@
|
| }
|
|
|
|
|
| -void LCodeGen::DoIsSmi(LIsSmi* instr) {
|
| - LOperand* input_operand = instr->InputAt(0);
|
| - Register result = ToRegister(instr->result());
|
| - if (input_operand->IsRegister()) {
|
| - Register input = ToRegister(input_operand);
|
| - __ CheckSmiToIndicator(result, input);
|
| - } else {
|
| - Operand input = ToOperand(instr->InputAt(0));
|
| - __ CheckSmiToIndicator(result, input);
|
| - }
|
| - // result is zero if input is a smi, and one otherwise.
|
| - ASSERT(Heap::kFalseValueRootIndex == Heap::kTrueValueRootIndex + 1);
|
| - __ LoadRootIndexed(result, result, Heap::kTrueValueRootIndex);
|
| -}
|
| -
|
| -
|
| void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) {
|
| int true_block = chunk_->LookupDestination(instr->true_block_id());
|
| int false_block = chunk_->LookupDestination(instr->false_block_id());
|
| @@ -1760,25 +1627,6 @@
|
| }
|
|
|
|
|
| -void LCodeGen::DoIsUndetectable(LIsUndetectable* instr) {
|
| - Register input = ToRegister(instr->InputAt(0));
|
| - Register result = ToRegister(instr->result());
|
| -
|
| - ASSERT(instr->hydrogen()->value()->representation().IsTagged());
|
| - Label false_label, done;
|
| - __ JumpIfSmi(input, &false_label);
|
| - __ movq(result, FieldOperand(input, HeapObject::kMapOffset));
|
| - __ testb(FieldOperand(result, Map::kBitFieldOffset),
|
| - Immediate(1 << Map::kIsUndetectable));
|
| - __ j(zero, &false_label);
|
| - __ LoadRoot(result, Heap::kTrueValueRootIndex);
|
| - __ jmp(&done);
|
| - __ bind(&false_label);
|
| - __ LoadRoot(result, Heap::kFalseValueRootIndex);
|
| - __ bind(&done);
|
| -}
|
| -
|
| -
|
| void LCodeGen::DoIsUndetectableAndBranch(LIsUndetectableAndBranch* instr) {
|
| Register input = ToRegister(instr->InputAt(0));
|
| Register temp = ToRegister(instr->TempAt(0));
|
| @@ -1794,7 +1642,7 @@
|
| }
|
|
|
|
|
| -static InstanceType TestType(HHasInstanceType* instr) {
|
| +static InstanceType TestType(HHasInstanceTypeAndBranch* instr) {
|
| InstanceType from = instr->from();
|
| InstanceType to = instr->to();
|
| if (from == FIRST_TYPE) return to;
|
| @@ -1803,7 +1651,7 @@
|
| }
|
|
|
|
|
| -static Condition BranchCondition(HHasInstanceType* instr) {
|
| +static Condition BranchCondition(HHasInstanceTypeAndBranch* instr) {
|
| InstanceType from = instr->from();
|
| InstanceType to = instr->to();
|
| if (from == to) return equal;
|
| @@ -1814,25 +1662,6 @@
|
| }
|
|
|
|
|
| -void LCodeGen::DoHasInstanceType(LHasInstanceType* instr) {
|
| - Register input = ToRegister(instr->InputAt(0));
|
| - Register result = ToRegister(instr->result());
|
| -
|
| - ASSERT(instr->hydrogen()->value()->representation().IsTagged());
|
| - __ testl(input, Immediate(kSmiTagMask));
|
| - Label done, is_false;
|
| - __ j(zero, &is_false);
|
| - __ CmpObjectType(input, TestType(instr->hydrogen()), result);
|
| - __ j(NegateCondition(BranchCondition(instr->hydrogen())),
|
| - &is_false, Label::kNear);
|
| - __ LoadRoot(result, Heap::kTrueValueRootIndex);
|
| - __ jmp(&done, Label::kNear);
|
| - __ bind(&is_false);
|
| - __ LoadRoot(result, Heap::kFalseValueRootIndex);
|
| - __ bind(&done);
|
| -}
|
| -
|
| -
|
| void LCodeGen::DoHasInstanceTypeAndBranch(LHasInstanceTypeAndBranch* instr) {
|
| Register input = ToRegister(instr->InputAt(0));
|
|
|
| @@ -1862,21 +1691,6 @@
|
| }
|
|
|
|
|
| -void LCodeGen::DoHasCachedArrayIndex(LHasCachedArrayIndex* instr) {
|
| - Register input = ToRegister(instr->InputAt(0));
|
| - Register result = ToRegister(instr->result());
|
| -
|
| - ASSERT(instr->hydrogen()->value()->representation().IsTagged());
|
| - __ LoadRoot(result, Heap::kTrueValueRootIndex);
|
| - __ testl(FieldOperand(input, String::kHashFieldOffset),
|
| - Immediate(String::kContainsCachedArrayIndexMask));
|
| - Label done;
|
| - __ j(zero, &done, Label::kNear);
|
| - __ LoadRoot(result, Heap::kFalseValueRootIndex);
|
| - __ bind(&done);
|
| -}
|
| -
|
| -
|
| void LCodeGen::DoHasCachedArrayIndexAndBranch(
|
| LHasCachedArrayIndexAndBranch* instr) {
|
| Register input = ToRegister(instr->InputAt(0));
|
| @@ -1898,26 +1712,27 @@
|
| Register input,
|
| Register temp) {
|
| __ JumpIfSmi(input, is_false);
|
| - __ CmpObjectType(input, FIRST_JS_OBJECT_TYPE, temp);
|
| + __ CmpObjectType(input, FIRST_SPEC_OBJECT_TYPE, temp);
|
| __ j(below, is_false);
|
|
|
| // Map is now in temp.
|
| // Functions have class 'Function'.
|
| - __ CmpInstanceType(temp, JS_FUNCTION_TYPE);
|
| + __ CmpInstanceType(temp, FIRST_CALLABLE_SPEC_OBJECT_TYPE);
|
| if (class_name->IsEqualTo(CStrVector("Function"))) {
|
| - __ j(equal, is_true);
|
| + __ j(above_equal, is_true);
|
| } else {
|
| - __ j(equal, is_false);
|
| + __ j(above_equal, is_false);
|
| }
|
|
|
| // Check if the constructor in the map is a function.
|
| __ movq(temp, FieldOperand(temp, Map::kConstructorOffset));
|
|
|
| - // As long as JS_FUNCTION_TYPE is the last instance type and it is
|
| - // right after LAST_JS_OBJECT_TYPE, we can avoid checking for
|
| - // LAST_JS_OBJECT_TYPE.
|
| - ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
|
| - ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
|
| + // 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);
|
| @@ -1944,29 +1759,6 @@
|
| }
|
|
|
|
|
| -void LCodeGen::DoClassOfTest(LClassOfTest* instr) {
|
| - Register input = ToRegister(instr->InputAt(0));
|
| - Register result = ToRegister(instr->result());
|
| - ASSERT(input.is(result));
|
| - Register temp = ToRegister(instr->TempAt(0));
|
| - Handle<String> class_name = instr->hydrogen()->class_name();
|
| - Label done;
|
| - Label is_true, is_false;
|
| -
|
| - EmitClassOfTest(&is_true, &is_false, class_name, input, temp);
|
| -
|
| - __ j(not_equal, &is_false);
|
| -
|
| - __ bind(&is_true);
|
| - __ LoadRoot(result, Heap::kTrueValueRootIndex);
|
| - __ jmp(&done, Label::kNear);
|
| -
|
| - __ bind(&is_false);
|
| - __ LoadRoot(result, Heap::kFalseValueRootIndex);
|
| - __ bind(&done);
|
| -}
|
| -
|
| -
|
| void LCodeGen::DoClassOfTestAndBranch(LClassOfTestAndBranch* instr) {
|
| Register input = ToRegister(instr->InputAt(0));
|
| Register temp = ToRegister(instr->TempAt(0));
|
| @@ -2010,19 +1802,6 @@
|
| }
|
|
|
|
|
| -void LCodeGen::DoInstanceOfAndBranch(LInstanceOfAndBranch* instr) {
|
| - int true_block = chunk_->LookupDestination(instr->true_block_id());
|
| - int false_block = chunk_->LookupDestination(instr->false_block_id());
|
| -
|
| - InstanceofStub stub(InstanceofStub::kNoFlags);
|
| - __ push(ToRegister(instr->InputAt(0)));
|
| - __ push(ToRegister(instr->InputAt(1)));
|
| - CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
|
| - __ testq(rax, rax);
|
| - EmitBranch(true_block, false_block, zero);
|
| -}
|
| -
|
| -
|
| void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) {
|
| class DeferredInstanceOfKnownGlobal: public LDeferredCode {
|
| public:
|
| @@ -2154,25 +1933,6 @@
|
| }
|
|
|
|
|
| -void LCodeGen::DoCmpTAndBranch(LCmpTAndBranch* instr) {
|
| - Token::Value op = instr->op();
|
| - int true_block = chunk_->LookupDestination(instr->true_block_id());
|
| - int false_block = chunk_->LookupDestination(instr->false_block_id());
|
| -
|
| - Handle<Code> ic = CompareIC::GetUninitialized(op);
|
| - CallCode(ic, RelocInfo::CODE_TARGET, instr);
|
| -
|
| - // The compare stub expects compare condition and the input operands
|
| - // reversed for GT and LTE.
|
| - Condition condition = TokenToCondition(op, false);
|
| - if (op == Token::GT || op == Token::LTE) {
|
| - condition = ReverseCondition(condition);
|
| - }
|
| - __ testq(rax, rax);
|
| - EmitBranch(true_block, false_block, condition);
|
| -}
|
| -
|
| -
|
| void LCodeGen::DoReturn(LReturn* instr) {
|
| if (FLAG_trace) {
|
| // Preserve the return value on the stack and rely on the runtime
|
| @@ -2420,7 +2180,7 @@
|
| Register input = ToRegister(instr->InputAt(0));
|
| __ movq(result, FieldOperand(input, JSObject::kElementsOffset));
|
| if (FLAG_debug_code) {
|
| - Label done;
|
| + Label done, ok, fail;
|
| __ CompareRoot(FieldOperand(result, HeapObject::kMapOffset),
|
| Heap::kFixedArrayMapRootIndex);
|
| __ j(equal, &done, Label::kNear);
|
| @@ -2430,11 +2190,19 @@
|
| Register temp((result.is(rax)) ? rbx : rax);
|
| __ push(temp);
|
| __ movq(temp, FieldOperand(result, HeapObject::kMapOffset));
|
| - __ movzxbq(temp, FieldOperand(temp, Map::kInstanceTypeOffset));
|
| - __ subq(temp, Immediate(FIRST_EXTERNAL_ARRAY_TYPE));
|
| - __ cmpq(temp, Immediate(kExternalArrayTypeCount));
|
| + __ movzxbq(temp, FieldOperand(temp, Map::kBitField2Offset));
|
| + __ and_(temp, Immediate(Map::kElementsKindMask));
|
| + __ shr(temp, Immediate(Map::kElementsKindShift));
|
| + __ cmpl(temp, Immediate(JSObject::FAST_ELEMENTS));
|
| + __ j(equal, &ok, Label::kNear);
|
| + __ cmpl(temp, Immediate(JSObject::FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND));
|
| + __ j(less, &fail, Label::kNear);
|
| + __ cmpl(temp, Immediate(JSObject::LAST_EXTERNAL_ARRAY_ELEMENTS_KIND));
|
| + __ j(less_equal, &ok, Label::kNear);
|
| + __ bind(&fail);
|
| + __ Abort("Check for fast or external elements failed");
|
| + __ bind(&ok);
|
| __ pop(temp);
|
| - __ Check(below, "Check for fast elements failed.");
|
| __ bind(&done);
|
| }
|
| }
|
| @@ -2487,11 +2255,12 @@
|
| }
|
|
|
|
|
| -Operand LCodeGen::BuildExternalArrayOperand(LOperand* external_pointer,
|
| - LOperand* key,
|
| - ExternalArrayType array_type) {
|
| +Operand LCodeGen::BuildExternalArrayOperand(
|
| + LOperand* external_pointer,
|
| + LOperand* key,
|
| + JSObject::ElementsKind elements_kind) {
|
| Register external_pointer_reg = ToRegister(external_pointer);
|
| - int shift_size = ExternalArrayTypeToShiftSize(array_type);
|
| + int shift_size = ElementsKindToShiftSize(elements_kind);
|
| if (key->IsConstantOperand()) {
|
| int constant_value = ToInteger32(LConstantOperand::cast(key));
|
| if (constant_value & 0xF0000000) {
|
| @@ -2507,35 +2276,35 @@
|
|
|
| void LCodeGen::DoLoadKeyedSpecializedArrayElement(
|
| LLoadKeyedSpecializedArrayElement* instr) {
|
| - ExternalArrayType array_type = instr->array_type();
|
| + JSObject::ElementsKind elements_kind = instr->elements_kind();
|
| Operand operand(BuildExternalArrayOperand(instr->external_pointer(),
|
| - instr->key(), array_type));
|
| - if (array_type == kExternalFloatArray) {
|
| + instr->key(), elements_kind));
|
| + if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) {
|
| XMMRegister result(ToDoubleRegister(instr->result()));
|
| __ movss(result, operand);
|
| __ cvtss2sd(result, result);
|
| - } else if (array_type == kExternalDoubleArray) {
|
| + } else if (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) {
|
| __ movsd(ToDoubleRegister(instr->result()), operand);
|
| } else {
|
| Register result(ToRegister(instr->result()));
|
| - switch (array_type) {
|
| - case kExternalByteArray:
|
| + switch (elements_kind) {
|
| + case JSObject::EXTERNAL_BYTE_ELEMENTS:
|
| __ movsxbq(result, operand);
|
| break;
|
| - case kExternalUnsignedByteArray:
|
| - case kExternalPixelArray:
|
| + case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
|
| + case JSObject::EXTERNAL_PIXEL_ELEMENTS:
|
| __ movzxbq(result, operand);
|
| break;
|
| - case kExternalShortArray:
|
| + case JSObject::EXTERNAL_SHORT_ELEMENTS:
|
| __ movsxwq(result, operand);
|
| break;
|
| - case kExternalUnsignedShortArray:
|
| + case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
|
| __ movzxwq(result, operand);
|
| break;
|
| - case kExternalIntArray:
|
| + case JSObject::EXTERNAL_INT_ELEMENTS:
|
| __ movsxlq(result, operand);
|
| break;
|
| - case kExternalUnsignedIntArray:
|
| + case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS:
|
| __ movl(result, operand);
|
| __ testl(result, result);
|
| // TODO(danno): we could be more clever here, perhaps having a special
|
| @@ -2543,8 +2312,12 @@
|
| // happens, and generate code that returns a double rather than int.
|
| DeoptimizeIf(negative, instr->environment());
|
| break;
|
| - case kExternalFloatArray:
|
| - case kExternalDoubleArray:
|
| + case JSObject::EXTERNAL_FLOAT_ELEMENTS:
|
| + case JSObject::EXTERNAL_DOUBLE_ELEMENTS:
|
| + case JSObject::FAST_ELEMENTS:
|
| + case JSObject::FAST_DOUBLE_ELEMENTS:
|
| + case JSObject::DICTIONARY_ELEMENTS:
|
| + case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS:
|
| UNREACHABLE();
|
| break;
|
| }
|
| @@ -2619,9 +2392,27 @@
|
| ASSERT(function.is(rdi)); // Required by InvokeFunction.
|
| ASSERT(ToRegister(instr->result()).is(rax));
|
|
|
| - // If the receiver is null or undefined, we have to pass the global object
|
| - // as a receiver.
|
| + // If the receiver is null or undefined, we have to pass the global
|
| + // object as a receiver to normal functions. Values have to be
|
| + // passed unchanged to builtins and strict-mode functions.
|
| Label global_object, receiver_ok;
|
| +
|
| + // Do not transform the receiver to object for strict mode
|
| + // functions.
|
| + __ movq(kScratchRegister,
|
| + FieldOperand(function, JSFunction::kSharedFunctionInfoOffset));
|
| + __ testb(FieldOperand(kScratchRegister,
|
| + SharedFunctionInfo::kStrictModeByteOffset),
|
| + Immediate(1 << SharedFunctionInfo::kStrictModeBitWithinByte));
|
| + __ j(not_equal, &receiver_ok, Label::kNear);
|
| +
|
| + // Do not transform the receiver to object for builtins.
|
| + __ testb(FieldOperand(kScratchRegister,
|
| + SharedFunctionInfo::kNativeByteOffset),
|
| + Immediate(1 << SharedFunctionInfo::kNativeBitWithinByte));
|
| + __ j(not_equal, &receiver_ok, Label::kNear);
|
| +
|
| + // Normal function. Replace undefined or null with global receiver.
|
| __ CompareRoot(receiver, Heap::kNullValueRootIndex);
|
| __ j(equal, &global_object, Label::kNear);
|
| __ CompareRoot(receiver, Heap::kUndefinedValueRootIndex);
|
| @@ -2630,7 +2421,7 @@
|
| // The receiver should be a JS object.
|
| Condition is_smi = __ CheckSmi(receiver);
|
| DeoptimizeIf(is_smi, instr->environment());
|
| - __ CmpObjectType(receiver, FIRST_JS_OBJECT_TYPE, kScratchRegister);
|
| + __ CmpObjectType(receiver, FIRST_SPEC_OBJECT_TYPE, kScratchRegister);
|
| DeoptimizeIf(below, instr->environment());
|
| __ jmp(&receiver_ok, Label::kNear);
|
|
|
| @@ -2638,8 +2429,9 @@
|
| // TODO(kmillikin): We have a hydrogen value for the global object. See
|
| // if it's better to use it than to explicitly fetch it from the context
|
| // here.
|
| - __ movq(receiver, Operand(rbp, StandardFrameConstants::kContextOffset));
|
| - __ movq(receiver, ContextOperand(receiver, Context::GLOBAL_INDEX));
|
| + __ movq(receiver, ContextOperand(rsi, Context::GLOBAL_INDEX));
|
| + __ movq(receiver,
|
| + FieldOperand(receiver, JSGlobalObject::kGlobalReceiverOffset));
|
| __ bind(&receiver_ok);
|
|
|
| // Copy the arguments to this function possibly from the
|
| @@ -2673,7 +2465,8 @@
|
| pointers,
|
| env->deoptimization_index());
|
| v8::internal::ParameterCount actual(rax);
|
| - __ InvokeFunction(function, actual, CALL_FUNCTION, safepoint_generator);
|
| + __ InvokeFunction(function, actual, CALL_FUNCTION,
|
| + safepoint_generator, CALL_AS_METHOD);
|
| __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
|
| }
|
|
|
| @@ -2684,6 +2477,12 @@
|
| }
|
|
|
|
|
| +void LCodeGen::DoThisFunction(LThisFunction* instr) {
|
| + Register result = ToRegister(instr->result());
|
| + __ movq(result, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
|
| +}
|
| +
|
| +
|
| void LCodeGen::DoContext(LContext* instr) {
|
| Register result = ToRegister(instr->result());
|
| __ movq(result, rsi);
|
| @@ -2694,8 +2493,7 @@
|
| Register context = ToRegister(instr->context());
|
| Register result = ToRegister(instr->result());
|
| __ movq(result,
|
| - Operand(context, Context::SlotOffset(Context::CLOSURE_INDEX)));
|
| - __ movq(result, FieldOperand(result, JSFunction::kContextOffset));
|
| + Operand(context, Context::SlotOffset(Context::PREVIOUS_INDEX)));
|
| }
|
|
|
|
|
| @@ -3086,7 +2884,7 @@
|
| RegisterEnvironmentForDeoptimization(env);
|
| SafepointGenerator generator(this, pointers, env->deoptimization_index());
|
| ParameterCount count(instr->arity());
|
| - __ InvokeFunction(rdi, count, CALL_FUNCTION, generator);
|
| + __ InvokeFunction(rdi, count, CALL_FUNCTION, generator, CALL_AS_METHOD);
|
| __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
|
| }
|
|
|
| @@ -3205,33 +3003,37 @@
|
|
|
| void LCodeGen::DoStoreKeyedSpecializedArrayElement(
|
| LStoreKeyedSpecializedArrayElement* instr) {
|
| - ExternalArrayType array_type = instr->array_type();
|
| + JSObject::ElementsKind elements_kind = instr->elements_kind();
|
| Operand operand(BuildExternalArrayOperand(instr->external_pointer(),
|
| - instr->key(), array_type));
|
| - if (array_type == kExternalFloatArray) {
|
| + instr->key(), elements_kind));
|
| + if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) {
|
| XMMRegister value(ToDoubleRegister(instr->value()));
|
| __ cvtsd2ss(value, value);
|
| __ movss(operand, value);
|
| - } else if (array_type == kExternalDoubleArray) {
|
| + } else if (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) {
|
| __ movsd(operand, ToDoubleRegister(instr->value()));
|
| } else {
|
| Register value(ToRegister(instr->value()));
|
| - switch (array_type) {
|
| - case kExternalPixelArray:
|
| - case kExternalByteArray:
|
| - case kExternalUnsignedByteArray:
|
| + switch (elements_kind) {
|
| + case JSObject::EXTERNAL_PIXEL_ELEMENTS:
|
| + case JSObject::EXTERNAL_BYTE_ELEMENTS:
|
| + case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
|
| __ movb(operand, value);
|
| break;
|
| - case kExternalShortArray:
|
| - case kExternalUnsignedShortArray:
|
| + case JSObject::EXTERNAL_SHORT_ELEMENTS:
|
| + case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
|
| __ movw(operand, value);
|
| break;
|
| - case kExternalIntArray:
|
| - case kExternalUnsignedIntArray:
|
| + case JSObject::EXTERNAL_INT_ELEMENTS:
|
| + case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS:
|
| __ movl(operand, value);
|
| break;
|
| - case kExternalFloatArray:
|
| - case kExternalDoubleArray:
|
| + case JSObject::EXTERNAL_FLOAT_ELEMENTS:
|
| + case JSObject::EXTERNAL_DOUBLE_ELEMENTS:
|
| + case JSObject::FAST_ELEMENTS:
|
| + case JSObject::FAST_DOUBLE_ELEMENTS:
|
| + case JSObject::DICTIONARY_ELEMENTS:
|
| + case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS:
|
| UNREACHABLE();
|
| break;
|
| }
|
| @@ -3575,8 +3377,9 @@
|
|
|
| void LCodeGen::EmitNumberUntagD(Register input_reg,
|
| XMMRegister result_reg,
|
| + bool deoptimize_on_undefined,
|
| LEnvironment* env) {
|
| - Label load_smi, heap_number, done;
|
| + Label load_smi, done;
|
|
|
| // Smi check.
|
| __ JumpIfSmi(input_reg, &load_smi, Label::kNear);
|
| @@ -3584,18 +3387,23 @@
|
| // Heap number map check.
|
| __ CompareRoot(FieldOperand(input_reg, HeapObject::kMapOffset),
|
| Heap::kHeapNumberMapRootIndex);
|
| - __ j(equal, &heap_number, Label::kNear);
|
| + if (deoptimize_on_undefined) {
|
| + DeoptimizeIf(not_equal, env);
|
| + } else {
|
| + Label heap_number;
|
| + __ j(equal, &heap_number, Label::kNear);
|
|
|
| - __ CompareRoot(input_reg, Heap::kUndefinedValueRootIndex);
|
| - DeoptimizeIf(not_equal, env);
|
| + __ CompareRoot(input_reg, Heap::kUndefinedValueRootIndex);
|
| + DeoptimizeIf(not_equal, env);
|
|
|
| - // Convert undefined to NaN. Compute NaN as 0/0.
|
| - __ xorps(result_reg, result_reg);
|
| - __ divsd(result_reg, result_reg);
|
| - __ jmp(&done, Label::kNear);
|
| + // Convert undefined to NaN. Compute NaN as 0/0.
|
| + __ xorps(result_reg, result_reg);
|
| + __ divsd(result_reg, result_reg);
|
| + __ jmp(&done, Label::kNear);
|
|
|
| + __ bind(&heap_number);
|
| + }
|
| // Heap number to XMM conversion.
|
| - __ bind(&heap_number);
|
| __ movsd(result_reg, FieldOperand(input_reg, HeapNumber::kValueOffset));
|
| __ jmp(&done, Label::kNear);
|
|
|
| @@ -3686,7 +3494,9 @@
|
| Register input_reg = ToRegister(input);
|
| XMMRegister result_reg = ToDoubleRegister(result);
|
|
|
| - EmitNumberUntagD(input_reg, result_reg, instr->environment());
|
| + EmitNumberUntagD(input_reg, result_reg,
|
| + instr->hydrogen()->deoptimize_on_undefined(),
|
| + instr->environment());
|
| }
|
|
|
|
|
| @@ -4030,29 +3840,6 @@
|
| }
|
|
|
|
|
| -void LCodeGen::DoTypeofIs(LTypeofIs* instr) {
|
| - Register input = ToRegister(instr->InputAt(0));
|
| - Register result = ToRegister(instr->result());
|
| - Label true_label;
|
| - Label false_label;
|
| - Label done;
|
| -
|
| - Condition final_branch_condition = EmitTypeofIs(&true_label,
|
| - &false_label,
|
| - input,
|
| - instr->type_literal());
|
| - __ j(final_branch_condition, &true_label);
|
| - __ bind(&false_label);
|
| - __ LoadRoot(result, Heap::kFalseValueRootIndex);
|
| - __ jmp(&done, Label::kNear);
|
| -
|
| - __ bind(&true_label);
|
| - __ LoadRoot(result, Heap::kTrueValueRootIndex);
|
| -
|
| - __ bind(&done);
|
| -}
|
| -
|
| -
|
| void LCodeGen::EmitPushTaggedOperand(LOperand* operand) {
|
| ASSERT(!operand->IsDoubleRegister());
|
| if (operand->IsConstantOperand()) {
|
| @@ -4119,17 +3906,17 @@
|
|
|
| } else if (type_name->Equals(heap()->function_symbol())) {
|
| __ JumpIfSmi(input, false_label);
|
| - __ CmpObjectType(input, FIRST_FUNCTION_CLASS_TYPE, input);
|
| + __ CmpObjectType(input, FIRST_CALLABLE_SPEC_OBJECT_TYPE, input);
|
| final_branch_condition = above_equal;
|
|
|
| } else if (type_name->Equals(heap()->object_symbol())) {
|
| __ JumpIfSmi(input, false_label);
|
| __ CompareRoot(input, Heap::kNullValueRootIndex);
|
| __ j(equal, true_label);
|
| - __ CmpObjectType(input, FIRST_JS_OBJECT_TYPE, input);
|
| + __ CmpObjectType(input, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, input);
|
| __ j(below, false_label);
|
| - __ CmpInstanceType(input, FIRST_FUNCTION_CLASS_TYPE);
|
| - __ j(above_equal, false_label);
|
| + __ CmpInstanceType(input, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
|
| + __ j(above, false_label);
|
| // Check for undetectable objects => false.
|
| __ testb(FieldOperand(input, Map::kBitFieldOffset),
|
| Immediate(1 << Map::kIsUndetectable));
|
| @@ -4144,25 +3931,6 @@
|
| }
|
|
|
|
|
| -void LCodeGen::DoIsConstructCall(LIsConstructCall* instr) {
|
| - Register result = ToRegister(instr->result());
|
| - Label true_label;
|
| - Label done;
|
| -
|
| - EmitIsConstructCall(result);
|
| - __ j(equal, &true_label, Label::kNear);
|
| -
|
| - __ LoadRoot(result, Heap::kFalseValueRootIndex);
|
| - __ jmp(&done, Label::kNear);
|
| -
|
| - __ bind(&true_label);
|
| - __ LoadRoot(result, Heap::kTrueValueRootIndex);
|
| -
|
| -
|
| - __ bind(&done);
|
| -}
|
| -
|
| -
|
| void LCodeGen::DoIsConstructCallAndBranch(LIsConstructCallAndBranch* instr) {
|
| Register temp = ToRegister(instr->TempAt(0));
|
| int true_block = chunk_->LookupDestination(instr->true_block_id());
|
| @@ -4243,15 +4011,48 @@
|
| }
|
|
|
|
|
| +void LCodeGen::DoDeferredStackCheck(LStackCheck* instr) {
|
| + {
|
| + PushSafepointRegistersScope scope(this);
|
| + __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
|
| + __ CallRuntimeSaveDoubles(Runtime::kStackGuard);
|
| + RegisterLazyDeoptimization(instr, RECORD_SAFEPOINT_WITH_REGISTERS, 0);
|
| + }
|
| +
|
| + // The gap code includes the restoring of the safepoint registers.
|
| + int pc = masm()->pc_offset();
|
| + safepoints_.SetPcAfterGap(pc);
|
| +}
|
| +
|
| +
|
| void LCodeGen::DoStackCheck(LStackCheck* instr) {
|
| - // Perform stack overflow check.
|
| - Label done;
|
| - __ CompareRoot(rsp, Heap::kStackLimitRootIndex);
|
| - __ j(above_equal, &done, Label::kNear);
|
| + class DeferredStackCheck: public LDeferredCode {
|
| + public:
|
| + DeferredStackCheck(LCodeGen* codegen, LStackCheck* instr)
|
| + : LDeferredCode(codegen), instr_(instr) { }
|
| + virtual void Generate() { codegen()->DoDeferredStackCheck(instr_); }
|
| + private:
|
| + LStackCheck* instr_;
|
| + };
|
|
|
| - StackCheckStub stub;
|
| - CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
|
| - __ bind(&done);
|
| + if (instr->hydrogen()->is_function_entry()) {
|
| + // Perform stack overflow check.
|
| + Label done;
|
| + __ CompareRoot(rsp, Heap::kStackLimitRootIndex);
|
| + __ j(above_equal, &done, Label::kNear);
|
| + StackCheckStub stub;
|
| + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
|
| + __ bind(&done);
|
| + } else {
|
| + ASSERT(instr->hydrogen()->is_backwards_branch());
|
| + // Perform stack overflow check if this goto needs it before jumping.
|
| + DeferredStackCheck* deferred_stack_check =
|
| + new DeferredStackCheck(this, instr);
|
| + __ CompareRoot(rsp, Heap::kStackLimitRootIndex);
|
| + __ j(below, deferred_stack_check->entry());
|
| + __ bind(instr->done_label());
|
| + deferred_stack_check->SetExit(instr->done_label());
|
| + }
|
| }
|
|
|
|
|
|
|