[turbofan] Add the TruncateFloat32ToInt32 operator to turbofan.

The new operator converts a float32 input to int32 through truncation.
I provide implementations of the new operator for x64, ia32, arm,
arm64, mips, mips64, and x87. @v8-ppc-ports, can you please take care
of the ppc implementation?

R=titzer@chromium.org, v8-arm-ports@googlegroups.com, v8-mips-ports@googlegroups.com, weiliang.lin@intel.com

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

Cr-Commit-Position: refs/heads/master@{#33346}
This commit is contained in:
ahaas 2016-01-16 03:40:47 -08:00 committed by Commit bot
parent dc6a593918
commit fc53eed14b
36 changed files with 145 additions and 15 deletions

View File

@ -2923,6 +2923,12 @@ void Assembler::vcvt_f64_u32(const DwVfpRegister dst,
}
void Assembler::vcvt_s32_f32(const SwVfpRegister dst, const SwVfpRegister src,
VFPConversionMode mode, const Condition cond) {
emit(EncodeVCVT(S32, dst.code(), F32, src.code(), mode, cond));
}
void Assembler::vcvt_s32_f64(const SwVfpRegister dst,
const DwVfpRegister src,
VFPConversionMode mode,

View File

@ -1125,6 +1125,10 @@ class Assembler : public AssemblerBase {
const SwVfpRegister src,
VFPConversionMode mode = kDefaultRoundToZero,
const Condition cond = al);
void vcvt_s32_f32(const SwVfpRegister dst,
const SwVfpRegister src,
VFPConversionMode mode = kDefaultRoundToZero,
const Condition cond = al);
void vcvt_s32_f64(const SwVfpRegister dst,
const DwVfpRegister src,
VFPConversionMode mode = kDefaultRoundToZero,

View File

@ -845,6 +845,13 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
DCHECK_EQ(LeaveCC, i.OutputSBit());
break;
}
case kArmVcvtS32F32: {
SwVfpRegister scratch = kScratchDoubleReg.low();
__ vcvt_s32_f32(scratch, i.InputFloat32Register(0));
__ vmov(i.OutputRegister(), scratch);
DCHECK_EQ(LeaveCC, i.OutputSBit());
break;
}
case kArmVcvtS32F64: {
SwVfpRegister scratch = kScratchDoubleReg.low();
__ vcvt_s32_f64(scratch, i.InputFloat64Register(0));

View File

@ -78,6 +78,7 @@ namespace compiler {
V(ArmVcvtF64F32) \
V(ArmVcvtF64S32) \
V(ArmVcvtF64U32) \
V(ArmVcvtS32F32) \
V(ArmVcvtS32F64) \
V(ArmVcvtU32F64) \
V(ArmVmovLowU32F64) \

View File

@ -80,6 +80,7 @@ int InstructionScheduler::GetTargetInstructionFlags(
case kArmVcvtF64F32:
case kArmVcvtF64S32:
case kArmVcvtF64U32:
case kArmVcvtS32F32:
case kArmVcvtS32F64:
case kArmVcvtU32F64:
case kArmVmovLowU32F64:

View File

@ -931,6 +931,11 @@ void InstructionSelector::VisitChangeUint32ToFloat64(Node* node) {
}
void InstructionSelector::VisitTruncateFloat32ToInt32(Node* node) {
VisitRR(this, kArmVcvtS32F32, node);
}
void InstructionSelector::VisitChangeFloat64ToInt32(Node* node) {
VisitRR(this, kArmVcvtS32F64, node);
}

View File

@ -1042,6 +1042,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
case kArm64Float64ToFloat32:
__ Fcvt(i.OutputDoubleRegister().S(), i.InputDoubleRegister(0));
break;
case kArm64Float32ToInt32:
__ Fcvtzs(i.OutputRegister32(), i.InputFloat32Register(0));
break;
case kArm64Float64ToInt32:
__ Fcvtzs(i.OutputRegister32(), i.InputDoubleRegister(0));
break;

View File

@ -110,6 +110,7 @@ namespace compiler {
V(Arm64Float64RoundTiesEven) \
V(Arm64Float32ToFloat64) \
V(Arm64Float64ToFloat32) \
V(Arm64Float32ToInt32) \
V(Arm64Float64ToInt32) \
V(Arm64Float64ToUint32) \
V(Arm64Float32ToInt64) \

View File

@ -106,6 +106,7 @@ int InstructionScheduler::GetTargetInstructionFlags(
case kArm64Float32RoundUp:
case kArm64Float32ToFloat64:
case kArm64Float64ToFloat32:
case kArm64Float32ToInt32:
case kArm64Float64ToInt32:
case kArm64Float64ToUint32:
case kArm64Float32ToInt64:

View File

@ -1229,6 +1229,11 @@ void InstructionSelector::VisitChangeUint32ToFloat64(Node* node) {
}
void InstructionSelector::VisitTruncateFloat32ToInt32(Node* node) {
VisitRR(this, kArm64Float32ToInt32, node);
}
void InstructionSelector::VisitChangeFloat64ToInt32(Node* node) {
VisitRR(this, kArm64Float64ToInt32, node);
}

View File

@ -739,6 +739,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
case kSSEFloat64ToFloat32:
__ cvtsd2ss(i.OutputDoubleRegister(), i.InputOperand(0));
break;
case kSSEFloat32ToInt32:
__ cvttss2si(i.OutputRegister(), i.InputOperand(0));
break;
case kSSEFloat64ToInt32:
__ cvttsd2si(i.OutputRegister(), i.InputOperand(0));
break;

View File

@ -58,6 +58,7 @@ namespace compiler {
V(SSEFloat64Round) \
V(SSEFloat32ToFloat64) \
V(SSEFloat64ToFloat32) \
V(SSEFloat32ToInt32) \
V(SSEFloat64ToInt32) \
V(SSEFloat64ToUint32) \
V(SSEInt32ToFloat64) \

View File

@ -61,6 +61,7 @@ int InstructionScheduler::GetTargetInstructionFlags(
case kSSEFloat64Round:
case kSSEFloat32ToFloat64:
case kSSEFloat64ToFloat32:
case kSSEFloat32ToInt32:
case kSSEFloat64ToInt32:
case kSSEFloat64ToUint32:
case kSSEInt32ToFloat64:

View File

@ -705,6 +705,11 @@ void InstructionSelector::VisitChangeUint32ToFloat64(Node* node) {
}
void InstructionSelector::VisitTruncateFloat32ToInt32(Node* node) {
VisitRO(this, node, kSSEFloat32ToInt32);
}
void InstructionSelector::VisitChangeFloat64ToInt32(Node* node) {
VisitRO(this, node, kSSEFloat64ToInt32);
}

View File

@ -956,6 +956,8 @@ void InstructionSelector::VisitNode(Node* node) {
return MarkAsWord32(node), VisitChangeFloat64ToInt32(node);
case IrOpcode::kChangeFloat64ToUint32:
return MarkAsWord32(node), VisitChangeFloat64ToUint32(node);
case IrOpcode::kTruncateFloat32ToInt32:
return MarkAsWord32(node), VisitTruncateFloat32ToInt32(node);
case IrOpcode::kTryTruncateFloat32ToInt64:
return MarkAsWord64(node), VisitTryTruncateFloat32ToInt64(node);
case IrOpcode::kTryTruncateFloat64ToInt64:

View File

@ -144,6 +144,7 @@ CheckedStoreRepresentation CheckedStoreRepresentationOf(Operator const* op) {
V(ChangeFloat32ToFloat64, Operator::kNoProperties, 1, 0, 1) \
V(ChangeFloat64ToInt32, Operator::kNoProperties, 1, 0, 1) \
V(ChangeFloat64ToUint32, Operator::kNoProperties, 1, 0, 1) \
V(TruncateFloat32ToInt32, Operator::kNoProperties, 1, 0, 1) \
V(TryTruncateFloat32ToInt64, Operator::kNoProperties, 1, 0, 2) \
V(TryTruncateFloat64ToInt64, Operator::kNoProperties, 1, 0, 2) \
V(TryTruncateFloat32ToUint64, Operator::kNoProperties, 1, 0, 2) \

View File

@ -213,6 +213,7 @@ class MachineOperatorBuilder final : public ZoneObject {
const Operator* ChangeFloat32ToFloat64();
const Operator* ChangeFloat64ToInt32(); // narrowing
const Operator* ChangeFloat64ToUint32(); // narrowing
const Operator* TruncateFloat32ToInt32();
const Operator* TryTruncateFloat32ToInt64();
const Operator* TryTruncateFloat64ToInt64();
const Operator* TryTruncateFloat32ToUint64();

View File

@ -513,6 +513,11 @@ void InstructionSelector::VisitChangeUint32ToFloat64(Node* node) {
}
void InstructionSelector::VisitTruncateFloat32ToInt32(Node* node) {
VisitRR(this, kMipsTruncWS, node);
}
void InstructionSelector::VisitChangeFloat64ToInt32(Node* node) {
MipsOperandGenerator g(this);
Node* value = node->InputAt(0);

View File

@ -812,6 +812,11 @@ void InstructionSelector::VisitChangeUint32ToFloat64(Node* node) {
}
void InstructionSelector::VisitTruncateFloat32ToInt32(Node* node) {
VisitRR(this, kMips64TruncWS, node);
}
void InstructionSelector::VisitChangeFloat64ToInt32(Node* node) {
Mips64OperandGenerator g(this);
Node* value = node->InputAt(0);

View File

@ -270,6 +270,7 @@
V(ChangeFloat32ToFloat64) \
V(ChangeFloat64ToInt32) \
V(ChangeFloat64ToUint32) \
V(TruncateFloat32ToInt32) \
V(TryTruncateFloat32ToInt64) \
V(TryTruncateFloat64ToInt64) \
V(TryTruncateFloat32ToUint64) \

View File

@ -432,10 +432,8 @@ class RawMachineAssembler {
Node* ChangeFloat64ToUint32(Node* a) {
return AddNode(machine()->ChangeFloat64ToUint32(), a);
}
Node* TruncateFloat32ToInt64(Node* a) {
// TODO(ahaas): Remove this function as soon as it is not used anymore in
// WebAssembly.
return AddNode(machine()->TryTruncateFloat32ToInt64(), a);
Node* TruncateFloat32ToInt32(Node* a) {
return AddNode(machine()->TruncateFloat32ToInt32(), a);
}
Node* TryTruncateFloat32ToInt64(Node* a) {
return AddNode(machine()->TryTruncateFloat32ToInt64(), a);
@ -448,11 +446,6 @@ class RawMachineAssembler {
Node* TryTruncateFloat64ToInt64(Node* a) {
return AddNode(machine()->TryTruncateFloat64ToInt64(), a);
}
Node* TruncateFloat32ToUint64(Node* a) {
// TODO(ahaas): Remove this function as soon as it is not used anymore in
// WebAssembly.
return AddNode(machine()->TryTruncateFloat32ToUint64(), a);
}
Node* TryTruncateFloat32ToUint64(Node* a) {
return AddNode(machine()->TryTruncateFloat32ToUint64(), a);
}

View File

@ -2145,6 +2145,11 @@ Type* Typer::Visitor::TypeChangeFloat64ToUint32(Node* node) {
}
Type* Typer::Visitor::TypeTruncateFloat32ToInt32(Node* node) {
return Type::Intersect(Type::Signed32(), Type::UntaggedIntegral32(), zone());
}
Type* Typer::Visitor::TypeTryTruncateFloat32ToInt64(Node* node) {
return Type::Internal();
}

View File

@ -924,6 +924,7 @@ void Verifier::Visitor::Check(Node* node) {
case IrOpcode::kChangeFloat32ToFloat64:
case IrOpcode::kChangeFloat64ToInt32:
case IrOpcode::kChangeFloat64ToUint32:
case IrOpcode::kTruncateFloat32ToInt32:
case IrOpcode::kTryTruncateFloat32ToInt64:
case IrOpcode::kTryTruncateFloat64ToInt64:
case IrOpcode::kTryTruncateFloat32ToUint64:

View File

@ -1121,14 +1121,12 @@ Node* WasmGraphBuilder::BuildI32SConvertF32(Node* input) {
MachineOperatorBuilder* m = jsgraph()->machine();
// Truncation of the input value is needed for the overflow check later.
Node* trunc = Unop(wasm::kExprF32Trunc, input);
// TODO(titzer): two conversions
Node* f64_trunc = graph()->NewNode(m->ChangeFloat32ToFloat64(), trunc);
Node* result = graph()->NewNode(m->ChangeFloat64ToInt32(), f64_trunc);
Node* result = graph()->NewNode(m->TruncateFloat32ToInt32(), trunc);
// Convert the result back to f64. If we end up at a different value than the
// truncated input value, then there has been an overflow and we trap.
Node* check = Unop(wasm::kExprF64SConvertI32, result);
Node* overflow = Binop(wasm::kExprF64Ne, f64_trunc, check);
Node* check = Unop(wasm::kExprF32SConvertI32, result);
Node* overflow = Binop(wasm::kExprF32Ne, trunc, check);
trap_->AddTrapIfTrue(kTrapFloatUnrepresentable, overflow);
return result;

View File

@ -947,6 +947,13 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ Roundss(i.OutputDoubleRegister(), i.InputDoubleRegister(0), mode);
break;
}
case kSSEFloat32ToInt32:
if (instr->InputAt(0)->IsDoubleRegister()) {
__ Cvttss2si(i.OutputRegister(), i.InputDoubleRegister(0));
} else {
__ Cvttss2si(i.OutputRegister(), i.InputOperand(0));
}
break;
case kSSEFloat64Cmp:
ASSEMBLE_SSE_BINOP(Ucomisd);
break;

View File

@ -63,6 +63,7 @@ namespace compiler {
V(SSEFloat32Max) \
V(SSEFloat32Min) \
V(SSEFloat32ToFloat64) \
V(SSEFloat32ToInt32) \
V(SSEFloat32Round) \
V(SSEFloat64Cmp) \
V(SSEFloat64Add) \

View File

@ -79,6 +79,7 @@ int InstructionScheduler::GetTargetInstructionFlags(
case kSSEFloat64Max:
case kSSEFloat64Min:
case kSSEFloat64ToFloat32:
case kSSEFloat32ToInt32:
case kSSEFloat64ToInt32:
case kSSEFloat64ToUint32:
case kSSEFloat64ToInt64:

View File

@ -857,6 +857,12 @@ void InstructionSelector::VisitChangeFloat64ToUint32(Node* node) {
}
void InstructionSelector::VisitTruncateFloat32ToInt32(Node* node) {
X64OperandGenerator g(this);
Emit(kSSEFloat32ToInt32, g.DefineAsRegister(node), g.Use(node->InputAt(0)));
}
void InstructionSelector::VisitTryTruncateFloat32ToInt64(Node* node) {
X64OperandGenerator g(this);
InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0))};

View File

@ -1005,6 +1005,16 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ LoadUint32NoSSE2(i.InputRegister(0));
break;
}
case kX87Float32ToInt32: {
if (!instr->InputAt(0)->IsDoubleRegister()) {
__ fld_s(i.InputOperand(0));
}
__ TruncateX87TOSToI(i.OutputRegister(0));
if (!instr->InputAt(0)->IsDoubleRegister()) {
__ fstp(0);
}
break;
}
case kX87Float64ToInt32: {
if (!instr->InputAt(0)->IsDoubleRegister()) {
__ fld_d(i.InputOperand(0));

View File

@ -57,6 +57,7 @@ namespace compiler {
V(X87Float32ToFloat64) \
V(X87Uint32ToFloat64) \
V(X87Float64ToInt32) \
V(X87Float32ToInt32) \
V(X87Float64ToFloat32) \
V(X87Float64ToUint32) \
V(X87Float64ExtractHighWord32) \

View File

@ -669,6 +669,12 @@ void InstructionSelector::VisitChangeUint32ToFloat64(Node* node) {
}
void InstructionSelector::VisitTruncateFloat32ToInt32(Node* node) {
X87OperandGenerator g(this);
Emit(kX87Float32ToInt32, g.DefineAsRegister(node), g.Use(node->InputAt(0)));
}
void InstructionSelector::VisitChangeFloat64ToInt32(Node* node) {
X87OperandGenerator g(this);
Emit(kX87Float64ToInt32, g.DefineAsRegister(node), g.Use(node->InputAt(0)));

View File

@ -1384,6 +1384,14 @@ class Assembler : public AssemblerBase {
void vcvtqsi2sd(XMMRegister dst, XMMRegister src1, const Operand& src2) {
vsd(0x2a, dst, src1, src2, kF2, k0F, kW1);
}
void vcvttss2si(Register dst, XMMRegister src) {
XMMRegister idst = {dst.code()};
vsd(0x2c, idst, xmm0, src, kF3, k0F, kW0);
}
void vcvttss2si(Register dst, const Operand& src) {
XMMRegister idst = {dst.code()};
vsd(0x2c, idst, xmm0, src, kF3, k0F, kW0);
}
void vcvttsd2si(Register dst, XMMRegister src) {
XMMRegister idst = {dst.code()};
vsd(0x2c, idst, xmm0, src, kF2, k0F, kW0);

View File

@ -918,6 +918,26 @@ void MacroAssembler::Cvtsd2si(Register dst, XMMRegister src) {
}
void MacroAssembler::Cvttss2si(Register dst, XMMRegister src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvttss2si(dst, src);
} else {
cvttss2si(dst, src);
}
}
void MacroAssembler::Cvttss2si(Register dst, const Operand& src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvttss2si(dst, src);
} else {
cvttss2si(dst, src);
}
}
void MacroAssembler::Cvttsd2si(Register dst, XMMRegister src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);

View File

@ -823,6 +823,8 @@ class MacroAssembler: public Assembler {
void Cvtsd2si(Register dst, XMMRegister src);
void Cvttss2si(Register dst, XMMRegister src);
void Cvttss2si(Register dst, const Operand& src);
void Cvttsd2si(Register dst, XMMRegister src);
void Cvttsd2si(Register dst, const Operand& src);
void Cvttss2siq(Register dst, XMMRegister src);

View File

@ -92,7 +92,7 @@ class BufferedRawMachineAssemblerTester
// parameters from memory. Thereby it is possible to pass 64 bit parameters
// to the IR graph.
Node* Parameter(size_t index) {
CHECK(index >= 0 && index < 4);
CHECK(index < 4);
return parameter_nodes_[index];
}

View File

@ -4124,6 +4124,18 @@ TEST(RunChangeUint32ToFloat64) {
}
TEST(RunTruncateFloat32ToInt32) {
BufferedRawMachineAssemblerTester<int32_t> m(MachineType::Float32());
m.Return(m.TruncateFloat32ToInt32(m.Parameter(0)));
FOR_FLOAT32_INPUTS(i) {
if (*i <= static_cast<float>(std::numeric_limits<int32_t>::max()) &&
*i >= static_cast<float>(std::numeric_limits<int32_t>::min())) {
CheckFloatEq(static_cast<int32_t>(*i), m.Call(*i));
}
}
}
TEST(RunChangeFloat64ToInt32_A) {
BufferedRawMachineAssemblerTester<int32_t> m;
double magic = 11.1;