Commit 6a639106 authored by sgjesse@chromium.org's avatar sgjesse@chromium.org

Re-apply "Inline floating point compare"

This re-applies r4220 and r4233, which was reverted in r4254 due to a bug. This bug has now been fixed, with the only change being line 2884 changed from

  __ SmiTag(left_side->reg());

to

  __ SmiTag(operand->reg());

Added a regression test.

BUG=http://crbug.com/39160
TEST=test/mjsunit/regress/regress-crbug-39160.js

Review URL: http://codereview.chromium.org/1251009

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4261 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 24451355
...@@ -7018,44 +7018,47 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { ...@@ -7018,44 +7018,47 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
} }
// Unfortunately you have to run without snapshots to see most of these
// names in the profile since most compare stubs end up in the snapshot.
const char* CompareStub::GetName() { const char* CompareStub::GetName() {
if (name_ != NULL) return name_;
const int kMaxNameLength = 100;
name_ = Bootstrapper::AllocateAutoDeletedArray(kMaxNameLength);
if (name_ == NULL) return "OOM";
const char* cc_name;
switch (cc_) { switch (cc_) {
case lt: return "CompareStub_LT"; case lt: cc_name = "LT"; break;
case gt: return "CompareStub_GT"; case gt: cc_name = "GT"; break;
case le: return "CompareStub_LE"; case le: cc_name = "LE"; break;
case ge: return "CompareStub_GE"; case ge: cc_name = "GE"; break;
case ne: { case eq: cc_name = "EQ"; break;
if (strict_) { case ne: cc_name = "NE"; break;
if (never_nan_nan_) { default: cc_name = "UnknownCondition"; break;
return "CompareStub_NE_STRICT_NO_NAN"; }
} else {
return "CompareStub_NE_STRICT"; const char* strict_name = "";
} if (strict_ && (cc_ == eq || cc_ == ne)) {
} else { strict_name = "_STRICT";
if (never_nan_nan_) {
return "CompareStub_NE_NO_NAN";
} else {
return "CompareStub_NE";
}
}
}
case eq: {
if (strict_) {
if (never_nan_nan_) {
return "CompareStub_EQ_STRICT_NO_NAN";
} else {
return "CompareStub_EQ_STRICT";
}
} else {
if (never_nan_nan_) {
return "CompareStub_EQ_NO_NAN";
} else {
return "CompareStub_EQ";
}
}
}
default: return "CompareStub";
} }
const char* never_nan_nan_name = "";
if (never_nan_nan_ && (cc_ == eq || cc_ == ne)) {
never_nan_nan_name = "_NO_NAN";
}
const char* include_number_compare_name = "";
if (!include_number_compare_) {
include_number_compare_name = "_NO_NUMBER";
}
OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
"CompareStub_%s%s%s%s",
cc_name,
strict_name,
never_nan_nan_name,
include_number_compare_name);
return name_;
} }
...@@ -7063,10 +7066,11 @@ int CompareStub::MinorKey() { ...@@ -7063,10 +7066,11 @@ int CompareStub::MinorKey() {
// Encode the three parameters in a unique 16 bit value. To avoid duplicate // Encode the three parameters in a unique 16 bit value. To avoid duplicate
// stubs the never NaN NaN condition is only taken into account if the // stubs the never NaN NaN condition is only taken into account if the
// condition is equals. // condition is equals.
ASSERT((static_cast<unsigned>(cc_) >> 28) < (1 << 14)); ASSERT((static_cast<unsigned>(cc_) >> 28) < (1 << 13));
return ConditionField::encode(static_cast<unsigned>(cc_) >> 28) return ConditionField::encode(static_cast<unsigned>(cc_) >> 28)
| StrictField::encode(strict_) | StrictField::encode(strict_)
| NeverNanNanField::encode(cc_ == eq ? never_nan_nan_ : false); | NeverNanNanField::encode(cc_ == eq ? never_nan_nan_ : false)
| IncludeNumberCompareField::encode(include_number_compare_);
} }
......
...@@ -346,8 +346,13 @@ class CompareStub: public CodeStub { ...@@ -346,8 +346,13 @@ class CompareStub: public CodeStub {
public: public:
CompareStub(Condition cc, CompareStub(Condition cc,
bool strict, bool strict,
NaNInformation nan_info = kBothCouldBeNaN) : NaNInformation nan_info = kBothCouldBeNaN,
cc_(cc), strict_(strict), never_nan_nan_(nan_info == kCantBothBeNaN) { } bool include_number_compare = true) :
cc_(cc),
strict_(strict),
never_nan_nan_(nan_info == kCantBothBeNaN),
include_number_compare_(include_number_compare),
name_(NULL) { }
void Generate(MacroAssembler* masm); void Generate(MacroAssembler* masm);
...@@ -360,11 +365,16 @@ class CompareStub: public CodeStub { ...@@ -360,11 +365,16 @@ class CompareStub: public CodeStub {
// generating the minor key for other comparisons to avoid creating more // generating the minor key for other comparisons to avoid creating more
// stubs. // stubs.
bool never_nan_nan_; bool never_nan_nan_;
// Do generate the number comparison code in the stub. Stubs without number
// comparison code is used when the number comparison has been inlined, and
// the stub will be called if one of the operands is not a number.
bool include_number_compare_;
// Encoding of the minor key CCCCCCCCCCCCCCNS. // Encoding of the minor key CCCCCCCCCCCCCCNS.
class StrictField: public BitField<bool, 0, 1> {}; class StrictField: public BitField<bool, 0, 1> {};
class NeverNanNanField: public BitField<bool, 1, 1> {}; class NeverNanNanField: public BitField<bool, 1, 1> {};
class ConditionField: public BitField<int, 2, 14> {}; class IncludeNumberCompareField: public BitField<bool, 2, 1> {};
class ConditionField: public BitField<int, 3, 13> {};
Major MajorKey() { return Compare; } Major MajorKey() { return Compare; }
...@@ -378,12 +388,16 @@ class CompareStub: public CodeStub { ...@@ -378,12 +388,16 @@ class CompareStub: public CodeStub {
// Unfortunately you have to run without snapshots to see most of these // Unfortunately you have to run without snapshots to see most of these
// names in the profile since most compare stubs end up in the snapshot. // names in the profile since most compare stubs end up in the snapshot.
char* name_;
const char* GetName(); const char* GetName();
#ifdef DEBUG #ifdef DEBUG
void Print() { void Print() {
PrintF("CompareStub (cc %d), (strict %s)\n", PrintF("CompareStub (cc %d), (strict %s), "
"(never_nan_nan %s), (number_compare %s)\n",
static_cast<int>(cc_), static_cast<int>(cc_),
strict_ ? "true" : "false"); strict_ ? "true" : "false",
never_nan_nan_ ? "true" : "false",
include_number_compare_ ? "included" : "not included");
} }
#endif #endif
}; };
......
...@@ -911,6 +911,7 @@ class FloatingPointHelper : public AllStatic { ...@@ -911,6 +911,7 @@ class FloatingPointHelper : public AllStatic {
// operand in register number. Returns operand as floating point number // operand in register number. Returns operand as floating point number
// on FPU stack. // on FPU stack.
static void LoadFloatOperand(MacroAssembler* masm, Register number); static void LoadFloatOperand(MacroAssembler* masm, Register number);
// Code pattern for loading floating point values. Input values must // Code pattern for loading floating point values. Input values must
// be either smi or heap number objects (fp values). Requirements: // be either smi or heap number objects (fp values). Requirements:
// operand_1 on TOS+1 or in edx, operand_2 on TOS+2 or in eax. // operand_1 on TOS+1 or in edx, operand_2 on TOS+2 or in eax.
...@@ -929,6 +930,7 @@ class FloatingPointHelper : public AllStatic { ...@@ -929,6 +930,7 @@ class FloatingPointHelper : public AllStatic {
static void CheckFloatOperands(MacroAssembler* masm, static void CheckFloatOperands(MacroAssembler* masm,
Label* non_float, Label* non_float,
Register scratch); Register scratch);
// Takes the operands in edx and eax and loads them as integers in eax // Takes the operands in edx and eax and loads them as integers in eax
// and ecx. // and ecx.
static void LoadAsIntegers(MacroAssembler* masm, static void LoadAsIntegers(MacroAssembler* masm,
...@@ -947,6 +949,7 @@ class FloatingPointHelper : public AllStatic { ...@@ -947,6 +949,7 @@ class FloatingPointHelper : public AllStatic {
// into xmm0 and xmm1 if they are. Operands are in edx and eax. // into xmm0 and xmm1 if they are. Operands are in edx and eax.
// Leaves operands unchanged. // Leaves operands unchanged.
static void LoadSSE2Operands(MacroAssembler* masm); static void LoadSSE2Operands(MacroAssembler* masm);
// Test if operands are numbers (smi or HeapNumber objects), and load // Test if operands are numbers (smi or HeapNumber objects), and load
// them into xmm0 and xmm1 if they are. Jump to label not_numbers if // them into xmm0 and xmm1 if they are. Jump to label not_numbers if
// either operand is not a number. Operands are in edx and eax. // either operand is not a number. Operands are in edx and eax.
...@@ -2361,6 +2364,22 @@ static bool CouldBeNaN(const Result& result) { ...@@ -2361,6 +2364,22 @@ static bool CouldBeNaN(const Result& result) {
} }
// 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, void CodeGenerator::Comparison(AstNode* node,
Condition cc, Condition cc,
bool strict, bool strict,
...@@ -2431,7 +2450,7 @@ void CodeGenerator::Comparison(AstNode* node, ...@@ -2431,7 +2450,7 @@ void CodeGenerator::Comparison(AstNode* node,
left_side = right_side; left_side = right_side;
right_side = temp; right_side = temp;
cc = ReverseCondition(cc); 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. // CompareStub and the inline code both support all values of cc.
} }
// Implement comparison against a constant Smi, inlining the case // Implement comparison against a constant Smi, inlining the case
...@@ -2480,16 +2499,7 @@ void CodeGenerator::Comparison(AstNode* node, ...@@ -2480,16 +2499,7 @@ void CodeGenerator::Comparison(AstNode* node,
// Jump to builtin for NaN. // Jump to builtin for NaN.
not_number.Branch(parity_even, &left_side); not_number.Branch(parity_even, &left_side);
left_side.Unuse(); left_side.Unuse();
Condition double_cc = cc; dest->true_target()->Branch(DoubleCondition(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->false_target()->Jump(); dest->false_target()->Jump();
not_number.Bind(&left_side); not_number.Bind(&left_side);
} }
...@@ -2688,21 +2698,53 @@ void CodeGenerator::Comparison(AstNode* node, ...@@ -2688,21 +2698,53 @@ void CodeGenerator::Comparison(AstNode* node,
dest->Split(cc); dest->Split(cc);
} }
} else { } else {
// Neither side is a constant Smi or null. // Neither side is a constant Smi, constant 1-char string or constant null.
// If either side is a non-smi constant, skip the smi check. // If either side is a non-smi constant, or known to be a heap number skip
// the smi check.
bool known_non_smi = bool known_non_smi =
(left_side.is_constant() && !left_side.handle()->IsSmi()) || (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.number_info().IsDouble() ||
right_side.number_info().IsDouble();
NaNInformation nan_info = NaNInformation nan_info =
(CouldBeNaN(left_side) && CouldBeNaN(right_side)) ? (CouldBeNaN(left_side) && CouldBeNaN(right_side)) ?
kBothCouldBeNaN : kBothCouldBeNaN :
kCantBothBeNaN; 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 and right needed in registers for the following code.
left_side.ToRegister(); left_side.ToRegister();
right_side.ToRegister(); right_side.ToRegister();
if (known_non_smi) { if (known_non_smi) {
// When non-smi, call out to the compare stub. // Inline the equality check if both operands can't be a NaN. If both
CompareStub stub(cc, strict, nan_info); // objects are the same they are equal.
if (nan_info == kCantBothBeNaN && cc == equal) {
__ cmp(left_side.reg(), Operand(right_side.reg()));
dest->true_target()->Branch(equal);
}
// Inline number comparison.
if (inline_number_compare) {
GenerateInlineNumberComparison(&left_side, &right_side, cc, dest);
}
// End of in-line compare, call out to the compare stub. Don't include
// number comparison in the stub if it was inlined.
CompareStub stub(cc, strict, nan_info, !inline_number_compare);
Result answer = frame_->CallStub(&stub, &left_side, &right_side); Result answer = frame_->CallStub(&stub, &left_side, &right_side);
if (cc == equal) { if (cc == equal) {
__ test(answer.reg(), Operand(answer.reg())); __ test(answer.reg(), Operand(answer.reg()));
...@@ -2721,6 +2763,7 @@ void CodeGenerator::Comparison(AstNode* node, ...@@ -2721,6 +2763,7 @@ void CodeGenerator::Comparison(AstNode* node,
Register left_reg = left_side.reg(); Register left_reg = left_side.reg();
Register right_reg = right_side.reg(); Register right_reg = right_side.reg();
// In-line check for comparing two smis.
Result temp = allocator_->Allocate(); Result temp = allocator_->Allocate();
ASSERT(temp.is_valid()); ASSERT(temp.is_valid());
__ mov(temp.reg(), left_side.reg()); __ mov(temp.reg(), left_side.reg());
...@@ -2728,8 +2771,22 @@ void CodeGenerator::Comparison(AstNode* node, ...@@ -2728,8 +2771,22 @@ void CodeGenerator::Comparison(AstNode* node,
__ test(temp.reg(), Immediate(kSmiTagMask)); __ test(temp.reg(), Immediate(kSmiTagMask));
temp.Unuse(); temp.Unuse();
is_smi.Branch(zero, taken); is_smi.Branch(zero, taken);
// When non-smi, call out to the compare stub.
CompareStub stub(cc, strict, nan_info); // 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) {
__ cmp(left_side.reg(), Operand(right_side.reg()));
dest->true_target()->Branch(equal);
}
// Inline number comparison.
if (inline_number_compare) {
GenerateInlineNumberComparison(&left_side, &right_side, cc, dest);
}
// End of in-line compare, call out to the compare stub. Don't include
// number comparison in the stub if it was inlined.
CompareStub stub(cc, strict, nan_info, !inline_number_compare);
Result answer = frame_->CallStub(&stub, &left_side, &right_side); Result answer = frame_->CallStub(&stub, &left_side, &right_side);
if (cc == equal) { if (cc == equal) {
__ test(answer.reg(), Operand(answer.reg())); __ test(answer.reg(), Operand(answer.reg()));
...@@ -2752,6 +2809,148 @@ void CodeGenerator::Comparison(AstNode* node, ...@@ -2752,6 +2809,148 @@ void CodeGenerator::Comparison(AstNode* node,
} }
// Check that the comparison operand is a number. Jump to not_numbers jump
// target passing the left and right result if the operand is not a number.
static void CheckComparisonOperand(MacroAssembler* masm_,
Result* operand,
Result* left_side,
Result* right_side,
JumpTarget* not_numbers) {
// Perform check if operand is not known to be a number.
if (!operand->number_info().IsNumber()) {
Label done;
__ test(operand->reg(), Immediate(kSmiTagMask));
__ j(zero, &done);
__ cmp(FieldOperand(operand->reg(), HeapObject::kMapOffset),
Immediate(Factory::heap_number_map()));
not_numbers->Branch(not_equal, left_side, right_side, not_taken);
__ bind(&done);
}
}
// Load a comparison operand to the FPU stack. This assumes that the operand has
// already been checked and is a number.
static void LoadComparisonOperand(MacroAssembler* masm_,
Result* operand) {
Label done;
if (operand->number_info().IsDouble()) {
// Operand is known to be a heap number, just load it.
__ fld_d(FieldOperand(operand->reg(), HeapNumber::kValueOffset));
} else if (operand->number_info().IsSmi()) {
// Operand is known to be a smi. Convert it to double and keep the original
// smi.
__ SmiUntag(operand->reg());
__ push(operand->reg());
__ fild_s(Operand(esp, 0));
__ pop(operand->reg());
__ SmiTag(operand->reg());
} else {
// Operand type not known, check for smi otherwise assume heap number.
Label smi;
__ test(operand->reg(), Immediate(kSmiTagMask));
__ j(zero, &smi);
__ fld_d(FieldOperand(operand->reg(), HeapNumber::kValueOffset));
__ jmp(&done);
__ bind(&smi);
__ SmiUntag(operand->reg());
__ push(operand->reg());
__ fild_s(Operand(esp, 0));
__ pop(operand->reg());
__ SmiTag(operand->reg());
__ jmp(&done);
}
__ bind(&done);
}
// Load a comparison operand into into a XMM register. Jump to not_numbers jump
// target passing the left and right result if the operand is not a number.
static void LoadComparisonOperandSSE2(MacroAssembler* masm_,
Result* operand,
XMMRegister reg,
Result* left_side,
Result* right_side,
JumpTarget* not_numbers) {
Label done;
if (operand->number_info().IsDouble()) {
// Operand is known to be a heap number, just load it.
__ movdbl(reg, FieldOperand(operand->reg(), HeapNumber::kValueOffset));
} else if (operand->number_info().IsSmi()) {
// Operand is known to be a smi. Convert it to double and keep the original
// smi.
__ SmiUntag(operand->reg());
__ cvtsi2sd(reg, Operand(operand->reg()));
__ SmiTag(operand->reg());
} else {
// Operand type not known, check for smi or heap number.
Label smi;
__ test(operand->reg(), Immediate(kSmiTagMask));
__ j(zero, &smi);
if (!operand->number_info().IsNumber()) {
__ cmp(FieldOperand(operand->reg(), HeapObject::kMapOffset),
Immediate(Factory::heap_number_map()));
not_numbers->Branch(not_equal, left_side, right_side, taken);
}
__ movdbl(reg, FieldOperand(operand->reg(), HeapNumber::kValueOffset));
__ jmp(&done);
__ bind(&smi);
// Comvert smi to float and keep the original smi.
__ SmiUntag(operand->reg());
__ cvtsi2sd(reg, Operand(operand->reg()));
__ SmiTag(operand->reg());
__ jmp(&done);
}
__ bind(&done);
}
void CodeGenerator::GenerateInlineNumberComparison(Result* left_side,
Result* right_side,
Condition cc,
ControlDestination* dest) {
ASSERT(left_side->is_register());
ASSERT(right_side->is_register());
JumpTarget not_numbers;
if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope use_sse2(SSE2);
// Load left and right operand into registers xmm0 and xmm1 and compare.
LoadComparisonOperandSSE2(masm_, left_side, xmm0, left_side, right_side,
&not_numbers);
LoadComparisonOperandSSE2(masm_, right_side, xmm1, left_side, right_side,
&not_numbers);
__ comisd(xmm0, xmm1);
} else {
Label check_right, compare;
// Make sure that both comparison operands are numbers.
CheckComparisonOperand(masm_, left_side, left_side, right_side,
&not_numbers);
CheckComparisonOperand(masm_, right_side, left_side, right_side,
&not_numbers);
// Load right and left operand to FPU stack and compare.
LoadComparisonOperand(masm_, right_side);
LoadComparisonOperand(masm_, left_side);
__ FCmp();
}
// Bail out if a NaN is involved.
not_numbers.Branch(parity_even, left_side, right_side, not_taken);
// Split to destination targets based on comparison.
left_side->Unuse();
right_side->Unuse();
dest->true_target()->Branch(DoubleCondition(cc));
dest->false_target()->Jump();
not_numbers.Bind(left_side, right_side);
}
// Call the function just below TOS on the stack with the given // Call the function just below TOS on the stack with the given
// arguments. The receiver is the TOS. // arguments. The receiver is the TOS.
void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args, void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
...@@ -10877,63 +11076,70 @@ void CompareStub::Generate(MacroAssembler* masm) { ...@@ -10877,63 +11076,70 @@ void CompareStub::Generate(MacroAssembler* masm) {
__ push(edx); __ push(edx);
__ push(ecx); __ push(ecx);
// Inlined floating point compare. // Generate the number comparison code.
// Call builtin if operands are not floating point or smi. if (include_number_compare_) {
Label check_for_symbols; Label non_number_comparison;
Label unordered; Label unordered;
if (CpuFeatures::IsSupported(SSE2)) { if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope use_sse2(SSE2); CpuFeatures::Scope use_sse2(SSE2);
CpuFeatures::Scope use_cmov(CMOV); CpuFeatures::Scope use_cmov(CMOV);
FloatingPointHelper::LoadSSE2Operands(masm, &check_for_symbols); FloatingPointHelper::LoadSSE2Operands(masm, &non_number_comparison);
__ comisd(xmm0, xmm1); __ comisd(xmm0, xmm1);
// Don't base result on EFLAGS when a NaN is involved.
__ j(parity_even, &unordered, not_taken);
// Return a result of -1, 0, or 1, based on EFLAGS.
__ mov(eax, 0); // equal
__ mov(ecx, Immediate(Smi::FromInt(1)));
__ cmov(above, eax, Operand(ecx));
__ mov(ecx, Immediate(Smi::FromInt(-1)));
__ cmov(below, eax, Operand(ecx));
__ ret(2 * kPointerSize);
} else {
FloatingPointHelper::CheckFloatOperands(
masm, &non_number_comparison, ebx);
FloatingPointHelper::LoadFloatOperands(masm, ecx);
__ FCmp();
// Jump to builtin for NaN. // Don't base result on EFLAGS when a NaN is involved.
__ j(parity_even, &unordered, not_taken); __ j(parity_even, &unordered, not_taken);
__ mov(eax, 0); // equal
__ mov(ecx, Immediate(Smi::FromInt(1)));
__ cmov(above, eax, Operand(ecx));
__ mov(ecx, Immediate(Smi::FromInt(-1)));
__ cmov(below, eax, Operand(ecx));
__ ret(2 * kPointerSize);
} else {
FloatingPointHelper::CheckFloatOperands(masm, &check_for_symbols, ebx);
FloatingPointHelper::LoadFloatOperands(masm, ecx);
__ FCmp();
// Jump to builtin for NaN. Label below_label, above_label;
__ j(parity_even, &unordered, not_taken); // Return a result of -1, 0, or 1, based on EFLAGS. In all cases remove
// two arguments from the stack as they have been pushed in preparation
// of a possible runtime call.
__ j(below, &below_label, not_taken);
__ j(above, &above_label, not_taken);
Label below_lbl, above_lbl; __ xor_(eax, Operand(eax));
// Return a result of -1, 0, or 1, to indicate result of comparison. __ ret(2 * kPointerSize);
__ j(below, &below_lbl, not_taken);
__ j(above, &above_lbl, not_taken);
__ xor_(eax, Operand(eax)); // equal __ bind(&below_label);
// Both arguments were pushed in case a runtime call was needed. __ mov(eax, Immediate(Smi::FromInt(-1)));
__ ret(2 * kPointerSize); __ ret(2 * kPointerSize);
__ bind(&below_lbl); __ bind(&above_label);
__ mov(eax, Immediate(Smi::FromInt(-1))); __ mov(eax, Immediate(Smi::FromInt(1)));
__ ret(2 * kPointerSize); __ ret(2 * kPointerSize);
}
__ bind(&above_lbl); // If one of the numbers was NaN, then the result is always false.
__ mov(eax, Immediate(Smi::FromInt(1))); // The cc is never not-equal.
__ bind(&unordered);
ASSERT(cc_ != not_equal);
if (cc_ == less || cc_ == less_equal) {
__ mov(eax, Immediate(Smi::FromInt(1)));
} else {
__ mov(eax, Immediate(Smi::FromInt(-1)));
}
__ ret(2 * kPointerSize); // eax, edx were pushed __ ret(2 * kPointerSize); // eax, edx were pushed
// The number comparison code did not provide a valid result.
__ bind(&non_number_comparison);
} }
// 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) {
__ mov(eax, Immediate(Smi::FromInt(1)));
} else {
__ mov(eax, Immediate(Smi::FromInt(-1)));
}
__ ret(2 * kPointerSize); // eax, edx were pushed
// Fast negative check for symbol-to-symbol equality. // Fast negative check for symbol-to-symbol equality.
__ bind(&check_for_symbols);
Label check_for_strings; Label check_for_strings;
if (cc_ == equal) { if (cc_ == equal) {
BranchIfNonSymbol(masm, &check_for_strings, eax, ecx); BranchIfNonSymbol(masm, &check_for_strings, eax, ecx);
...@@ -11543,57 +11749,59 @@ void InstanceofStub::Generate(MacroAssembler* masm) { ...@@ -11543,57 +11749,59 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
} }
int CompareStub::MinorKey() {
// Encode the three parameters in a unique 16 bit value. To avoid duplicate
// stubs the never NaN NaN condition is only taken into account if the
// condition is equals.
ASSERT(static_cast<unsigned>(cc_) < (1 << 13));
return ConditionField::encode(static_cast<unsigned>(cc_))
| StrictField::encode(strict_)
| NeverNanNanField::encode(cc_ == equal ? never_nan_nan_ : false)
| IncludeNumberCompareField::encode(include_number_compare_);
}
// Unfortunately you have to run without snapshots to see most of these // Unfortunately you have to run without snapshots to see most of these
// names in the profile since most compare stubs end up in the snapshot. // names in the profile since most compare stubs end up in the snapshot.
const char* CompareStub::GetName() { const char* CompareStub::GetName() {
if (name_ != NULL) return name_;
const int kMaxNameLength = 100;
name_ = Bootstrapper::AllocateAutoDeletedArray(kMaxNameLength);
if (name_ == NULL) return "OOM";
const char* cc_name;
switch (cc_) { switch (cc_) {
case less: return "CompareStub_LT"; case less: cc_name = "LT"; break;
case greater: return "CompareStub_GT"; case greater: cc_name = "GT"; break;
case less_equal: return "CompareStub_LE"; case less_equal: cc_name = "LE"; break;
case greater_equal: return "CompareStub_GE"; case greater_equal: cc_name = "GE"; break;
case not_equal: { case equal: cc_name = "EQ"; break;
if (strict_) { case not_equal: cc_name = "NE"; break;
if (never_nan_nan_) { default: cc_name = "UnknownCondition"; break;
return "CompareStub_NE_STRICT_NO_NAN";
} else {
return "CompareStub_NE_STRICT";
}
} else {
if (never_nan_nan_) {
return "CompareStub_NE_NO_NAN";
} else {
return "CompareStub_NE";
}
}
}
case equal: {
if (strict_) {
if (never_nan_nan_) {
return "CompareStub_EQ_STRICT_NO_NAN";
} else {
return "CompareStub_EQ_STRICT";
}
} else {
if (never_nan_nan_) {
return "CompareStub_EQ_NO_NAN";
} else {
return "CompareStub_EQ";
}
}
}
default: return "CompareStub";
} }
}
const char* strict_name = "";
if (strict_ && (cc_ == equal || cc_ == not_equal)) {
strict_name = "_STRICT";
}
int CompareStub::MinorKey() { const char* never_nan_nan_name = "";
// Encode the three parameters in a unique 16 bit value. To avoid duplicate if (never_nan_nan_ && (cc_ == equal || cc_ == not_equal)) {
// stubs the never NaN NaN condition is only taken into account if the never_nan_nan_name = "_NO_NAN";
// condition is equals. }
ASSERT(static_cast<unsigned>(cc_) < (1 << 14));
return ConditionField::encode(static_cast<unsigned>(cc_)) const char* include_number_compare_name = "";
| StrictField::encode(strict_) if (!include_number_compare_) {
| NeverNanNanField::encode(cc_ == equal ? never_nan_nan_ : false); include_number_compare_name = "_NO_NUMBER";
}
OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
"CompareStub_%s%s%s%s",
cc_name,
strict_name,
never_nan_nan_name,
include_number_compare_name);
return name_;
} }
...@@ -12227,6 +12435,9 @@ void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm, ...@@ -12227,6 +12435,9 @@ void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
Label result_not_equal; Label result_not_equal;
Label result_greater; Label result_greater;
Label compare_lengths; Label compare_lengths;
__ IncrementCounter(&Counters::string_compare_native, 1);
// Find minimum length. // Find minimum length.
Label left_shorter; Label left_shorter;
__ mov(scratch1, FieldOperand(left, String::kLengthOffset)); __ mov(scratch1, FieldOperand(left, String::kLengthOffset));
...@@ -12324,7 +12535,6 @@ void StringCompareStub::Generate(MacroAssembler* masm) { ...@@ -12324,7 +12535,6 @@ void StringCompareStub::Generate(MacroAssembler* masm) {
__ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx, &runtime); __ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx, &runtime);
// Compare flat ascii strings. // Compare flat ascii strings.
__ IncrementCounter(&Counters::string_compare_native, 1);
GenerateCompareFlatAsciiStrings(masm, edx, eax, ecx, ebx, edi); GenerateCompareFlatAsciiStrings(masm, edx, eax, ecx, ebx, edi);
// Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater) // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
......
...@@ -528,6 +528,10 @@ class CodeGenerator: public AstVisitor { ...@@ -528,6 +528,10 @@ class CodeGenerator: public AstVisitor {
Condition cc, Condition cc,
bool strict, bool strict,
ControlDestination* destination); ControlDestination* destination);
void GenerateInlineNumberComparison(Result* left_side,
Result* right_side,
Condition cc,
ControlDestination* dest);
// To prevent long attacker-controlled byte sequences, integer constants // To prevent long attacker-controlled byte sequences, integer constants
// from the JavaScript source are loaded in two parts if they are larger // from the JavaScript source are loaded in two parts if they are larger
......
...@@ -290,6 +290,25 @@ void JumpTarget::Branch(Condition cc, Result* arg, Hint hint) { ...@@ -290,6 +290,25 @@ void JumpTarget::Branch(Condition cc, Result* arg, Hint hint) {
} }
void JumpTarget::Branch(Condition cc, Result* arg0, Result* arg1, Hint hint) {
ASSERT(cgen()->has_valid_frame());
// We want to check that non-frame registers at the call site stay in
// the same registers on the fall-through branch.
DECLARE_ARGCHECK_VARS(arg0);
DECLARE_ARGCHECK_VARS(arg1);
cgen()->frame()->Push(arg0);
cgen()->frame()->Push(arg1);
DoBranch(cc, hint);
*arg1 = cgen()->frame()->Pop();
*arg0 = cgen()->frame()->Pop();
ASSERT_ARGCHECK(arg0);
ASSERT_ARGCHECK(arg1);
}
void BreakTarget::Branch(Condition cc, Result* arg, Hint hint) { void BreakTarget::Branch(Condition cc, Result* arg, Hint hint) {
ASSERT(cgen()->has_valid_frame()); ASSERT(cgen()->has_valid_frame());
...@@ -331,6 +350,17 @@ void JumpTarget::Bind(Result* arg) { ...@@ -331,6 +350,17 @@ void JumpTarget::Bind(Result* arg) {
} }
void JumpTarget::Bind(Result* arg0, Result* arg1) {
if (cgen()->has_valid_frame()) {
cgen()->frame()->Push(arg0);
cgen()->frame()->Push(arg1);
}
DoBind();
*arg1 = cgen()->frame()->Pop();
*arg0 = cgen()->frame()->Pop();
}
void JumpTarget::AddReachingFrame(VirtualFrame* frame) { void JumpTarget::AddReachingFrame(VirtualFrame* frame) {
ASSERT(reaching_frames_.length() == merge_labels_.length()); ASSERT(reaching_frames_.length() == merge_labels_.length());
ASSERT(entry_frame_ == NULL); ASSERT(entry_frame_ == NULL);
......
...@@ -117,12 +117,17 @@ class JumpTarget : public ZoneObject { // Shadows are dynamically allocated. ...@@ -117,12 +117,17 @@ class JumpTarget : public ZoneObject { // Shadows are dynamically allocated.
// the target and the fall-through. // the target and the fall-through.
virtual void Branch(Condition cc, Hint hint = no_hint); virtual void Branch(Condition cc, Hint hint = no_hint);
virtual void Branch(Condition cc, Result* arg, Hint hint = no_hint); virtual void Branch(Condition cc, Result* arg, Hint hint = no_hint);
virtual void Branch(Condition cc,
Result* arg0,
Result* arg1,
Hint hint = no_hint);
// Bind a jump target. If there is no current frame at the binding // Bind a jump target. If there is no current frame at the binding
// site, there must be at least one frame reaching via a forward // site, there must be at least one frame reaching via a forward
// jump. // jump.
virtual void Bind(); virtual void Bind();
virtual void Bind(Result* arg); virtual void Bind(Result* arg);
virtual void Bind(Result* arg0, Result* arg1);
// Emit a call to a jump target. There must be a current frame at // Emit a call to a jump target. There must be a current frame at
// the call. The frame at the target is the same as the current // the call. The frame at the target is the same as the current
......
...@@ -9105,51 +9105,55 @@ int CompareStub::MinorKey() { ...@@ -9105,51 +9105,55 @@ int CompareStub::MinorKey() {
// Encode the three parameters in a unique 16 bit value. To avoid duplicate // Encode the three parameters in a unique 16 bit value. To avoid duplicate
// stubs the never NaN NaN condition is only taken into account if the // stubs the never NaN NaN condition is only taken into account if the
// condition is equals. // condition is equals.
ASSERT(static_cast<unsigned>(cc_) < (1 << 14)); ASSERT(static_cast<unsigned>(cc_) < (1 << 13));
return ConditionField::encode(static_cast<unsigned>(cc_)) return ConditionField::encode(static_cast<unsigned>(cc_))
| StrictField::encode(strict_) | StrictField::encode(strict_)
| NeverNanNanField::encode(cc_ == equal ? never_nan_nan_ : false); | NeverNanNanField::encode(cc_ == equal ? never_nan_nan_ : false)
| IncludeNumberCompareField::encode(include_number_compare_);
} }
// Unfortunately you have to run without snapshots to see most of these
// names in the profile since most compare stubs end up in the snapshot.
const char* CompareStub::GetName() { const char* CompareStub::GetName() {
if (name_ != NULL) return name_;
const int kMaxNameLength = 100;
name_ = Bootstrapper::AllocateAutoDeletedArray(kMaxNameLength);
if (name_ == NULL) return "OOM";
const char* cc_name;
switch (cc_) { switch (cc_) {
case less: return "CompareStub_LT"; case less: cc_name = "LT"; break;
case greater: return "CompareStub_GT"; case greater: cc_name = "GT"; break;
case less_equal: return "CompareStub_LE"; case less_equal: cc_name = "LE"; break;
case greater_equal: return "CompareStub_GE"; case greater_equal: cc_name = "GE"; break;
case not_equal: { case equal: cc_name = "EQ"; break;
if (strict_) { case not_equal: cc_name = "NE"; break;
if (never_nan_nan_) { default: cc_name = "UnknownCondition"; break;
return "CompareStub_NE_STRICT_NO_NAN"; }
} else {
return "CompareStub_NE_STRICT"; const char* strict_name = "";
} if (strict_ && (cc_ == equal || cc_ == not_equal)) {
} else { strict_name = "_STRICT";
if (never_nan_nan_) {
return "CompareStub_NE_NO_NAN";
} else {
return "CompareStub_NE";
}
}
}
case equal: {
if (strict_) {
if (never_nan_nan_) {
return "CompareStub_EQ_STRICT_NO_NAN";
} else {
return "CompareStub_EQ_STRICT";
}
} else {
if (never_nan_nan_) {
return "CompareStub_EQ_NO_NAN";
} else {
return "CompareStub_EQ";
}
}
}
default: return "CompareStub";
} }
const char* never_nan_nan_name = "";
if (never_nan_nan_ && (cc_ == equal || cc_ == not_equal)) {
never_nan_nan_name = "_NO_NAN";
}
const char* include_number_compare_name = "";
if (!include_number_compare_) {
include_number_compare_name = "_NO_NUMBER";
}
OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
"CompareStub_%s%s%s%s",
cc_name,
strict_name,
never_nan_nan_name,
include_number_compare_name);
return name_;
} }
......
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// See http://crbug.com/39160
// To reproduce the bug we need an inlined comparison (i.e. in a loop) where
// the left hand side is known to be a smi (max smi value is 1073741823). This
// test crashes with the bug.
function f(a) {
for (var i = 1073741820; i < 1073741822; i++) {
if (a < i) {
a += i;
}
}
}
f(5)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment