Truncate booleans to 0/1 in truncating t-to-i.
authorolivf@chromium.org <olivf@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 11 Oct 2013 15:13:12 +0000 (15:13 +0000)
committerolivf@chromium.org <olivf@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 11 Oct 2013 15:13:12 +0000 (15:13 +0000)
Thanks to weiliang.lin2@gmail.com for discovering the issue.

BUG=
R=bmeurer@chromium.org

Review URL: https://codereview.chromium.org/26824002

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@17166 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/arm/lithium-codegen-arm.cc
src/code-stubs.cc
src/ia32/lithium-codegen-ia32.cc
src/x64/lithium-codegen-x64.cc
test/mjsunit/bitwise-operations-bools.js [new file with mode: 0644]

index 71c1f86..801c30b 100644 (file)
@@ -4970,18 +4970,33 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
   if (instr->truncating()) {
     // Performs a truncating conversion of a floating point number as used by
     // the JS bitwise operations.
-    Label heap_number;
-    __ b(eq, &heap_number);
-    // Check for undefined. Undefined is converted to zero for truncating
-    // conversions.
+    Label no_heap_number, check_bools, check_false;
+    __ b(ne, &no_heap_number);
+    __ 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));
-    DeoptimizeIf(ne, instr->environment());
+    __ b(ne, &check_bools);
     __ mov(input_reg, Operand::Zero());
     __ b(&done);
 
