| Index: src/x64/codegen-x64.cc
|
| ===================================================================
|
| --- src/x64/codegen-x64.cc (revision 4514)
|
| +++ src/x64/codegen-x64.cc (working copy)
|
| @@ -202,11 +202,21 @@
|
| // Code pattern for loading a floating point value. Input value must
|
| // be either a smi or a heap number object (fp value). Requirements:
|
| // operand in src register. Returns operand as floating point number
|
| - // in XMM register
|
| + // in XMM register. May destroy src register.
|
| static void LoadFloatOperand(MacroAssembler* masm,
|
| Register src,
|
| XMMRegister dst);
|
|
|
| + // Code pattern for loading a possible number into a XMM register.
|
| + // If the contents of src is not a number, control branches to
|
| + // the Label not_number. If contents of src is a smi or a heap number
|
| + // object (fp value), it is loaded into the XMM register as a double.
|
| + // The register src is not changed, and src may not be kScratchRegister.
|
| + static void LoadFloatOperand(MacroAssembler* masm,
|
| + Register src,
|
| + XMMRegister dst,
|
| + Label *not_number);
|
| +
|
| // Code pattern for loading floating point values. Input values must
|
| // be either smi or heap number objects (fp values). Requirements:
|
| // operand_1 in rdx, operand_2 in rax; Returns operands as
|
| @@ -5320,6 +5330,22 @@
|
| }
|
|
|
|
|
| +// Convert from signed to unsigned comparison to match the way EFLAGS are set
|
| +// by FPU and XMM compare instructions.
|
| +static Condition DoubleCondition(Condition cc) {
|
| + switch (cc) {
|
| + case less: return below;
|
| + case equal: return equal;
|
| + case less_equal: return below_equal;
|
| + case greater: return above;
|
| + case greater_equal: return above_equal;
|
| + default: UNREACHABLE();
|
| + }
|
| + UNREACHABLE();
|
| + return equal;
|
| +}
|
| +
|
| +
|
| void CodeGenerator::Comparison(AstNode* node,
|
| Condition cc,
|
| bool strict,
|
| @@ -5391,7 +5417,7 @@
|
| left_side = right_side;
|
| right_side = temp;
|
| cc = ReverseCondition(cc);
|
| - // This may reintroduce greater or less_equal as the value of cc.
|
| + // This may re-introduce greater or less_equal as the value of cc.
|
| // CompareStub and the inline code both support all values of cc.
|
| }
|
| // Implement comparison against a constant Smi, inlining the case
|
| @@ -5434,22 +5460,13 @@
|
| // Jump to builtin for NaN.
|
| not_number.Branch(parity_even, &left_side);
|
| left_side.Unuse();
|
| - Condition double_cc = cc;
|
| - switch (cc) {
|
| - case less: double_cc = below; break;
|
| - case equal: double_cc = equal; break;
|
| - case less_equal: double_cc = below_equal; break;
|
| - case greater: double_cc = above; break;
|
| - case greater_equal: double_cc = above_equal; break;
|
| - default: UNREACHABLE();
|
| - }
|
| - dest->true_target()->Branch(double_cc);
|
| + dest->true_target()->Branch(DoubleCondition(cc));
|
| dest->false_target()->Jump();
|
| not_number.Bind(&left_side);
|
| }
|
|
|
| // Setup and call the compare stub.
|
| - CompareStub stub(cc, strict);
|
| + CompareStub stub(cc, strict, kCantBothBeNaN);
|
| Result result = frame_->CallStub(&stub, &left_side, &right_side);
|
| result.ToRegister();
|
| __ testq(result.reg(), result.reg());
|
| @@ -5642,17 +5659,34 @@
|
| // If either side is a non-smi constant, skip the smi check.
|
| bool known_non_smi =
|
| (left_side.is_constant() && !left_side.handle()->IsSmi()) ||
|
| - (right_side.is_constant() && !right_side.handle()->IsSmi());
|
| + (right_side.is_constant() && !right_side.handle()->IsSmi()) ||
|
| + left_side.type_info().IsDouble() ||
|
| + right_side.type_info().IsDouble();
|
|
|
| NaNInformation nan_info =
|
| (CouldBeNaN(left_side) && CouldBeNaN(right_side)) ?
|
| kBothCouldBeNaN :
|
| kCantBothBeNaN;
|
|
|
| + // Inline number comparison handling any combination of smi's and heap
|
| + // numbers if:
|
| + // code is in a loop
|
| + // the compare operation is different from equal
|
| + // compare is not a for-loop comparison
|
| + // The reason for excluding equal is that it will most likely be done
|
| + // with smi's (not heap numbers) and the code to comparing smi's is inlined
|
| + // separately. The same reason applies for for-loop comparison which will
|
| + // also most likely be smi comparisons.
|
| + bool is_loop_condition = (node->AsExpression() != NULL)
|
| + && node->AsExpression()->is_loop_condition();
|
| + bool inline_number_compare =
|
| + loop_nesting() > 0 && cc != equal && !is_loop_condition;
|
| +
|
| left_side.ToRegister();
|
| right_side.ToRegister();
|
|
|
| if (known_non_smi) {
|
| + // Inlined equality check:
|
| // If at least one of the objects is not NaN, then if the objects
|
| // are identical, they are equal.
|
| if (nan_info == kCantBothBeNaN && cc == equal) {
|
| @@ -5660,8 +5694,15 @@
|
| dest->true_target()->Branch(equal);
|
| }
|
|
|
| - // When non-smi, call out to the compare stub.
|
| - CompareStub stub(cc, strict);
|
| + // Inlined number comparison:
|
| + if (inline_number_compare) {
|
| + GenerateInlineNumberComparison(&left_side, &right_side, cc, dest);
|
| + }
|
| +
|
| + // Call the compare stub.
|
| + // TODO(whesse@chromium.org): Enable the inlining flag once
|
| + // GenerateInlineNumberComparison is implemented.
|
| + CompareStub stub(cc, strict, nan_info, true || !inline_number_compare);
|
| Result answer = frame_->CallStub(&stub, &left_side, &right_side);
|
| // The result is a Smi, which is negative, zero, or positive.
|
| __ SmiTest(answer.reg()); // Sets both zero and sign flag.
|
| @@ -5679,15 +5720,23 @@
|
|
|
| Condition both_smi = masm_->CheckBothSmi(left_reg, right_reg);
|
| is_smi.Branch(both_smi);
|
| - // When non-smi, call out to the compare stub, after inlined checks.
|
| - // If at least one of the objects is not NaN, then if the objects
|
| - // are identical, they are equal.
|
| +
|
| + // Inline the equality check if both operands can't be a NaN. If both
|
| + // objects are the same they are equal.
|
| if (nan_info == kCantBothBeNaN && cc == equal) {
|
| __ cmpq(left_side.reg(), right_side.reg());
|
| dest->true_target()->Branch(equal);
|
| }
|
|
|
| - CompareStub stub(cc, strict);
|
| + // Inlined number comparison:
|
| + if (inline_number_compare) {
|
| + GenerateInlineNumberComparison(&left_side, &right_side, cc, dest);
|
| + }
|
| +
|
| + // Call the compare stub.
|
| + // TODO(whesse@chromium.org): Enable the inlining flag once
|
| + // GenerateInlineNumberComparison is implemented.
|
| + CompareStub stub(cc, strict, nan_info, true || !inline_number_compare);
|
| Result answer = frame_->CallStub(&stub, &left_side, &right_side);
|
| __ SmiTest(answer.reg()); // Sets both zero and sign flags.
|
| answer.Unuse();
|
| @@ -5706,6 +5755,17 @@
|
| }
|
|
|
|
|
| +void CodeGenerator::GenerateInlineNumberComparison(Result* left_side,
|
| + Result* right_side,
|
| + Condition cc,
|
| + ControlDestination* dest) {
|
| + ASSERT(left_side->is_register());
|
| + ASSERT(right_side->is_register());
|
| + // TODO(whesse@chromium.org): Implement this function, and enable the
|
| + // corresponding flags in the CompareStub.
|
| +}
|
| +
|
| +
|
| class DeferredInlineBinaryOperation: public DeferredCode {
|
| public:
|
| DeferredInlineBinaryOperation(Token::Value op,
|
| @@ -7781,60 +7841,90 @@
|
| }
|
|
|
|
|
| +static int NegativeComparisonResult(Condition cc) {
|
| + ASSERT(cc != equal);
|
| + ASSERT((cc == less) || (cc == less_equal)
|
| + || (cc == greater) || (cc == greater_equal));
|
| + return (cc == greater || cc == greater_equal) ? LESS : GREATER;
|
| +}
|
| +
|
| void CompareStub::Generate(MacroAssembler* masm) {
|
| Label call_builtin, done;
|
|
|
| // NOTICE! This code is only reached after a smi-fast-case check, so
|
| // it is certain that at least one operand isn't a smi.
|
|
|
| - if (cc_ == equal) { // Both strict and non-strict.
|
| - Label slow; // Fallthrough label.
|
| - // Equality is almost reflexive (everything but NaN), so start by testing
|
| - // for "identity and not NaN".
|
| - {
|
| - Label not_identical;
|
| - __ cmpq(rax, rdx);
|
| - __ j(not_equal, ¬_identical);
|
| - // Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
|
| - // so we do the second best thing - test it ourselves.
|
| + // Identical objects can be compared fast, but there are some tricky cases
|
| + // for NaN and undefined.
|
| + {
|
| + Label not_identical;
|
| + __ cmpq(rax, rdx);
|
| + __ j(not_equal, ¬_identical);
|
|
|
| - if (never_nan_nan_) {
|
| - __ xor_(rax, rax);
|
| - __ ret(0);
|
| - } else {
|
| - Label return_equal;
|
| - Label heap_number;
|
| - // If it's not a heap number, then return equal.
|
| - __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
|
| - Factory::heap_number_map());
|
| - __ j(equal, &heap_number);
|
| - __ bind(&return_equal);
|
| - __ xor_(rax, rax);
|
| - __ ret(0);
|
| + if (cc_ != equal) {
|
| + // Check for undefined. undefined OP undefined is false even though
|
| + // undefined == undefined.
|
| + Label check_for_nan;
|
| + __ CompareRoot(rdx, Heap::kUndefinedValueRootIndex);
|
| + __ j(not_equal, &check_for_nan);
|
| + __ Set(rax, NegativeComparisonResult(cc_));
|
| + __ ret(0);
|
| + __ bind(&check_for_nan);
|
| + }
|
|
|
| - __ bind(&heap_number);
|
| - // It is a heap number, so return non-equal if it's NaN and equal if
|
| - // it's not NaN.
|
| - // The representation of NaN values has all exponent bits (52..62) set,
|
| - // and not all mantissa bits (0..51) clear.
|
| - // We only allow QNaNs, which have bit 51 set (which also rules out
|
| - // the value being Infinity).
|
| + // Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
|
| + // so we do the second best thing - test it ourselves.
|
| + // Note: if cc_ != equal, never_nan_nan_ is not used.
|
| + if (never_nan_nan_ && (cc_ == equal)) {
|
| + __ Set(rax, EQUAL);
|
| + __ ret(0);
|
| + } else {
|
| + Label return_equal;
|
| + Label heap_number;
|
| + // If it's not a heap number, then return equal.
|
| + __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
|
| + Factory::heap_number_map());
|
| + __ j(equal, &heap_number);
|
| + __ bind(&return_equal);
|
| + __ Set(rax, EQUAL);
|
| + __ ret(0);
|
|
|
| - // Value is a QNaN if value & kQuietNaNMask == kQuietNaNMask, i.e.,
|
| - // all bits in the mask are set. We only need to check the word
|
| - // that contains the exponent and high bit of the mantissa.
|
| - ASSERT_NE(0, (kQuietNaNHighBitsMask << 1) & 0x80000000u);
|
| - __ movl(rdx, FieldOperand(rdx, HeapNumber::kExponentOffset));
|
| - __ xorl(rax, rax);
|
| - __ addl(rdx, rdx); // Shift value and mask so mask applies to top bits.
|
| - __ cmpl(rdx, Immediate(kQuietNaNHighBitsMask << 1));
|
| + __ bind(&heap_number);
|
| + // It is a heap number, so return non-equal if it's NaN and equal if
|
| + // it's not NaN.
|
| + // The representation of NaN values has all exponent bits (52..62) set,
|
| + // and not all mantissa bits (0..51) clear.
|
| + // We only allow QNaNs, which have bit 51 set (which also rules out
|
| + // the value being Infinity).
|
| +
|
| + // Value is a QNaN if value & kQuietNaNMask == kQuietNaNMask, i.e.,
|
| + // all bits in the mask are set. We only need to check the word
|
| + // that contains the exponent and high bit of the mantissa.
|
| + ASSERT_NE(0, (kQuietNaNHighBitsMask << 1) & 0x80000000u);
|
| + __ movl(rdx, FieldOperand(rdx, HeapNumber::kExponentOffset));
|
| + __ xorl(rax, rax);
|
| + __ addl(rdx, rdx); // Shift value and mask so mask applies to top bits.
|
| + __ cmpl(rdx, Immediate(kQuietNaNHighBitsMask << 1));
|
| + if (cc_ == equal) {
|
| __ setcc(above_equal, rax);
|
| __ ret(0);
|
| + } else {
|
| + Label nan;
|
| + __ j(above_equal, &nan);
|
| + __ Set(rax, EQUAL);
|
| + __ ret(0);
|
| + __ bind(&nan);
|
| + __ Set(rax, NegativeComparisonResult(cc_));
|
| + __ ret(0);
|
| }
|
| -
|
| - __ bind(¬_identical);
|
| }
|
|
|
| + __ bind(¬_identical);
|
| + }
|
| +
|
| + if (cc_ == equal) { // Both strict and non-strict.
|
| + Label slow; // Fallthrough label.
|
| +
|
| // If we're doing a strict equality comparison, we don't have to do
|
| // type conversion, so we generate code to do fast comparison for objects
|
| // and oddballs. Non-smi numbers and strings still go through the usual
|
| @@ -7896,36 +7986,43 @@
|
| __ push(rdx);
|
| __ push(rcx);
|
|
|
| - // Inlined floating point compare.
|
| - // Call builtin if operands are not floating point or smi.
|
| - Label check_for_symbols;
|
| - // Push arguments on stack, for helper functions.
|
| - FloatingPointHelper::CheckNumberOperands(masm, &check_for_symbols);
|
| - FloatingPointHelper::LoadFloatOperands(masm, rax, rdx);
|
| - __ FCmp();
|
| + // Generate the number comparison code.
|
| + if (include_number_compare_) {
|
| + Label non_number_comparison;
|
| + Label unordered;
|
| + FloatingPointHelper::LoadFloatOperand(masm, rdx, xmm0,
|
| + &non_number_comparison);
|
| + FloatingPointHelper::LoadFloatOperand(masm, rax, xmm1,
|
| + &non_number_comparison);
|
|
|
| - // Jump to builtin for NaN.
|
| - __ j(parity_even, &call_builtin);
|
| + __ comisd(xmm0, xmm1);
|
|
|
| - // TODO(1243847): Use cmov below once CpuFeatures are properly hooked up.
|
| - Label below_lbl, above_lbl;
|
| - // use rdx, rax to convert unsigned to signed comparison
|
| - __ j(below, &below_lbl);
|
| - __ j(above, &above_lbl);
|
| + // Don't base result on EFLAGS when a NaN is involved.
|
| + __ j(parity_even, &unordered);
|
| + // Return a result of -1, 0, or 1, based on EFLAGS.
|
| + __ movq(rax, Immediate(0)); // equal
|
| + __ movq(rcx, Immediate(1));
|
| + __ cmovq(above, rax, rcx);
|
| + __ movq(rcx, Immediate(-1));
|
| + __ cmovq(below, rax, rcx);
|
| + __ ret(2 * kPointerSize); // rax, rdx were pushed
|
|
|
| - __ xor_(rax, rax); // equal
|
| - __ ret(2 * kPointerSize);
|
| + // If one of the numbers was NaN, then the result is always false.
|
| + // The cc is never not-equal.
|
| + __ bind(&unordered);
|
| + ASSERT(cc_ != not_equal);
|
| + if (cc_ == less || cc_ == less_equal) {
|
| + __ Set(rax, 1);
|
| + } else {
|
| + __ Set(rax, -1);
|
| + }
|
| + __ ret(2 * kPointerSize); // rax, rdx were pushed
|
|
|
| - __ bind(&below_lbl);
|
| - __ movq(rax, Immediate(-1));
|
| - __ ret(2 * kPointerSize);
|
| + // The number comparison code did not provide a valid result.
|
| + __ bind(&non_number_comparison);
|
| + }
|
|
|
| - __ bind(&above_lbl);
|
| - __ movq(rax, Immediate(1));
|
| - __ ret(2 * kPointerSize); // rax, rdx were pushed
|
| -
|
| // Fast negative check for symbol-to-symbol equality.
|
| - __ bind(&check_for_symbols);
|
| Label check_for_strings;
|
| if (cc_ == equal) {
|
| BranchIfNonSymbol(masm, &check_for_strings, rax, kScratchRegister);
|
| @@ -7968,14 +8065,7 @@
|
| builtin = strict_ ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
|
| } else {
|
| builtin = Builtins::COMPARE;
|
| - int ncr; // NaN compare result
|
| - if (cc_ == less || cc_ == less_equal) {
|
| - ncr = GREATER;
|
| - } else {
|
| - ASSERT(cc_ == greater || cc_ == greater_equal); // remaining cases
|
| - ncr = LESS;
|
| - }
|
| - __ Push(Smi::FromInt(ncr));
|
| + __ push(Immediate(NegativeComparisonResult(cc_)));
|
| }
|
|
|
| // Restore return address on the stack.
|
| @@ -8764,6 +8854,27 @@
|
| }
|
|
|
|
|
| +void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
|
| + Register src,
|
| + XMMRegister dst,
|
| + Label* not_number) {
|
| + Label load_smi, done;
|
| + ASSERT(!src.is(kScratchRegister));
|
| + __ JumpIfSmi(src, &load_smi);
|
| + __ LoadRoot(kScratchRegister, Heap::kHeapNumberMapRootIndex);
|
| + __ cmpq(FieldOperand(src, HeapObject::kMapOffset), kScratchRegister);
|
| + __ j(not_equal, not_number);
|
| + __ movsd(dst, FieldOperand(src, HeapNumber::kValueOffset));
|
| + __ jmp(&done);
|
| +
|
| + __ bind(&load_smi);
|
| + __ SmiToInteger32(kScratchRegister, src);
|
| + __ cvtlsi2sd(dst, kScratchRegister);
|
| +
|
| + __ bind(&done);
|
| +}
|
| +
|
| +
|
| void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm,
|
| XMMRegister dst1,
|
| XMMRegister dst2) {
|
|
|