Commit 7d383be9 authored by bmeurer's avatar bmeurer Committed by Commit bot

[crankshaft] Support all oddballs for truncating TaggedToI changes.

For inputs to truncating binary operations like <<, | or >>>, support
all Oddballs not just undefined, true and false. This unifies treatment
of these truncations in Crankshaft and TurboFan, and is very easy
nowadays, since the memory layout of Oddball and HeapNumber is
compatible.

R=yangguo@chromium.org
BUG=v8:5400

Review-Url: https://codereview.chromium.org/2452193003
Cr-Commit-Position: refs/heads/master@{#40608}
parent 9bc155b9
...@@ -318,7 +318,7 @@ OsrGuardType OsrGuardTypeOf(Operator const* op) { ...@@ -318,7 +318,7 @@ OsrGuardType OsrGuardTypeOf(Operator const* op) {
V(LostPrecisionOrNaN) \ V(LostPrecisionOrNaN) \
V(NoReason) \ V(NoReason) \
V(NotAHeapNumber) \ V(NotAHeapNumber) \
V(NotAHeapNumberUndefinedBoolean) \ V(NotANumberOrOddball) \
V(NotASmi) \ V(NotASmi) \
V(OutOfBounds) \ V(OutOfBounds) \
V(WrongInstanceType) \ V(WrongInstanceType) \
......
...@@ -1818,8 +1818,7 @@ EffectControlLinearizer::BuildCheckedHeapNumberOrOddballToFloat64( ...@@ -1818,8 +1818,7 @@ EffectControlLinearizer::BuildCheckedHeapNumberOrOddballToFloat64(
graph()->NewNode(machine()->Word32Equal(), instance_type, graph()->NewNode(machine()->Word32Equal(), instance_type,
jsgraph()->Int32Constant(ODDBALL_TYPE)); jsgraph()->Int32Constant(ODDBALL_TYPE));
if_false = efalse = graph()->NewNode( if_false = efalse = graph()->NewNode(
common()->DeoptimizeUnless( common()->DeoptimizeUnless(DeoptimizeReason::kNotANumberOrOddball),
DeoptimizeReason::kNotAHeapNumberUndefinedBoolean),
check_oddball, frame_state, efalse, if_false); check_oddball, frame_state, efalse, if_false);
STATIC_ASSERT(HeapNumber::kValueOffset == Oddball::kToNumberRawOffset); STATIC_ASSERT(HeapNumber::kValueOffset == Oddball::kToNumberRawOffset);
......
...@@ -4538,34 +4538,12 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) { ...@@ -4538,34 +4538,12 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
__ cmp(scratch1, Operand(ip)); __ cmp(scratch1, Operand(ip));
if (instr->truncating()) { if (instr->truncating()) {
// Performs a truncating conversion of a floating point number as used by Label truncate;
// the JS bitwise operations. __ b(eq, &truncate);
Label no_heap_number, check_bools, check_false; __ CompareInstanceType(scratch1, scratch1, ODDBALL_TYPE);
__ b(ne, &no_heap_number); DeoptimizeIf(ne, instr, DeoptimizeReason::kNotANumberOrOddball);
__ bind(&truncate);
__ TruncateHeapNumberToI(input_reg, scratch2); __ TruncateHeapNumberToI(input_reg, scratch2);
__ b(&done);
// Check for Oddballs. Undefined/False is converted to zero and True to one
// for truncating conversions.
__ bind(&no_heap_number);
__ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
__ cmp(scratch2, Operand(ip));
__ b(ne, &check_bools);
__ mov(input_reg, Operand::Zero());
__ b(&done);
__ bind(&check_bools);
__ LoadRoot(ip, Heap::kTrueValueRootIndex);
__ cmp(scratch2, Operand(ip));
__ b(ne, &check_false);
__ mov(input_reg, Operand(1));
__ b(&done);
__ bind(&check_false);
__ LoadRoot(ip, Heap::kFalseValueRootIndex);
__ cmp(scratch2, Operand(ip));
DeoptimizeIf(ne, instr, DeoptimizeReason::kNotAHeapNumberUndefinedBoolean);
__ mov(input_reg, Operand::Zero());
} else { } else {
DeoptimizeIf(ne, instr, DeoptimizeReason::kNotAHeapNumber); DeoptimizeIf(ne, instr, DeoptimizeReason::kNotAHeapNumber);
......
...@@ -5181,30 +5181,18 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr, ...@@ -5181,30 +5181,18 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr,
Label done; Label done;
if (instr->truncating()) { if (instr->truncating()) {
UseScratchRegisterScope temps(masm());
Register output = ToRegister(instr->result()); Register output = ToRegister(instr->result());
Label check_bools; Register input_map = temps.AcquireX();
Register input_instance_type = input_map;
// If it's not a heap number, jump to undefined check. Label truncate;
__ JumpIfNotHeapNumber(input, &check_bools); __ CompareObjectType(input, input_map, input_instance_type,
HEAP_NUMBER_TYPE);
// A heap number: load value and convert to int32 using truncating function. __ B(eq, &truncate);
__ Cmp(input_instance_type, ODDBALL_TYPE);
DeoptimizeIf(ne, instr, DeoptimizeReason::kNotANumberOrOddball);
__ Bind(&truncate);
__ TruncateHeapNumberToI(output, input); __ TruncateHeapNumberToI(output, input);
__ B(&done);
__ Bind(&check_bools);
Register true_root = output;
Register false_root = scratch1;
__ LoadTrueFalseRoots(true_root, false_root);
__ Cmp(input, true_root);
__ Cset(output, eq);
__ Ccmp(input, false_root, ZFlag, ne);
__ B(eq, &done);
// Output contains zero, undefined is converted to zero for truncating
// conversions.
DeoptimizeIfNotRoot(input, Heap::kUndefinedValueRootIndex, instr,
DeoptimizeReason::kNotAHeapNumberUndefinedBoolean);
} else { } else {
Register output = ToRegister32(instr->result()); Register output = ToRegister32(instr->result());
DoubleRegister dbl_scratch2 = ToDoubleRegister(temp2); DoubleRegister dbl_scratch2 = ToDoubleRegister(temp2);
......
...@@ -4308,34 +4308,18 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr, Label* done) { ...@@ -4308,34 +4308,18 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr, Label* done) {
__ lea(input_reg, Operand(input_reg, times_2, kHeapObjectTag)); __ lea(input_reg, Operand(input_reg, times_2, kHeapObjectTag));
if (instr->truncating()) { if (instr->truncating()) {
Label no_heap_number, check_bools, check_false; Label truncate;
Label::Distance truncate_distance =
// Heap number map check. DeoptEveryNTimes() ? Label::kFar : Label::kNear;
__ cmp(FieldOperand(input_reg, HeapObject::kMapOffset), __ cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
factory()->heap_number_map()); factory()->heap_number_map());
__ j(not_equal, &no_heap_number, Label::kNear); __ j(equal, &truncate, truncate_distance);
__ push(input_reg);
__ CmpObjectType(input_reg, ODDBALL_TYPE, input_reg);
__ pop(input_reg);
DeoptimizeIf(not_equal, instr, DeoptimizeReason::kNotANumberOrOddball);
__ bind(&truncate);
__ TruncateHeapNumberToI(input_reg, input_reg); __ TruncateHeapNumberToI(input_reg, input_reg);
__ jmp(done);
__ bind(&no_heap_number);
// Check for Oddballs. Undefined/False is converted to zero and True to one
// for truncating conversions.
__ cmp(input_reg, factory()->undefined_value());
__ j(not_equal, &check_bools, Label::kNear);
__ Move(input_reg, Immediate(0));
__ jmp(done);
__ bind(&check_bools);
__ cmp(input_reg, factory()->true_value());
__ j(not_equal, &check_false, Label::kNear);
__ Move(input_reg, Immediate(1));
__ jmp(done);
__ bind(&check_false);
__ cmp(input_reg, factory()->false_value());
DeoptimizeIf(not_equal, instr,
DeoptimizeReason::kNotAHeapNumberUndefinedBoolean);
__ Move(input_reg, Immediate(0));
} else { } else {
XMMRegister scratch = ToDoubleRegister(instr->temp()); XMMRegister scratch = ToDoubleRegister(instr->temp());
DCHECK(!scratch.is(xmm0)); DCHECK(!scratch.is(xmm0));
......
...@@ -4487,36 +4487,14 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) { ...@@ -4487,36 +4487,14 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
// of the if. // of the if.
if (instr->truncating()) { if (instr->truncating()) {
// Performs a truncating conversion of a floating point number as used by Label truncate;
// the JS bitwise operations. __ Branch(USE_DELAY_SLOT, &truncate, eq, scratch1, Operand(at));
Label no_heap_number, check_bools, check_false;
// Check HeapNumber map.
__ Branch(USE_DELAY_SLOT, &no_heap_number, ne, scratch1, Operand(at));
__ mov(scratch2, input_reg); // In delay slot. __ mov(scratch2, input_reg); // In delay slot.
__ lbu(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
DeoptimizeIf(ne, instr, DeoptimizeReason::kNotANumberOrOddball, scratch1,
Operand(ODDBALL_TYPE));
__ bind(&truncate);
__ TruncateHeapNumberToI(input_reg, scratch2); __ TruncateHeapNumberToI(input_reg, scratch2);
__ Branch(&done);
// Check for Oddballs. Undefined/False is converted to zero and True to one
// for truncating conversions.
__ bind(&no_heap_number);
__ LoadRoot(at, Heap::kUndefinedValueRootIndex);
__ Branch(&check_bools, ne, input_reg, Operand(at));
DCHECK(ToRegister(instr->result()).is(input_reg));
__ Branch(USE_DELAY_SLOT, &done);
__ mov(input_reg, zero_reg); // In delay slot.
__ bind(&check_bools);
__ LoadRoot(at, Heap::kTrueValueRootIndex);
__ Branch(&check_false, ne, scratch2, Operand(at));
__ Branch(USE_DELAY_SLOT, &done);
__ li(input_reg, Operand(1)); // In delay slot.
__ bind(&check_false);
__ LoadRoot(at, Heap::kFalseValueRootIndex);
DeoptimizeIf(ne, instr, DeoptimizeReason::kNotAHeapNumberUndefinedBoolean,
scratch2, Operand(at));
__ Branch(USE_DELAY_SLOT, &done);
__ mov(input_reg, zero_reg); // In delay slot.
} else { } else {
DeoptimizeIf(ne, instr, DeoptimizeReason::kNotAHeapNumber, scratch1, DeoptimizeIf(ne, instr, DeoptimizeReason::kNotAHeapNumber, scratch1,
Operand(at)); Operand(at));
......
...@@ -4695,36 +4695,14 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) { ...@@ -4695,36 +4695,14 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
// of the if. // of the if.
if (instr->truncating()) { if (instr->truncating()) {
// Performs a truncating conversion of a floating point number as used by Label truncate;
// the JS bitwise operations. __ Branch(USE_DELAY_SLOT, &truncate, eq, scratch1, Operand(at));
Label no_heap_number, check_bools, check_false;
// Check HeapNumber map.
__ Branch(USE_DELAY_SLOT, &no_heap_number, ne, scratch1, Operand(at));
__ mov(scratch2, input_reg); // In delay slot. __ mov(scratch2, input_reg); // In delay slot.
__ lbu(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
DeoptimizeIf(ne, instr, DeoptimizeReason::kNotANumberOrOddball, scratch1,
Operand(ODDBALL_TYPE));
__ bind(&truncate);
__ TruncateHeapNumberToI(input_reg, scratch2); __ TruncateHeapNumberToI(input_reg, scratch2);
__ Branch(&done);
// Check for Oddballs. Undefined/False is converted to zero and True to one
// for truncating conversions.
__ bind(&no_heap_number);
__ LoadRoot(at, Heap::kUndefinedValueRootIndex);
__ Branch(&check_bools, ne, input_reg, Operand(at));
DCHECK(ToRegister(instr->result()).is(input_reg));
__ Branch(USE_DELAY_SLOT, &done);
__ mov(input_reg, zero_reg); // In delay slot.
__ bind(&check_bools);
__ LoadRoot(at, Heap::kTrueValueRootIndex);
__ Branch(&check_false, ne, scratch2, Operand(at));
__ Branch(USE_DELAY_SLOT, &done);
__ li(input_reg, Operand(1)); // In delay slot.
__ bind(&check_false);
__ LoadRoot(at, Heap::kFalseValueRootIndex);
DeoptimizeIf(ne, instr, DeoptimizeReason::kNotAHeapNumberUndefinedBoolean,
scratch2, Operand(at));
__ Branch(USE_DELAY_SLOT, &done);
__ mov(input_reg, zero_reg); // In delay slot.
} else { } else {
DeoptimizeIf(ne, instr, DeoptimizeReason::kNotAHeapNumber, scratch1, DeoptimizeIf(ne, instr, DeoptimizeReason::kNotAHeapNumber, scratch1,
Operand(at)); Operand(at));
......
...@@ -4594,34 +4594,17 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr, Label* done) { ...@@ -4594,34 +4594,17 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr, Label* done) {
Register input_reg = ToRegister(instr->value()); Register input_reg = ToRegister(instr->value());
if (instr->truncating()) { if (instr->truncating()) {
Label no_heap_number, check_bools, check_false; Register input_map_reg = kScratchRegister;
Label truncate;
// Heap number map check. Label::Distance truncate_distance =
__ CompareRoot(FieldOperand(input_reg, HeapObject::kMapOffset), DeoptEveryNTimes() ? Label::kFar : Label::kNear;
Heap::kHeapNumberMapRootIndex); __ movp(input_map_reg, FieldOperand(input_reg, HeapObject::kMapOffset));
__ j(not_equal, &no_heap_number, Label::kNear); __ JumpIfRoot(input_map_reg, Heap::kHeapNumberMapRootIndex, &truncate,
truncate_distance);
__ CmpInstanceType(input_map_reg, ODDBALL_TYPE);
DeoptimizeIf(not_equal, instr, DeoptimizeReason::kNotANumberOrOddball);
__ bind(&truncate);
__ TruncateHeapNumberToI(input_reg, input_reg); __ TruncateHeapNumberToI(input_reg, input_reg);
__ jmp(done);
__ bind(&no_heap_number);
// Check for Oddballs. Undefined/False is converted to zero and True to one
// for truncating conversions.
__ CompareRoot(input_reg, Heap::kUndefinedValueRootIndex);
__ j(not_equal, &check_bools, Label::kNear);
__ Set(input_reg, 0);
__ jmp(done);
__ bind(&check_bools);
__ CompareRoot(input_reg, Heap::kTrueValueRootIndex);
__ j(not_equal, &check_false, Label::kNear);
__ Set(input_reg, 1);
__ jmp(done);
__ bind(&check_false);
__ CompareRoot(input_reg, Heap::kFalseValueRootIndex);
DeoptimizeIf(not_equal, instr,
DeoptimizeReason::kNotAHeapNumberUndefinedBoolean);
__ Set(input_reg, 0);
} else { } else {
XMMRegister scratch = ToDoubleRegister(instr->temp()); XMMRegister scratch = ToDoubleRegister(instr->temp());
DCHECK(!scratch.is(double_scratch0())); DCHECK(!scratch.is(double_scratch0()));
......
...@@ -45,9 +45,9 @@ namespace internal { ...@@ -45,9 +45,9 @@ namespace internal {
V(NegativeValue, "negative value") \ V(NegativeValue, "negative value") \
V(NoCache, "no cache") \ V(NoCache, "no cache") \
V(NotAHeapNumber, "not a heap number") \ V(NotAHeapNumber, "not a heap number") \
V(NotAHeapNumberUndefinedBoolean, "not a heap number/undefined/true/false") \
V(NotAHeapNumberUndefined, "not a heap number/undefined") \ V(NotAHeapNumberUndefined, "not a heap number/undefined") \
V(NotAJavaScriptObject, "not a JavaScript object") \ V(NotAJavaScriptObject, "not a JavaScript object") \
V(NotANumberOrOddball, "not a Number or Oddball") \
V(NotASmi, "not a Smi") \ V(NotASmi, "not a Smi") \
V(OutOfBounds, "out of bounds") \ V(OutOfBounds, "out of bounds") \
V(OutsideOfRange, "Outside of range") \ V(OutsideOfRange, "Outside of range") \
......
...@@ -270,8 +270,8 @@ BinaryOpICState::Kind BinaryOpICState::UpdateKind(Handle<Object> object, ...@@ -270,8 +270,8 @@ BinaryOpICState::Kind BinaryOpICState::UpdateKind(Handle<Object> object,
Kind kind) const { Kind kind) const {
Kind new_kind = GENERIC; Kind new_kind = GENERIC;
bool is_truncating = Token::IsTruncatingBinaryOp(op()); bool is_truncating = Token::IsTruncatingBinaryOp(op());
if (object->IsBoolean() && is_truncating) { if (object->IsOddball() && is_truncating) {
// Booleans will be automatically truncated by HChange. // Oddballs will be automatically truncated by HChange.
new_kind = INT32; new_kind = INT32;
} else if (object->IsUndefined(isolate_)) { } else if (object->IsUndefined(isolate_)) {
// Undefined will be automatically truncated by HChange. // Undefined will be automatically truncated by HChange.
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
(function() {
function foo(x, y) { return x << y; }
foo(1.1, 0.1);
%BaselineFunctionOnNextCall(foo);
foo(0.1, 1.1);
%OptimizeFunctionOnNextCall(foo);
foo(undefined, 1.1);
assertOptimized(foo);
foo(1.1, undefined);
assertOptimized(foo);
foo(null, 1.1);
assertOptimized(foo);
foo(1.1, null);
assertOptimized(foo);
foo(true, 1.1);
assertOptimized(foo);
foo(1.1, true);
assertOptimized(foo);
foo(false, 1.1);
assertOptimized(foo);
foo(1.1, false);
assertOptimized(foo);
})();
(function() {
function foo(x, y) { return x >> y; }
foo(1.1, 0.1);
%BaselineFunctionOnNextCall(foo);
foo(0.1, 1.1);
%OptimizeFunctionOnNextCall(foo);
foo(undefined, 1.1);
assertOptimized(foo);
foo(1.1, undefined);
assertOptimized(foo);
foo(null, 1.1);
assertOptimized(foo);
foo(1.1, null);
assertOptimized(foo);
foo(true, 1.1);
assertOptimized(foo);
foo(1.1, true);
assertOptimized(foo);
foo(false, 1.1);
assertOptimized(foo);
foo(1.1, false);
assertOptimized(foo);
})();
(function() {
function foo(x, y) { return x >>> y; }
foo(1.1, 0.1);
%BaselineFunctionOnNextCall(foo);
foo(0.1, 1.1);
%OptimizeFunctionOnNextCall(foo);
foo(undefined, 1.1);
assertOptimized(foo);
foo(1.1, undefined);
assertOptimized(foo);
foo(null, 1.1);
assertOptimized(foo);
foo(1.1, null);
assertOptimized(foo);
foo(true, 1.1);
assertOptimized(foo);
foo(1.1, true);
assertOptimized(foo);
foo(false, 1.1);
assertOptimized(foo);
foo(1.1, false);
assertOptimized(foo);
})();
(function() {
function foo(x, y) { return x ^ y; }
foo(1.1, 0.1);
%BaselineFunctionOnNextCall(foo);
foo(0.1, 1.1);
%OptimizeFunctionOnNextCall(foo);
foo(undefined, 1.1);
assertOptimized(foo);
foo(1.1, undefined);
assertOptimized(foo);
foo(null, 1.1);
assertOptimized(foo);
foo(1.1, null);
assertOptimized(foo);
foo(true, 1.1);
assertOptimized(foo);
foo(1.1, true);
assertOptimized(foo);
foo(false, 1.1);
assertOptimized(foo);
foo(1.1, false);
assertOptimized(foo);
})();
(function() {
function foo(x, y) { return x | y; }
foo(1.1, 0.1);
%BaselineFunctionOnNextCall(foo);
foo(0.1, 1.1);
%OptimizeFunctionOnNextCall(foo);
foo(undefined, 1.1);
assertOptimized(foo);
foo(1.1, undefined);
assertOptimized(foo);
foo(null, 1.1);
assertOptimized(foo);
foo(1.1, null);
assertOptimized(foo);
foo(true, 1.1);
assertOptimized(foo);
foo(1.1, true);
assertOptimized(foo);
foo(false, 1.1);
assertOptimized(foo);
foo(1.1, false);
assertOptimized(foo);
})();
(function() {
function foo(x, y) { return x & y; }
foo(1.1, 0.1);
%BaselineFunctionOnNextCall(foo);
foo(0.1, 1.1);
%OptimizeFunctionOnNextCall(foo);
foo(undefined, 1.1);
assertOptimized(foo);
foo(1.1, undefined);
assertOptimized(foo);
foo(null, 1.1);
assertOptimized(foo);
foo(1.1, null);
assertOptimized(foo);
foo(true, 1.1);
assertOptimized(foo);
foo(1.1, true);
assertOptimized(foo);
foo(false, 1.1);
assertOptimized(foo);
foo(1.1, false);
assertOptimized(foo);
})();
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