-    __ bind(&heap_number);
-    __ TruncateHeapNumberToI(input_reg, scratch2);
+    __ 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->environment());
+    __ mov(input_reg, Operand::Zero());
+    __ b(&done);
   } else {
     // Deoptimize if we don't have a heap number.
     DeoptimizeIf(ne, instr->environment());
index 2283f84..2a60d15 100644 (file)
@@ -585,9 +585,9 @@ void BinaryOpStub::UpdateStatus(Handle<Object> left,
 
   if (old_state == GetExtraICState()) {
     // Tagged operations can lead to non-truncating HChanges
-    if (left->IsUndefined()) {
+    if (left->IsUndefined() || left->IsBoolean()) {
       left_state_ = GENERIC;
-    } else if (right->IsUndefined()) {
+    } else if (right->IsUndefined() || right->IsBoolean()) {
       right_state_ = GENERIC;
     } else {
       // Since the fpu is to precise, we might bail out on numbers which
@@ -602,14 +602,17 @@ void BinaryOpStub::UpdateStatus(Handle<Object> left,
 
 void BinaryOpStub::UpdateStatus(Handle<Object> object,
                                 State* state) {
+  bool is_truncating = (op_ == Token::BIT_AND || op_ == Token::BIT_OR ||
+                        op_ == Token::BIT_XOR || op_ == Token::SAR ||
+                        op_ == Token::SHL || op_ == Token::SHR);
   v8::internal::TypeInfo type = v8::internal::TypeInfo::FromValue(object);
+  if (object->IsBoolean() && is_truncating) {
+    // Booleans are converted by truncating by HChange.
+    type = TypeInfo::Integer32();
+  }
   if (object->IsUndefined()) {
     // Undefined will be automatically truncated for us by HChange.
-    type = (op_ == Token::BIT_AND || op_ == Token::BIT_OR ||
-            op_ == Token::BIT_XOR || op_ == Token::SAR ||
-            op_ == Token::SHL || op_ == Token::SHR)
-      ? TypeInfo::Integer32()
-      : TypeInfo::Double();
+    type = is_truncating ? TypeInfo::Integer32() : TypeInfo::Double();
   }
   State int_state = SmiValuesAre32Bits() ? NUMBER : INT32;
   State new_state = NONE;
index 504a8b1..648bb73 100644 (file)
@@ -5383,25 +5383,36 @@ void LCodeGen::EmitNumberUntagD(Register input_reg,
 void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr, Label* done) {
   Register input_reg = ToRegister(instr->value());
 
-
   if (instr->truncating()) {
-    Label heap_number, slow_case;
+    Label no_heap_number, check_bools, check_false;
 
     // Heap number map check.
     __ cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
            factory()->heap_number_map());
-    __ j(equal, &heap_number, Label::kNear);
+    __ j(not_equal, &no_heap_number, Label::kNear);
+    __ TruncateHeapNumberToI(input_reg, input_reg);
+    __ jmp(done);
 
-    // Check for undefined. Undefined is converted to zero for truncating
-    // conversions.
+    __ 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);
+    __ Set(input_reg, Immediate(0));
+    __ jmp(done);
+
+    __ bind(&check_bools);
+    __ cmp(input_reg, factory()->true_value());
+    __ j(not_equal, &check_false, Label::kNear);
+    __ Set(input_reg, Immediate(1));
+    __ jmp(done);
+
+    __ bind(&check_false);
+    __ cmp(input_reg, factory()->false_value());
     __ RecordComment("Deferred TaggedToI: cannot truncate");
     DeoptimizeIf(not_equal, instr->environment());
-    __ mov(input_reg, 0);
+    __ Set(input_reg, Immediate(0));
     __ jmp(done);
-
-    __ bind(&heap_number);
-    __ TruncateHeapNumberToI(input_reg, input_reg);
   } else {
     Label bailout;
     XMMRegister scratch = (instr->temp() != NULL)
index 35fdcbb..1c8bbbe 100644 (file)
@@ -4612,24 +4612,38 @@ void LCodeGen::EmitNumberUntagD(Register input_reg,
 
 
 void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr, Label* done) {
-  Label heap_number;
   Register input_reg = ToRegister(instr->value());
 
-
   if (instr->truncating()) {
+    Label no_heap_number, check_bools, check_false;
+
     // Heap number map check.
     __ CompareRoot(FieldOperand(input_reg, HeapObject::kMapOffset),
                    Heap::kHeapNumberMapRootIndex);
-    __ j(equal, &heap_number, Label::kNear);
-    // Check for undefined. Undefined is converted to zero for truncating
-    // conversions.
+    __ j(not_equal, &no_heap_number, Label::kNear);
+    __ 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);
-    DeoptimizeIf(not_equal, instr->environment());
+    __ j(not_equal, &check_bools, Label::kNear);
     __ Set(input_reg, 0);
     __ jmp(done);
 
-    __ bind(&heap_number);
-    __ TruncateHeapNumberToI(input_reg, input_reg);
+    __ 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);
+    __ RecordComment("Deferred TaggedToI: cannot truncate");
+    DeoptimizeIf(not_equal, instr->environment());
+    __ Set(input_reg, 0);
+    __ jmp(done);
   } else {
     Label bailout;
     XMMRegister xmm_temp = ToDoubleRegister(instr->temp());
diff --git a/test/mjsunit/bitwise-operations-bools.js b/test/mjsunit/bitwise-operations-bools.js
new file mode 100644 (file)
index 0000000..6c7da11
--- /dev/null
@@ -0,0 +1,94 @@
+// Copyright 2013 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.
+
+// Test bitwise operations with booleans.
+
+var t = 1;
+
+function testFalseLeftHandSide() {
+  var b;
+  if (t) b = false;
+  assertEquals(b | 1, 1);
+  assertEquals(b & 1, 0);
+  assertEquals(b ^ 1, 1);
+  assertEquals(b << 1, 0);
+  assertEquals(b >> 1, 0);
+  assertEquals(b >>> 1, 0);
+}
+
+function testFalseRightHandSide() {
+  if (t) b = false;
+  assertEquals(1 |   b, 1);
+  assertEquals(1 &   b, 0);
+  assertEquals(1 ^   b, 1);
+  assertEquals(1 <<  b, 1);
+  assertEquals(1 >>  b, 1);
+  assertEquals(1 >>> b, 1);
+}
+
+function testTrueLeftHandSide() {
+  if (t) b = true;
+  assertEquals(b | 1, 1);
+  assertEquals(b & 1, 1);
+  assertEquals(b ^ 1, 0);
+  assertEquals(b << 1, 2);
+  assertEquals(b >> 1, 0);
+  assertEquals(b >>> 1, 0);
+}
+
+function testTrueRightHandSide() {
+  if (t) b = true;
+  assertEquals(1 |   b, 1);
+  assertEquals(1 &   b, 1);
+  assertEquals(1 ^   b, 0);
+  assertEquals(1 <<  b, 2);
+  assertEquals(1 >>  b, 0);
+  assertEquals(1 >>> b, 0);
+}
+
+function testBothSides() {
+  if (t) a = true;
+  if (t) b = false;
+  assertEquals(a |   b, 1);
+  assertEquals(a &   b, 0);
+  assertEquals(a ^   b, 1);
+  assertEquals(a <<  b, 1);
+  assertEquals(a >>  b, 1);
+  assertEquals(a >>> b, 1);
+}
+
+
+testFalseLeftHandSide();
+testFalseRightHandSide();
+testTrueLeftHandSide();
+testTrueRightHandSide();
+testFalseLeftHandSide();
+testFalseRightHandSide();
+testTrueLeftHandSide();
+testTrueRightHandSide();
+testBothSides();
+testBothSides();