TF: Add ConvertFloat64ToUint32 and ConvertUint32ToFloat64 machine operators.
authortitzer@chromium.org <titzer@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 31 Jul 2014 11:45:22 +0000 (11:45 +0000)
committertitzer@chromium.org <titzer@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 31 Jul 2014 11:45:22 +0000 (11:45 +0000)
R=bmeurer@chromium.org
BUG=

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

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

14 files changed:
src/compiler/arm/instruction-selector-arm.cc
src/compiler/arm64/code-generator-arm64.cc
src/compiler/arm64/instruction-codes-arm64.h
src/compiler/arm64/instruction-selector-arm64.cc
src/compiler/ia32/code-generator-ia32.cc
src/compiler/ia32/instruction-codes-ia32.h
src/compiler/ia32/instruction-selector-ia32.cc
src/compiler/instruction-selector.cc
src/compiler/machine-node-factory.h
src/compiler/machine-operator.h
src/compiler/x64/code-generator-x64.cc
src/compiler/x64/instruction-codes-x64.h
src/compiler/x64/instruction-selector-x64.cc
test/cctest/compiler/test-run-machops.cc

index 6f8c22fb9076a0771d193d13c67b9c149b3361ed..081ad80c7bcb6620d7ad10a05c07bf181de6adf0 100644 (file)
@@ -659,6 +659,13 @@ void InstructionSelector::VisitConvertInt32ToFloat64(Node* node) {
 }
 
 
