Commit 9ba2f35c authored by fpizlo@apple.com's avatar fpizlo@apple.com

FTL should use cvttsd2si directly for double-to-int32 conversions

https://bugs.webkit.org/show_bug.cgi?id=125275

Source/JavaScriptCore: 

Reviewed by Michael Saboff.
        
Wow. This was an ordeal. Using cvttsd2si was actually easy, but I learned, and
sometimes even fixed, some interesting things:
        
- The llvm.x86.sse2.cvttsd2si intrinsic can actually result in LLVM emitting a
  vcvttsd2si. I guess the intrinsic doesn't actually imply the instruction.
        
- That whole thing about branchTruncateDoubleToUint32? Yeah we don't need that. It's
  better to use branchTruncateDoubleToInt32 instead. It has the right semantics for
  all of its callers (err, its one-and-only caller), and it's more likely to take
  fast path. This patch kills branchTruncateDoubleToUint32.
        
- "a[i] = v; v = a[i]". Does this change v? OK, assume that 'a[i]' is a pure-ish
  operation - like an array access with 'i' being an integer index and we're not
  having a bad time. Now does this change v? CSE assumes that it doesn't. That's
  wrong. If 'a' is a typed array - the most sensible and pure kind of array - then
  this can be a truncating cast. For example 'v' could be a double and 'a' could be
  an integer array.
        
- "v1 = a[i]; v2 = a[i]". Is v1 === v2 assuming that 'a[i]' is pure-ish? The answer
  is no. You could have a different arrayMode in each access. I know this sounds
  weird, but with concurrent JIT that might happen.
        
This patch adds tests for all of this stuff, except for the first issue (it's weird
but probably doesn't matter) and the last issue (it's too much of a freakshow).

* assembler/MacroAssemblerARM64.h:
* assembler/MacroAssemblerARMv7.h:
* assembler/MacroAssemblerX86Common.h:
* dfg/DFGCSEPhase.cpp:
(JSC::DFG::CSEPhase::getByValLoadElimination):
(JSC::DFG::CSEPhase::performNodeCSE):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compilePutByValForIntTypedArray):
* ftl/FTLAbbreviations.h:
(JSC::FTL::vectorType):
(JSC::FTL::getUndef):
(JSC::FTL::buildInsertElement):
* ftl/FTLIntrinsicRepository.h:
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::doubleToInt32):
(JSC::FTL::LowerDFGToLLVM::doubleToUInt32):
(JSC::FTL::LowerDFGToLLVM::sensibleDoubleToInt32):
* ftl/FTLOutput.h:
(JSC::FTL::Output::insertElement):
(JSC::FTL::Output::hasSensibleDoubleToInt):
(JSC::FTL::Output::sensibleDoubleToInt):

LayoutTests: 

Reviewed by Michael Saboff.