+void InstructionSelector::VisitConvertUint32ToFloat64(Node* node) {
+  ArmOperandGenerator g(this);
+  Emit(kArmVcvtF64U32, g.DefineAsDoubleRegister(node),
+       g.UseRegister(node->InputAt(0)));
+}
+
+
 void InstructionSelector::VisitConvertFloat64ToInt32(Node* node) {
   ArmOperandGenerator g(this);
   Emit(kArmVcvtS32F64, g.DefineAsRegister(node),
@@ -666,6 +673,13 @@ void InstructionSelector::VisitConvertFloat64ToInt32(Node* node) {
 }
 
 
+void InstructionSelector::VisitConvertFloat64ToUint32(Node* node) {
+  ArmOperandGenerator g(this);
+  Emit(kArmVcvtU32F64, g.DefineAsRegister(node),
+       g.UseDoubleRegister(node->InputAt(0)));
+}
+
+
 void InstructionSelector::VisitFloat64Add(Node* node) {
   ArmOperandGenerator g(this);
   Int32BinopMatcher m(node);
index 28c7c9756fe523e6004f7dbb807fde8bd3474e86..87a2f41c9e98f321278ecbe72e53b70a7a3bcb51 100644 (file)
@@ -378,9 +378,15 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
     case kArm64Float64ToInt32:
       __ Fcvtzs(i.OutputRegister32(), i.InputDoubleRegister(0));
       break;
+    case kArm64Float64ToUint32:
+      __ Fcvtzu(i.OutputRegister32(), i.InputDoubleRegister(0));
+      break;
     case kArm64Int32ToFloat64:
       __ Scvtf(i.OutputDoubleRegister(), i.InputRegister32(0));
       break;
+    case kArm64Uint32ToFloat64:
+      __ Ucvtf(i.OutputDoubleRegister(), i.InputRegister32(0));
+      break;
     case kArm64LoadWord8:
       __ Ldrb(i.OutputRegister(), i.MemoryOperand());
       break;
index 7241e9a6b752176a6ec7e005eda0e8d71de779ad..2d71c02ef03a02a7ae72ae0339e8c9d251389281 100644 (file)
@@ -63,7 +63,9 @@ namespace compiler {
   V(Arm64Int32ToInt64)             \
   V(Arm64Int64ToInt32)             \
   V(Arm64Float64ToInt32)           \
+  V(Arm64Float64ToUint32)          \
   V(Arm64Int32ToFloat64)           \
+  V(Arm64Uint32ToFloat64)          \
   V(Arm64Float64Load)              \
   V(Arm64Float64Store)             \
   V(Arm64LoadWord8)                \
index 7352c46f022b17e8f01cede143fb28ad45915da1..fee086ca536ca524839b0cf59f9205e6c7901bd1 100644 (file)
@@ -395,6 +395,13 @@ void InstructionSelector::VisitConvertInt32ToFloat64(Node* node) {
 }
 
 
+void InstructionSelector::VisitConvertUint32ToFloat64(Node* node) {
+  Arm64OperandGenerator g(this);
+  Emit(kArm64Uint32ToFloat64, g.DefineAsDoubleRegister(node),
+       g.UseRegister(node->InputAt(0)));
+}
+
+
 void InstructionSelector::VisitConvertFloat64ToInt32(Node* node) {
   Arm64OperandGenerator g(this);
   Emit(kArm64Float64ToInt32, g.DefineAsRegister(node),
@@ -402,6 +409,13 @@ void InstructionSelector::VisitConvertFloat64ToInt32(Node* node) {
 }
 
 
+void InstructionSelector::VisitConvertFloat64ToUint32(Node* node) {
+  Arm64OperandGenerator g(this);
+  Emit(kArm64Float64ToUint32, g.DefineAsRegister(node),
+       g.UseDoubleRegister(node->InputAt(0)));
+}
+
+
 void InstructionSelector::VisitFloat64Add(Node* node) {
   VisitRRRFloat64(this, kArm64Float64Add, node);
 }
index 38f7d4b0c100f51c1e464786cb57f35df22d18a6..37ad39dd4eb79fb7be5d0b93c25d2aef58f60b37 100644 (file)
@@ -315,9 +315,22 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
     case kSSEFloat64ToInt32:
       __ cvttsd2si(i.OutputRegister(), i.InputOperand(0));
       break;
+    case kSSEFloat64ToUint32: {
+      XMMRegister scratch = xmm0;
+      __ Move(scratch, -2147483648.0);
+      // TODO(turbofan): IA32 SSE subsd() should take an operand.
+      __ addsd(scratch, i.InputDoubleRegister(0));
+      __ cvttsd2si(i.OutputRegister(), scratch);
+      __ add(i.OutputRegister(), Immediate(0x80000000));
+      break;
+    }
     case kSSEInt32ToFloat64:
       __ cvtsi2sd(i.OutputDoubleRegister(), i.InputOperand(0));
       break;
+    case kSSEUint32ToFloat64:
+      // TODO(turbofan): IA32 SSE LoadUint32() should take an operand.
+      __ LoadUint32(i.OutputDoubleRegister(), i.InputRegister(0));
+      break;
     case kSSELoad:
       __ movsd(i.OutputDoubleRegister(), i.MemoryOperand());
       break;
index 82fca55b67c3e93d03dee1d2a8d1a0bb974f0ed5..f175ebb559b4a9196fd9d3d677a2e9607980c90f 100644 (file)
@@ -39,7 +39,9 @@ namespace compiler {
   V(SSEFloat64Div)                 \
   V(SSEFloat64Mod)                 \
   V(SSEFloat64ToInt32)             \
+  V(SSEFloat64ToUint32)            \
   V(SSEInt32ToFloat64)             \
+  V(SSEUint32ToFloat64)            \
   V(SSELoad)                       \
   V(SSEStore)                      \
   V(IA32LoadWord8)                 \
index 8d6ca1ef5430c4245451c22522ad7c1093eccd2b..c10efefa34c7adfb04322c10c23a5e217165b04d 100644 (file)
@@ -326,12 +326,28 @@ void InstructionSelector::VisitConvertInt32ToFloat64(Node* node) {
 }
 
 
+void InstructionSelector::VisitConvertUint32ToFloat64(Node* node) {
+  IA32OperandGenerator g(this);
+  // TODO(turbofan): IA32 SSE LoadUint32() should take an operand.
+  Emit(kSSEUint32ToFloat64, g.DefineAsDoubleRegister(node),
+       g.UseRegister(node->InputAt(0)));
+}
+
+
 void InstructionSelector::VisitConvertFloat64ToInt32(Node* node) {
   IA32OperandGenerator g(this);
   Emit(kSSEFloat64ToInt32, g.DefineAsRegister(node), g.Use(node->InputAt(0)));
 }
 
 
+void InstructionSelector::VisitConvertFloat64ToUint32(Node* node) {
+  IA32OperandGenerator g(this);
+  // TODO(turbofan): IA32 SSE subsd() should take an operand.
+  Emit(kSSEFloat64ToUint32, g.DefineAsRegister(node),
+       g.UseDoubleRegister(node->InputAt(0)));
+}
+
+
 void InstructionSelector::VisitFloat64Add(Node* node) {
   IA32OperandGenerator g(this);
   Emit(kSSEFloat64Add, g.DefineSameAsFirst(node),
index dd3663d2aaf3a0fc68079ab55fe63f9c7eb1655f..d488ac40012f548abd8fdff1f46d74793d739feb 100644 (file)
@@ -558,8 +558,12 @@ void InstructionSelector::VisitNode(Node* node) {
       return VisitConvertInt64ToInt32(node);
     case IrOpcode::kConvertInt32ToFloat64:
       return MarkAsDouble(node), VisitConvertInt32ToFloat64(node);
+    case IrOpcode::kConvertUint32ToFloat64:
+      return MarkAsDouble(node), VisitConvertUint32ToFloat64(node);
     case IrOpcode::kConvertFloat64ToInt32:
       return VisitConvertFloat64ToInt32(node);
+    case IrOpcode::kConvertFloat64ToUint32:
+      return VisitConvertFloat64ToUint32(node);
     case IrOpcode::kFloat64Add:
       return MarkAsDouble(node), VisitFloat64Add(node);
     case IrOpcode::kFloat64Sub:
index 5a6ce27e33a20af4cffd72a85e9ccef590418966..b0e153181feb8d143c2e6341b54dc6319c7c54f1 100644 (file)
@@ -332,9 +332,15 @@ class MachineNodeFactory {
   Node* ConvertInt32ToFloat64(Node* a) {
     return NEW_NODE_1(MACHINE()->ConvertInt32ToFloat64(), a);
   }
+  Node* ConvertUint32ToFloat64(Node* a) {
+    return NEW_NODE_1(MACHINE()->ConvertUint32ToFloat64(), a);
+  }
   Node* ConvertFloat64ToInt32(Node* a) {
     return NEW_NODE_1(MACHINE()->ConvertFloat64ToInt32(), a);
   }
+  Node* ConvertFloat64ToUint32(Node* a) {
+    return NEW_NODE_1(MACHINE()->ConvertFloat64ToUint32(), a);
+  }
 
 #ifdef MACHINE_ASSEMBLER_SUPPORTS_CALL_C
   // Call to C.
index 88f257c3eef379369cfed9351611110630f9159b..deda9581ffde34a94aa3ba9880ac59076487a023 100644 (file)
@@ -136,18 +136,25 @@ class MachineOperatorBuilder {
 
   Operator* ConvertInt32ToInt64() { UNOP(ConvertInt32ToInt64); }
   Operator* ConvertInt64ToInt32() { UNOP(ConvertInt64ToInt32); }
+
+  // Convert representation of integers between float64 and int32/uint32.
+  // The precise rounding mode and handling of out of range inputs are *not*
+  // defined for these operators, since they are intended only for use with
+  // integers.
+  // TODO(titzer): rename ConvertXXX to ChangeXXX in machine operators.
   Operator* ConvertInt32ToFloat64() { UNOP(ConvertInt32ToFloat64); }
   Operator* ConvertUint32ToFloat64() { UNOP(ConvertUint32ToFloat64); }
-  // TODO(titzer): add rounding mode to floating point conversion.
   Operator* ConvertFloat64ToInt32() { UNOP(ConvertFloat64ToInt32); }
   Operator* ConvertFloat64ToUint32() { UNOP(ConvertFloat64ToUint32); }
 
-  // TODO(titzer): do we need different rounding modes for float arithmetic?
+  // Floating point operators always operate with IEEE 754 round-to-nearest.
   Operator* Float64Add() { BINOP_C(Float64Add); }
   Operator* Float64Sub() { BINOP(Float64Sub); }
   Operator* Float64Mul() { BINOP_C(Float64Mul); }
   Operator* Float64Div() { BINOP(Float64Div); }
   Operator* Float64Mod() { BINOP(Float64Mod); }
+
+  // Floating point comparisons complying to IEEE 754.
   Operator* Float64Equal() { BINOP_C(Float64Equal); }
   Operator* Float64LessThan() { BINOP(Float64LessThan); }
   Operator* Float64LessThanOrEqual() { BINOP(Float64LessThanOrEqual); }
index 7420841635d9594ed329ae5d2d56a781372f3d2c..abe921b045f0a69596a8ddb5e8094de9a88b54e6 100644 (file)
@@ -493,6 +493,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
       }
       break;
     }
+    case kSSEFloat64ToUint32: {
+      // TODO(turbofan): X64 SSE cvttsd2siq should support operands.
+      __ cvttsd2siq(i.OutputRegister(), i.InputDoubleRegister(0));
+      break;
+    }
     case kSSEInt32ToFloat64: {
       RegisterOrOperand input = i.InputRegisterOrOperand(0);
       if (input.type == kRegister) {
@@ -502,6 +507,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
       }
       break;
     }
+    case kSSEUint32ToFloat64: {
+      // TODO(turbofan): X64 SSE cvtqsi2sd should support operands.
+      __ cvtqsi2sd(i.OutputDoubleRegister(), i.InputRegister(0));
+      break;
+    }
+
     case kSSELoad:
       __ movsd(i.OutputDoubleRegister(), i.MemoryOperand());
       break;
index 307a184b11b5f3e5d23fe9758030398f6622d406..8ba33ab10d1013330eb6210bd7d6cf3acea143f5 100644 (file)
@@ -57,7 +57,9 @@ namespace compiler {
   V(X64Int32ToInt64)               \
   V(X64Int64ToInt32)               \
   V(SSEFloat64ToInt32)             \
+  V(SSEFloat64ToUint32)            \
   V(SSEInt32ToFloat64)             \
+  V(SSEUint32ToFloat64)            \
   V(SSELoad)                       \
   V(SSEStore)                      \
   V(X64LoadWord8)                  \
index 7d71ace1e69d062bbddf055628bd057bf5b9f397..d40bc6750bdc29970a88222f620d9be3bdd18056 100644 (file)
@@ -450,12 +450,28 @@ void InstructionSelector::VisitConvertInt32ToFloat64(Node* node) {
 }
 
 
+void InstructionSelector::VisitConvertUint32ToFloat64(Node* node) {
+  X64OperandGenerator g(this);
+  // TODO(turbofan): X64 SSE cvtqsi2sd should support operands.
+  Emit(kSSEUint32ToFloat64, g.DefineAsDoubleRegister(node),
+       g.UseRegister(node->InputAt(0)));
+}
+
+
 void InstructionSelector::VisitConvertFloat64ToInt32(Node* node) {
   X64OperandGenerator g(this);
   Emit(kSSEFloat64ToInt32, g.DefineAsRegister(node), g.Use(node->InputAt(0)));
 }
 
 
+void InstructionSelector::VisitConvertFloat64ToUint32(Node* node) {
+  X64OperandGenerator g(this);
+  // TODO(turbofan): X64 SSE cvttsd2siq should support operands.
+  Emit(kSSEFloat64ToUint32, g.DefineAsRegister(node),
+       g.UseDoubleRegister(node->InputAt(0)));
+}
+
+
 void InstructionSelector::VisitFloat64Add(Node* node) {
   X64OperandGenerator g(this);
   Emit(kSSEFloat64Add, g.DefineSameAsFirst(node),
index 9d947150e6c85c85be1b0fa36b071a8782c86783..27f0534450d729e6d752cf11ade81f92b622ce06 100644 (file)
@@ -2888,7 +2888,21 @@ TEST(RunConvertInt32ToFloat64_B) {
 }
 
 
-// TODO(titzer): Test ConvertUint32ToFloat64
+TEST(RunConvertUint32ToFloat64_B) {
+  RawMachineAssemblerTester<int32_t> m(kMachineWord32);
+  double output = 0;
+
+  Node* convert = m.ConvertUint32ToFloat64(m.Parameter(0));
+  m.Store(kMachineFloat64, m.PointerConstant(&output), m.Int32Constant(0),
+          convert);
+  m.Return(m.Parameter(0));
+
+  FOR_UINT32_INPUTS(i) {
+    uint32_t expect = *i;
+    CHECK_EQ(expect, m.Call(expect));
+    CHECK_EQ(static_cast<double>(expect), output);
+  }
+}
 
 
 TEST(RunConvertFloat64ToInt32_A) {
@@ -2921,49 +2935,73 @@ TEST(RunConvertFloat64ToInt32_B) {
   {
     FOR_INT32_INPUTS(i) {
       input = *i;
-      int expect = *i;
+      int32_t expect = *i;
       CHECK_EQ(expect, m.Call());
       CHECK_EQ(expect, output);
     }
   }
 
-  {
-    FOR_FLOAT64_INPUTS(i) {
-      input = *i;
-      // TODO(titzer): float64 -> int32 outside of the int32 range; the machine
-      // backends are all wrong in different ways, and they certainly don't
-      // implement the JavaScript conversions correctly.
-      if (std::isnan(input) || input > INT_MAX || input < INT_MIN) {
-        continue;
-      }
-      int32_t expect = static_cast<int32_t>(input);
+  // Check various powers of 2.
+  for (int32_t n = 1; n < 31; ++n) {
+    {
+      input = 1 << n;
+      int32_t expect = input;
+      CHECK_EQ(expect, m.Call());
+      CHECK_EQ(expect, output);
+    }
+
+    {
+      input = 3 << n;
+      int32_t expect = input;
       CHECK_EQ(expect, m.Call());
       CHECK_EQ(expect, output);
     }
   }
+  // Note we don't check fractional inputs, because these Convert operators
+  // really should be Change operators.
 }
 
 
-// TODO(titzer): test ConvertFloat64ToUint32
-
-
-TEST(RunConvertFloat64ToInt32_truncation) {
+TEST(RunConvertFloat64ToUint32_B) {
   RawMachineAssemblerTester<int32_t> m;
-  int32_t magic = 0x786234;
-  double input = 3.9;
-  int32_t result = 0;
+  double input = 0;
+  int32_t output = 0;
 
-  Node* input_node =
+  Node* load =
       m.Load(kMachineFloat64, m.PointerConstant(&input), m.Int32Constant(0));
-  m.Store(kMachineWord32, m.PointerConstant(&result), m.Int32Constant(0),
-          m.ConvertFloat64ToInt32(input_node));
-  m.Return(m.Int32Constant(magic));
+  Node* convert = m.ConvertFloat64ToUint32(load);
+  m.Store(kMachineWord32, m.PointerConstant(&output), m.Int32Constant(0),
+          convert);
+  m.Return(convert);
 
-  for (int i = -200; i < 200; i++) {
-    input = i + (i < 0 ? -0.9 : 0.9);
-    CHECK_EQ(magic, m.Call());
-    CHECK_EQ(i, result);
+  {
+    FOR_UINT32_INPUTS(i) {
+      input = *i;
+      // TODO(titzer): add a CheckEqualsHelper overload for uint32_t.
+      int32_t expect = static_cast<int32_t>(*i);
+      CHECK_EQ(expect, m.Call());
+      CHECK_EQ(expect, output);
+    }
+  }
+
+  // Check various powers of 2.
+  for (int32_t n = 1; n < 31; ++n) {
+    {
+      input = 1u << n;
+      int32_t expect = static_cast<int32_t>(static_cast<uint32_t>(input));
+      CHECK_EQ(expect, m.Call());
+      CHECK_EQ(expect, output);
+    }
+
+    {
+      input = 3u << n;
+      int32_t expect = static_cast<int32_t>(static_cast<uint32_t>(input));
+      CHECK_EQ(expect, m.Call());
+      CHECK_EQ(expect, output);
+    }
   }
+  // Note we don't check fractional inputs, because these Convert operators
+  // really should be Change operators.
 }