* js/regress/double-to-int32-typed-array-expected.txt: Added.
* js/regress/double-to-int32-typed-array-no-inline-expected.txt: Added.
* js/regress/double-to-int32-typed-array-no-inline.html: Added.
* js/regress/double-to-int32-typed-array.html: Added.
* js/regress/double-to-uint32-typed-array-expected.txt: Added.
* js/regress/double-to-uint32-typed-array-no-inline-expected.txt: Added.
* js/regress/double-to-uint32-typed-array-no-inline.html: Added.
* js/regress/double-to-uint32-typed-array.html: Added.
* js/regress/script-tests/double-to-int32-typed-array-no-inline.js: Added.
(foo):
(test):
* js/regress/script-tests/double-to-int32-typed-array.js: Added.
(foo):
(test):
* js/regress/script-tests/double-to-uint32-typed-array-no-inline.js: Added.
(foo):
(test):
* js/regress/script-tests/double-to-uint32-typed-array.js: Added.
(foo):
(test):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@160205 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 82a8937b
2013-12-04 Filip Pizlo <fpizlo@apple.com>
FTL should use cvttsd2si directly for double-to-int32 conversions
https://bugs.webkit.org/show_bug.cgi?id=125275
Reviewed by Michael Saboff.
* js/regress/double-to-int32-typed-array-expected.txt: Added.
* js/regress/double-to-int32-typed-array-no-inline-expected.txt: Added.
* js/regress/double-to-int32-typed-array-no-inline.html: Added.
* js/regress/double-to-int32-typed-array.html: Added.
* js/regress/double-to-uint32-typed-array-expected.txt: Added.
* js/regress/double-to-uint32-typed-array-no-inline-expected.txt: Added.
* js/regress/double-to-uint32-typed-array-no-inline.html: Added.
* js/regress/double-to-uint32-typed-array.html: Added.
* js/regress/script-tests/double-to-int32-typed-array-no-inline.js: Added.
(foo):
(test):
* js/regress/script-tests/double-to-int32-typed-array.js: Added.
(foo):
(test):
* js/regress/script-tests/double-to-uint32-typed-array-no-inline.js: Added.
(foo):
(test):
* js/regress/script-tests/double-to-uint32-typed-array.js: Added.
(foo):
(test):
2013-12-05 Bear Travis <betravis@adobe.com>
[CSS Shapes] Enable CSS Shapes on Windows
JSRegress/double-to-int32-typed-array
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS no exception thrown
PASS successfullyParsed is true
TEST COMPLETE
JSRegress/double-to-int32-typed-array-no-inline
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS no exception thrown
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<script src="../../resources/js-test-pre.js"></script>
</head>
<body>
<script src="resources/regress-pre.js"></script>
<script src="script-tests/double-to-int32-typed-array-no-inline.js"></script>
<script src="resources/regress-post.js"></script>
<script src="../../resources/js-test-post.js"></script>
</body>
</html>
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<script src="../../resources/js-test-pre.js"></script>
</head>
<body>
<script src="resources/regress-pre.js"></script>
<script src="script-tests/double-to-int32-typed-array.js"></script>
<script src="resources/regress-post.js"></script>
<script src="../../resources/js-test-post.js"></script>
</body>
</html>
JSRegress/double-to-uint32-typed-array
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS no exception thrown
PASS successfullyParsed is true
TEST COMPLETE
JSRegress/double-to-uint32-typed-array-no-inline
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS no exception thrown
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<script src="../../resources/js-test-pre.js"></script>
</head>
<body>
<script src="resources/regress-pre.js"></script>
<script src="script-tests/double-to-uint32-typed-array-no-inline.js"></script>
<script src="resources/regress-post.js"></script>
<script src="../../resources/js-test-post.js"></script>
</body>
</html>
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<script src="../../resources/js-test-pre.js"></script>
</head>
<body>
<script src="resources/regress-pre.js"></script>
<script src="script-tests/double-to-uint32-typed-array.js"></script>
<script src="resources/regress-post.js"></script>
<script src="../../resources/js-test-post.js"></script>
</body>
</html>
var array = new Int32Array(1);
function foo(value) {
array[0] = value;
return array[0];
}
noInline(foo);
function test(input, output) {
var result = foo(input);
if (result != output)
throw "Error: " + input + " was supposed to result in " + output + " but instead got " + result;
}
for (var i = 0; i < 100000; ++i)
test(i + 0.5, i);
test(0, 0);
test(100.5, 100);
test(-100.5, -100);
test(3000000000, -1294967296);
test(-3000000000, 1294967296);
test(-2147483648, -2147483648);
var array = new Int32Array(1);
function foo(value) {
array[0] = value;
return array[0];
}
function test(input, output) {
var result = foo(input);
if (result != output)
throw "Error: " + input + " was supposed to result in " + output + " but instead got " + result;
}
for (var i = 0; i < 100000; ++i)
test(i + 0.5, i);
test(0, 0);
test(100.5, 100);
test(-100.5, -100);
test(3000000000, -1294967296);
test(-3000000000, 1294967296);
test(-2147483648, -2147483648);
var array = new Uint32Array(1);
function foo(value) {
array[0] = value;
return array[0];
}
noInline(foo);
function test(input, output) {
var result = foo(input);
if (result != output)
throw "Error: " + input + " was supposed to result in " + output + " but instead got " + result;
}
for (var i = 0; i < 100000; ++i)
test(i + 0.5, i);
test(0, 0);
test(100.5, 100);
test(-100.5, 4294967196);
test(3000000000, 3000000000);
test(6000000000, 1705032704);
test(-3000000000, 1294967296);
test(-2147483648, 2147483648);
var array = new Uint32Array(1);
function foo(value) {
array[0] = value;
return array[0];
}
function test(input, output) {
var result = foo(input);
if (result != output)
throw "Error: " + input + " was supposed to result in " + output + " but instead got " + result;
}
for (var i = 0; i < 100000; ++i)
test(i + 0.5, i);
test(0, 0);
test(100.5, 100);
test(-100.5, 4294967196);
test(3000000000, 3000000000);
test(6000000000, 1705032704);
test(-3000000000, 1294967296);
test(-2147483648, 2147483648);
2013-12-04 Filip Pizlo <fpizlo@apple.com>
FTL should use cvttsd2si directly for double-to-int32 conversions
https://bugs.webkit.org/show_bug.cgi?id=125275
Reviewed by Michael Saboff.
Wow. This was an ordeal. Using cvttsd2si was actually easy, but I learned, and
sometimes even fixed, some interesting things:
- The llvm.x86.sse2.cvttsd2si intrinsic can actually result in LLVM emitting a
vcvttsd2si. I guess the intrinsic doesn't actually imply the instruction.
- That whole thing about branchTruncateDoubleToUint32? Yeah we don't need that. It's
better to use branchTruncateDoubleToInt32 instead. It has the right semantics for
all of its callers (err, its one-and-only caller), and it's more likely to take
fast path. This patch kills branchTruncateDoubleToUint32.
- "a[i] = v; v = a[i]". Does this change v? OK, assume that 'a[i]' is a pure-ish
operation - like an array access with 'i' being an integer index and we're not
having a bad time. Now does this change v? CSE assumes that it doesn't. That's
wrong. If 'a' is a typed array - the most sensible and pure kind of array - then
this can be a truncating cast. For example 'v' could be a double and 'a' could be
an integer array.
- "v1 = a[i]; v2 = a[i]". Is v1 === v2 assuming that 'a[i]' is pure-ish? The answer
is no. You could have a different arrayMode in each access. I know this sounds
weird, but with concurrent JIT that might happen.
This patch adds tests for all of this stuff, except for the first issue (it's weird
but probably doesn't matter) and the last issue (it's too much of a freakshow).
* assembler/MacroAssemblerARM64.h:
* assembler/MacroAssemblerARMv7.h:
* assembler/MacroAssemblerX86Common.h:
* dfg/DFGCSEPhase.cpp:
(JSC::DFG::CSEPhase::getByValLoadElimination):
(JSC::DFG::CSEPhase::performNodeCSE):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compilePutByValForIntTypedArray):
* ftl/FTLAbbreviations.h:
(JSC::FTL::vectorType):
(JSC::FTL::getUndef):
(JSC::FTL::buildInsertElement):
* ftl/FTLIntrinsicRepository.h:
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::doubleToInt32):
(JSC::FTL::LowerDFGToLLVM::doubleToUInt32):
(JSC::FTL::LowerDFGToLLVM::sensibleDoubleToInt32):
* ftl/FTLOutput.h:
(JSC::FTL::Output::insertElement):
(JSC::FTL::Output::hasSensibleDoubleToInt):
(JSC::FTL::Output::sensibleDoubleToInt):
2013-12-05 Commit Queue <commit-queue@webkit.org>
Unreviewed, rolling out r160133.
......
......@@ -1196,15 +1196,6 @@ public:
return Jump(makeBranch(branchType == BranchIfTruncateSuccessful ? Equal : NotEqual));
}
Jump branchTruncateDoubleToUint32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed)
{
// Truncate to a 64-bit integer in dataTempRegister, copy the low 32-bit to dest.
m_assembler.fcvtzs<64, 64>(dest, src);
// Check thlow 32-bits zero extend to be equal to the full value.
m_assembler.cmp<64>(dest, dest, ARM64Assembler::UXTW, 0);
return Jump(makeBranch(branchType == BranchIfTruncateSuccessful ? Equal : NotEqual));
}
void convertDoubleToFloat(FPRegisterID src, FPRegisterID dest)
{
m_assembler.fcvt<32, 64>(dest, src);
......
......@@ -1074,23 +1074,6 @@ public:
return failure;
}
Jump branchTruncateDoubleToUint32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed)
{
m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src);
m_assembler.vmov(dest, fpTempRegisterAsSingle());
Jump overflow = branch32(Equal, dest, TrustedImm32(0x7fffffff));
Jump success = branch32(GreaterThanOrEqual, dest, TrustedImm32(0));
overflow.link(this);
if (branchType == BranchIfTruncateSuccessful)
return success;
Jump failure = jump();
success.link(this);
return failure;
}
// Result is undefined if the value is outside of the integer range.
void truncateDoubleToInt32(FPRegisterID src, RegisterID dest)
{
......
......@@ -885,13 +885,6 @@ public:
return branch32(branchType ? NotEqual : Equal, dest, TrustedImm32(0x80000000));
}
Jump branchTruncateDoubleToUint32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed)
{
ASSERT(isSSE2Present());
m_assembler.cvttsd2si_rr(src, dest);
return branch32(branchType ? GreaterThanOrEqual : LessThan, dest, TrustedImm32(0));
}
void truncateDoubleToInt32(FPRegisterID src, RegisterID dest)
{
ASSERT(isSSE2Present());
......
......@@ -331,7 +331,7 @@ private:
return 0;
}
Node* getByValLoadElimination(Node* child1, Node* child2)
Node* getByValLoadElimination(Node* child1, Node* child2, ArrayMode arrayMode)
{
for (unsigned i = m_indexInBlock; i--;) {
Node* node = m_currentBlock->at(i);
......@@ -342,7 +342,9 @@ private:
case GetByVal:
if (!m_graph.byValIsPure(node))
return 0;
if (node->child1() == child1 && node->child2() == child2)
if (node->child1() == child1
&& node->child2() == child2
&& node->arrayMode().type() == arrayMode.type())
return node;
break;
......@@ -351,7 +353,12 @@ private:
case PutByValAlias: {
if (!m_graph.byValIsPure(node))
return 0;
if (m_graph.varArgChild(node, 0) == child1 && m_graph.varArgChild(node, 1) == child2)
// Typed arrays
if (arrayMode.typedArrayType() != NotTypedArray)
return 0;
if (m_graph.varArgChild(node, 0) == child1
&& m_graph.varArgChild(node, 1) == child2
&& node->arrayMode().type() == arrayMode.type())
return m_graph.varArgChild(node, 2).node();
// We must assume that the PutByVal will clobber the location we're getting from.
// FIXME: We can do better; if we know that the PutByVal is accessing an array of a
......@@ -1216,7 +1223,7 @@ private:
if (cseMode == StoreElimination)
break;
if (m_graph.byValIsPure(node))
setReplacement(getByValLoadElimination(node->child1().node(), node->child2().node()));
setReplacement(getByValLoadElimination(node->child1().node(), node->child2().node(), node->arrayMode()));
break;
case PutByValDirect:
......@@ -1226,7 +1233,7 @@ private:
Edge child1 = m_graph.varArgChild(node, 0);
Edge child2 = m_graph.varArgChild(node, 1);
if (node->arrayMode().canCSEStorage()) {
Node* replacement = getByValLoadElimination(child1.node(), child2.node());
Node* replacement = getByValLoadElimination(child1.node(), child2.node(), node->arrayMode());
if (!replacement)
break;
node->setOp(PutByValAlias);
......
......@@ -2506,11 +2506,8 @@ void SpeculativeJIT::compilePutByValForIntTypedArray(GPRReg base, GPRReg propert
MacroAssembler::Jump fixed = m_jit.jump();
notNaN.link(&m_jit);
MacroAssembler::Jump failed;
if (isSigned(type))
failed = m_jit.branchTruncateDoubleToInt32(fpr, gpr, MacroAssembler::BranchIfTruncateFailed);
else
failed = m_jit.branchTruncateDoubleToUint32(fpr, gpr, MacroAssembler::BranchIfTruncateFailed);
MacroAssembler::Jump failed = m_jit.branchTruncateDoubleToInt32(
fpr, gpr, MacroAssembler::BranchIfTruncateFailed);
addSlowPathGenerator(slowPathCall(failed, this, toInt32, gpr, fpr));
......
......@@ -58,6 +58,7 @@ static inline LType floatType(LContext context) { return llvm->FloatTypeInContex
static inline LType doubleType(LContext context) { return llvm->DoubleTypeInContext(context); }
static inline LType pointerType(LType type) { return llvm->PointerType(type, 0); }
static inline LType vectorType(LType type, unsigned count) { return llvm->VectorType(type, count); }
enum PackingMode { NotPacked, Packed };
static inline LType structType(LContext context, LType* elementTypes, unsigned elementCount, PackingMode packing = NotPacked)
......@@ -140,6 +141,7 @@ static inline LValue addExternFunction(LModule module, const char* name, LType t
}
static inline LValue getParam(LValue function, unsigned index) { return llvm->GetParam(function, index); }
static inline LValue getUndef(LType type) { return llvm->GetUndef(type); }
enum BitExtension { ZeroExtend, SignExtend };
static inline LValue constInt(LType type, unsigned long long value, BitExtension extension = ZeroExtend) { return llvm->ConstInt(type, value, extension == SignExtend); }
......@@ -217,6 +219,7 @@ static inline LValue buildPtrToInt(LBuilder builder, LValue value, LType type) {
static inline LValue buildBitCast(LBuilder builder, LValue value, LType type) { return llvm->BuildBitCast(builder, value, type, ""); }
static inline LValue buildICmp(LBuilder builder, LIntPredicate cond, LValue left, LValue right) { return llvm->BuildICmp(builder, cond, left, right, ""); }
static inline LValue buildFCmp(LBuilder builder, LRealPredicate cond, LValue left, LValue right) { return llvm->BuildFCmp(builder, cond, left, right, ""); }
static inline LValue buildInsertElement(LBuilder builder, LValue vector, LValue element, LValue index) { return llvm->BuildInsertElement(builder, vector, element, index, ""); }
enum SynchronizationScope { SingleThread, CrossThread };
static inline LValue buildFence(LBuilder builder, LAtomicOrdering ordering, SynchronizationScope scope = CrossThread)
......
......@@ -42,12 +42,13 @@ namespace JSC { namespace FTL {
macro(doubleAbs, "llvm.fabs.f64", functionType(doubleType, doubleType)) \
macro(mulWithOverflow32, "llvm.smul.with.overflow.i32", functionType(structType(m_context, int32, boolean), int32, int32)) \
macro(mulWithOverflow64, "llvm.smul.with.overflow.i64", functionType(structType(m_context, int64, boolean), int64, int64)) \
macro(subWithOverflow32, "llvm.ssub.with.overflow.i32", functionType(structType(m_context, int32, boolean), int32, int32)) \
macro(subWithOverflow64, "llvm.ssub.with.overflow.i64", functionType(structType(m_context, int64, boolean), int64, int64)) \
macro(patchpointInt64, "llvm.experimental.patchpoint.i64", functionType(int64, int32, int32, ref8, int32, Variadic)) \
macro(patchpointVoid, "llvm.experimental.patchpoint.void", functionType(voidType, int32, int32, ref8, int32, Variadic)) \
macro(stackmap, "llvm.experimental.stackmap", functionType(voidType, int32, int32, Variadic)) \
macro(trap, "llvm.trap", functionType(voidType))
macro(subWithOverflow32, "llvm.ssub.with.overflow.i32", functionType(structType(m_context, int32, boolean), int32, int32)) \
macro(subWithOverflow64, "llvm.ssub.with.overflow.i64", functionType(structType(m_context, int64, boolean), int64, int64)) \
macro(trap, "llvm.trap", functionType(voidType)) \
macro(x86SSE2CvtTSD2SI, "llvm.x86.sse2.cvttsd2si", functionType(int32, vectorType(doubleType, 2)))
#define FOR_EACH_FUNCTION_TYPE(macro) \
macro(C_JITOperation_ESt, functionType(intPtr, intPtr, intPtr)) \
......
......@@ -3011,15 +3011,41 @@ private:
LValue doubleToInt32(LValue doubleValue)
{
if (Output::hasSensibleDoubleToInt())
return sensibleDoubleToInt32(doubleValue);
double limit = pow(2, 31) - 1;
return doubleToInt32(doubleValue, -limit, limit);
}
LValue doubleToUInt32(LValue doubleValue)
{
if (Output::hasSensibleDoubleToInt())
return sensibleDoubleToInt32(doubleValue);
return doubleToInt32(doubleValue, 0, pow(2, 32) - 1, false);
}
LValue sensibleDoubleToInt32(LValue doubleValue)
{
LBasicBlock slowPath = FTL_NEW_BLOCK(m_out, ("sensible doubleToInt32 slow path"));
LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("sensible doubleToInt32 continuation"));
ValueFromBlock fastResult = m_out.anchor(
m_out.sensibleDoubleToInt(doubleValue));
m_out.branch(
m_out.equal(fastResult.value(), m_out.constInt32(0x80000000)),
slowPath, continuation);
LBasicBlock lastNext = m_out.appendTo(slowPath, continuation);
ValueFromBlock slowResult = m_out.anchor(
m_out.call(m_out.operation(toInt32), doubleValue));
m_out.jump(continuation);
m_out.appendTo(continuation, lastNext);
return m_out.phi(m_out.int32, fastResult, slowResult);
}
void speculateBackward(
ExitKind kind, FormattedValue lowValue, Node* highValue, LValue failCondition)
{
......
......@@ -154,6 +154,8 @@ public:
LValue lShr(LValue left, LValue right) { return buildLShr(m_builder, left, right); }
LValue bitNot(LValue value) { return buildNot(m_builder, value); }
LValue insertElement(LValue vector, LValue element, LValue index) { return buildInsertElement(m_builder, vector, element, index); }
LValue addWithOverflow32(LValue left, LValue right)
{
return call(addWithOverflow32Intrinsic(), left, right);
......@@ -183,6 +185,17 @@ public:
return call(doubleAbsIntrinsic(), value);
}
static bool hasSensibleDoubleToInt() { return isX86(); }
LValue sensibleDoubleToInt(LValue value)
{
RELEASE_ASSERT(isX86());
return call(
x86SSE2CvtTSD2SIIntrinsic(),
insertElement(
insertElement(getUndef(vectorType(doubleType, 2)), value, int32Zero),
doubleZero, int32One));
}
LValue signExt(LValue value, LType type) { return buildSExt(m_builder, value, type); }
LValue zeroExt(LValue value, LType type) { return buildZExt(m_builder, value, type); }
LValue fpToInt(LValue value, LType type) { return buildFPToSI(m_builder, value, type); }
......
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