WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
Patch
bug-156072-20160401075911.patch (text/plain), 52.91 KB, created by
Yusuke Suzuki
on 2016-03-31 15:59:08 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Yusuke Suzuki
Created:
2016-03-31 15:59:08 PDT
Size:
52.91 KB
patch
obsolete
>Subversion Revision: 198920 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index 1a2514cf47583bcd9b30dcaad779d4038607b5f3..c2354ef40eeeb765fedf315bfa5aae2dde7528b6 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,5 +1,112 @@ > 2016-03-31 Yusuke Suzuki <utatane.tea@gmail.com> > >+ [JSC] Add truncate operation (rounding to zero) >+ https://bugs.webkit.org/show_bug.cgi?id=156072 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add TruncIntrinsic for Math.trunc. DFG handles it as ArithTrunc. >+ In DFG, ArithTrunc behaves similar to ArithRound, ArithCeil, and ArithFloor. >+ ArithTrunc rounds the value towards zero. >+ >+ And we rewrite @toInteger to use @trunc instead of @abs, @floor, negation and branch. >+ This is completely the same to what we do in JSValue::toInteger. >+ >+ Since DFG recognize it, DFG can convert ArithTrunc to Identity if the given argument is Int32. >+ This is useful because almost all the argument is Int32 in @toLength -> @toInteger -> @trunc case. >+ In such cases, we can eliminate trunc() call. >+ >+ As a bonus, to speed up Math.trunc operation, we use x86 SSE round and frintz in ARM64 for ArithRound. >+ In DFG, we emit these instructions. In FTL, we use Patchpoint to emit these instructions to avoid adding a new B3 IR. >+ >+ * assembler/MacroAssemblerARM64.h: >+ (JSC::MacroAssemblerARM64::roundTowardZeroDouble): >+ (JSC::MacroAssemblerARM64::roundTowardZeroFloat): >+ * assembler/MacroAssemblerARMv7.h: >+ (JSC::MacroAssemblerARMv7::roundTowardZeroDouble): >+ * assembler/MacroAssemblerMIPS.h: >+ (JSC::MacroAssemblerMIPS::roundTowardZeroDouble): >+ * assembler/MacroAssemblerSH4.h: >+ (JSC::MacroAssemblerSH4::roundTowardZeroDouble): >+ * assembler/MacroAssemblerX86Common.h: >+ (JSC::MacroAssemblerX86Common::roundTowardZeroDouble): >+ (JSC::MacroAssemblerX86Common::roundTowardZeroFloat): >+ * builtins/GlobalObject.js: >+ (toInteger): >+ * dfg/DFGAbstractInterpreterInlines.h: >+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): >+ * dfg/DFGByteCodeParser.cpp: >+ (JSC::DFG::ByteCodeParser::handleIntrinsicCall): >+ * dfg/DFGClobberize.h: >+ (JSC::DFG::clobberize): >+ * dfg/DFGDoesGC.cpp: >+ (JSC::DFG::doesGC): >+ * dfg/DFGFixupPhase.cpp: >+ (JSC::DFG::FixupPhase::fixupNode): >+ * dfg/DFGGraph.h: >+ (JSC::DFG::Graph::roundShouldSpeculateInt32): >+ * dfg/DFGNode.h: >+ (JSC::DFG::Node::arithNodeFlags): >+ (JSC::DFG::Node::hasHeapPrediction): >+ (JSC::DFG::Node::hasArithRoundingMode): >+ * dfg/DFGNodeType.h: >+ * dfg/DFGPredictionPropagationPhase.cpp: >+ (JSC::DFG::PredictionPropagationPhase::propagate): >+ * dfg/DFGSafeToExecute.h: >+ (JSC::DFG::safeToExecute): >+ * dfg/DFGSpeculativeJIT.cpp: >+ (JSC::DFG::SpeculativeJIT::compileArithRounding): >+ * dfg/DFGSpeculativeJIT.h: >+ * dfg/DFGSpeculativeJIT32_64.cpp: >+ (JSC::DFG::SpeculativeJIT::compile): >+ * dfg/DFGSpeculativeJIT64.cpp: >+ (JSC::DFG::SpeculativeJIT::compile): >+ * ftl/FTLCapabilities.cpp: >+ (JSC::FTL::canCompile): >+ * ftl/FTLLowerDFGToB3.cpp: >+ (JSC::FTL::DFG::LowerDFGToB3::compileNode): >+ (JSC::FTL::DFG::LowerDFGToB3::compileArithTrunc): >+ * jit/ThunkGenerators.cpp: >+ (JSC::truncThunkGenerator): >+ * jit/ThunkGenerators.h: >+ * runtime/CommonIdentifiers.h: >+ * runtime/Intrinsic.h: >+ * runtime/JSGlobalObject.cpp: >+ (JSC::JSGlobalObject::init): >+ * runtime/MathObject.cpp: >+ (JSC::MathObject::finishCreation): >+ * runtime/MathObject.h: >+ * runtime/VM.cpp: >+ (JSC::thunkGeneratorForIntrinsic): >+ * tests/stress/math-rounding-infinity.js: >+ (testTrunc): >+ * tests/stress/math-rounding-nan.js: >+ (testTrunc): >+ * tests/stress/math-rounding-negative-zero.js: >+ (testTrunc): >+ * tests/stress/math-trunc-arith-rounding-mode.js: Added. >+ (firstCareAboutZeroSecondDoesNot): >+ (firstDoNotCareAboutZeroSecondDoes): >+ (warmup): >+ (verifyNegativeZeroIsPreserved): >+ * tests/stress/math-trunc-basics.js: Added. >+ (mathTruncOnIntegers): >+ (mathTruncOnDoubles): >+ (mathTruncOnBooleans): >+ (uselessMathTrunc): >+ (mathTruncWithOverflow): >+ (mathTruncConsumedAsDouble): >+ (mathTruncDoesNotCareAboutMinusZero): >+ (mathTruncNoArguments): >+ (mathTruncTooManyArguments): >+ (testMathTruncOnConstants): >+ (mathTruncStructTransition): >+ (Math.trunc): >+ * tests/stress/math-trunc-should-be-truncate.js: Added. >+ (mathTrunc): >+ >+2016-03-31 Yusuke Suzuki <utatane.tea@gmail.com> >+ > Fails to build in Linux / PowerPC due to different ucontext_t definition > https://bugs.webkit.org/show_bug.cgi?id=156015 > >diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerARM64.h b/Source/JavaScriptCore/assembler/MacroAssemblerARM64.h >index f619096fc219ac4d75667b4a6b117c9ebec3ff5a..de7e0e4c229414c81dd8c0cdabad0d0b0c163159 100644 >--- a/Source/JavaScriptCore/assembler/MacroAssemblerARM64.h >+++ b/Source/JavaScriptCore/assembler/MacroAssemblerARM64.h >@@ -1500,6 +1500,16 @@ class MacroAssemblerARM64 : public AbstractMacroAssembler<ARM64Assembler, MacroA > m_assembler.frintm<32>(dest, src); > } > >+ void roundTowardZeroDouble(FPRegisterID src, FPRegisterID dest) >+ { >+ m_assembler.frintz<64>(dest, src); >+ } >+ >+ void roundTowardZeroFloat(FPRegisterID src, FPRegisterID dest) >+ { >+ m_assembler.frintz<32>(dest, src); >+ } >+ > // Convert 'src' to an integer, and places the resulting 'dest'. > // If the result is not representable as a 32 bit value, branch. > // May also branch for some values that are representable in 32 bits >diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h b/Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h >index 3bc85bce901496fdfd71ffc6125e13264278baf3..d2256e1f68939a240e18edb9b7fc594ee0a88ad1 100644 >--- a/Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h >+++ b/Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h >@@ -1084,6 +1084,12 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler<ARMv7Assembler, MacroA > CRASH(); > } > >+ NO_RETURN_DUE_TO_CRASH void roundTowardZeroDouble(FPRegisterID, FPRegisterID) >+ { >+ ASSERT(!supportsFloatingPointRounding()); >+ CRASH(); >+ } >+ > void convertInt32ToDouble(RegisterID src, FPRegisterID dest) > { > m_assembler.vmov(fpTempRegister, src, src); >diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerMIPS.h b/Source/JavaScriptCore/assembler/MacroAssemblerMIPS.h >index 7c4bd080df1343b8eaed0f50224d2a2a99bde996..27a1842f21f9eef61aec0755fb4dc73c59f6b1e0 100644 >--- a/Source/JavaScriptCore/assembler/MacroAssemblerMIPS.h >+++ b/Source/JavaScriptCore/assembler/MacroAssemblerMIPS.h >@@ -659,6 +659,12 @@ class MacroAssemblerMIPS : public AbstractMacroAssembler<MIPSAssembler, MacroAss > CRASH(); > } > >+ NO_RETURN_DUE_TO_CRASH void roundTowardZeroDouble(FPRegisterID, FPRegisterID) >+ { >+ ASSERT(!supportsFloatingPointRounding()); >+ CRASH(); >+ } >+ > ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) > { > ConvertibleLoadLabel result(this); >diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerSH4.h b/Source/JavaScriptCore/assembler/MacroAssemblerSH4.h >index 75756095b800390dda9ec7de831990257d613bf6..53e71cfa3c7677cbab1546f93e9de3ecb21e5d7f 100644 >--- a/Source/JavaScriptCore/assembler/MacroAssemblerSH4.h >+++ b/Source/JavaScriptCore/assembler/MacroAssemblerSH4.h >@@ -1587,6 +1587,12 @@ class MacroAssemblerSH4 : public AbstractMacroAssembler<SH4Assembler, MacroAssem > CRASH(); > } > >+ NO_RETURN_DUE_TO_CRASH void roundTowardZeroDouble(FPRegisterID, FPRegisterID) >+ { >+ ASSERT(!supportsFloatingPointRounding()); >+ CRASH(); >+ } >+ > Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) > { > RegisterID addressTempRegister = claimScratch(); >diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h b/Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h >index 40a700c2940ce066005bf15cfe3e20601ad55280..a67cfd05e86874bee54bd471db4d46f4fef0d113 100644 >--- a/Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h >+++ b/Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h >@@ -735,6 +735,26 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler<X86Assembler, Macr > m_assembler.roundss_mr(src.offset, src.base, dst, X86Assembler::RoundingType::TowardNegativeInfiniti); > } > >+ void roundTowardZeroDouble(FPRegisterID src, FPRegisterID dst) >+ { >+ m_assembler.roundsd_rr(src, dst, X86Assembler::RoundingType::TowardZero); >+ } >+ >+ void roundTowardZeroDouble(Address src, FPRegisterID dst) >+ { >+ m_assembler.roundsd_mr(src.offset, src.base, dst, X86Assembler::RoundingType::TowardZero); >+ } >+ >+ void roundTowardZeroFloat(FPRegisterID src, FPRegisterID dst) >+ { >+ m_assembler.roundss_rr(src, dst, X86Assembler::RoundingType::TowardZero); >+ } >+ >+ void roundTowardZeroFloat(Address src, FPRegisterID dst) >+ { >+ m_assembler.roundss_mr(src.offset, src.base, dst, X86Assembler::RoundingType::TowardZero); >+ } >+ > // Memory access operations: > // > // Loads are of the form load(address, destination) and stores of the form >diff --git a/Source/JavaScriptCore/builtins/GlobalObject.js b/Source/JavaScriptCore/builtins/GlobalObject.js >index db53c397bbf54c0d50d6d0414edc823d33845d36..5bcbc226d2f526d91c3de899f1bbca95739e679e 100644 >--- a/Source/JavaScriptCore/builtins/GlobalObject.js >+++ b/Source/JavaScriptCore/builtins/GlobalObject.js >@@ -34,11 +34,7 @@ function toInteger(target) > // isNaN(numberValue) > if (numberValue !== numberValue) > return 0; >- >- if (numberValue === 0 || !@isFinite(numberValue)) >- return numberValue; >- >- return (numberValue > 0 ? 1 : -1) * @floor(@abs(numberValue)); >+ return @trunc(numberValue); > } > > function toLength(target) >diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >index 0274820c86e48a800ae33b757ef68429e3ea9a45..35b7d525b31a2184cc5d1f6ca7bd4fbfaf40eae3 100644 >--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >@@ -850,7 +850,8 @@ > > case ArithRound: > case ArithFloor: >- case ArithCeil: { >+ case ArithCeil: >+ case ArithTrunc: { > JSValue operand = forNode(node->child1()).value(); > if (operand && operand.isNumber()) { > double roundedValue = 0; >@@ -858,9 +859,11 @@ > roundedValue = jsRound(operand.asNumber()); > else if (node->op() == ArithFloor) > roundedValue = floor(operand.asNumber()); >- else { >- ASSERT(node->op() == ArithCeil); >+ else if (node->op() == ArithCeil) > roundedValue = ceil(operand.asNumber()); >+ else { >+ ASSERT(node->op() == ArithTrunc); >+ roundedValue = trunc(operand.asNumber()); > } > > if (producesInteger(node->arithRoundingMode())) { >diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >index a6a87e52a2a5856cadb1389d89dac943c12f5f12..6716687788d06504e2ed28bdf9b2bb196244b609 100644 >--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >@@ -2203,7 +2203,8 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin > > case RoundIntrinsic: > case FloorIntrinsic: >- case CeilIntrinsic: { >+ case CeilIntrinsic: >+ case TruncIntrinsic: { > if (argumentCountIncludingThis == 1) { > insertChecks(); > set(VirtualRegister(resultOperand), addToGraph(JSConstant, OpInfo(m_constantNaN))); >@@ -2217,9 +2218,11 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin > op = ArithRound; > else if (intrinsic == FloorIntrinsic) > op = ArithFloor; >- else { >- ASSERT(intrinsic == CeilIntrinsic); >+ else if (intrinsic == CeilIntrinsic) > op = ArithCeil; >+ else { >+ ASSERT(intrinsic == TruncIntrinsic); >+ op = ArithTrunc; > } > Node* roundNode = addToGraph(op, OpInfo(0), OpInfo(prediction), operand); > set(VirtualRegister(resultOperand), roundNode); >diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h >index 028b2985b4ebda9064e30347d61b5f37879df693..6a4eda2ef891680e5dfb3f44d1f405ecdac94478 100644 >--- a/Source/JavaScriptCore/dfg/DFGClobberize.h >+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h >@@ -305,6 +305,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu > case ArithRound: > case ArithFloor: > case ArithCeil: >+ case ArithTrunc: > def(PureValue(node, static_cast<uintptr_t>(node->arithRoundingMode()))); > return; > >diff --git a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp >index 0564d1fb5544d9d9956532117a9148e867b16be9..f38b9f99f80fa465dad3145db9eeed35acc597df 100644 >--- a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp >+++ b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp >@@ -90,6 +90,7 @@ bool doesGC(Graph& graph, Node* node) > case ArithRound: > case ArithFloor: > case ArithCeil: >+ case ArithTrunc: > case ArithFRound: > case ArithSin: > case ArithCos: >diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >index 61d19462a1ad5b9706e81677cdeb1e86ab8dca34..9612e18d061c5c509173591bde4d7cd7ef2f3172 100644 >--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >@@ -365,7 +365,8 @@ class FixupPhase : public Phase { > > case ArithRound: > case ArithFloor: >- case ArithCeil: { >+ case ArithCeil: >+ case ArithTrunc: { > if (m_graph.unaryArithShouldSpeculateInt32(node, FixupPass)) { > fixIntOrBooleanEdge(node->child1()); > insertCheck<Int32Use>(m_indexInBlock, node->child1().node()); >diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h >index bbd46eada101e7e383b6dcc250578217b4285e01..467db0ed9f757bdeed6b0e5eb286c18fe7fc8eca 100644 >--- a/Source/JavaScriptCore/dfg/DFGGraph.h >+++ b/Source/JavaScriptCore/dfg/DFGGraph.h >@@ -316,7 +316,7 @@ class Graph : public virtual Scannable { > > bool roundShouldSpeculateInt32(Node* arithRound, PredictionPass pass) > { >- ASSERT(arithRound->op() == ArithRound || arithRound->op() == ArithFloor || arithRound->op() == ArithCeil); >+ ASSERT(arithRound->op() == ArithRound || arithRound->op() == ArithFloor || arithRound->op() == ArithCeil || arithRound->op() == ArithTrunc); > return arithRound->canSpeculateInt32(pass) && !hasExitSite(arithRound->origin.semantic, Overflow) && !hasExitSite(arithRound->origin.semantic, NegativeZero); > } > >diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h >index 9e87973bb70f6a520686f753d64b7f4d5f99c068..28e5995655d098aa2b3cd70deeeda1763e46fbf1 100644 >--- a/Source/JavaScriptCore/dfg/DFGNode.h >+++ b/Source/JavaScriptCore/dfg/DFGNode.h >@@ -943,7 +943,7 @@ struct Node { > NodeFlags arithNodeFlags() > { > NodeFlags result = m_flags & NodeArithFlagsMask; >- if (op() == ArithMul || op() == ArithDiv || op() == ArithMod || op() == ArithNegate || op() == ArithPow || op() == ArithRound || op() == ArithFloor || op() == ArithCeil || op() == DoubleAsInt32) >+ if (op() == ArithMul || op() == ArithDiv || op() == ArithMod || op() == ArithNegate || op() == ArithPow || op() == ArithRound || op() == ArithFloor || op() == ArithCeil || op() == ArithTrunc || op() == DoubleAsInt32) > return result; > return result & ~NodeBytecodeNeedsNegZero; > } >@@ -1351,6 +1351,7 @@ struct Node { > case ArithRound: > case ArithFloor: > case ArithCeil: >+ case ArithTrunc: > case GetDirectPname: > case GetById: > case GetByIdFlush: >@@ -1733,7 +1734,7 @@ struct Node { > > bool hasArithRoundingMode() > { >- return op() == ArithRound || op() == ArithFloor || op() == ArithCeil; >+ return op() == ArithRound || op() == ArithFloor || op() == ArithCeil || op() == ArithTrunc; > } > > Arith::RoundingMode arithRoundingMode() >diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h >index 8e2a133c26a45acc237f823ddd2794cfe0bbac97..f1e1a56f19dfc1ff21345de0f90918005fd0f628 100644 >--- a/Source/JavaScriptCore/dfg/DFGNodeType.h >+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h >@@ -158,6 +158,7 @@ > macro(ArithRound, NodeResultNumber) \ > macro(ArithFloor, NodeResultNumber) \ > macro(ArithCeil, NodeResultNumber) \ >+ macro(ArithTrunc, NodeResultNumber) \ > macro(ArithSqrt, NodeResultNumber) \ > macro(ArithSin, NodeResultNumber) \ > macro(ArithCos, NodeResultNumber) \ >diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >index 83fa08d4ae4e6f6a2f5f3fdf1bf46d62346825bf..df4d507ca2d8e4a13980b55dc19763ad458ebd0b 100644 >--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >@@ -389,7 +389,8 @@ class PredictionPropagationPhase : public Phase { > > case ArithRound: > case ArithFloor: >- case ArithCeil: { >+ case ArithCeil: >+ case ArithTrunc: { > if (isInt32OrBooleanSpeculation(node->getHeapPrediction()) && m_graph.roundShouldSpeculateInt32(node, m_pass)) > changed |= setPrediction(SpecInt32); > else >diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h >index a2521db1b53704c1e004662b06bde9ed941bc81e..8077b859018b4ff2adbd72b11c41164fcb552e48 100644 >--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h >+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h >@@ -186,6 +186,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node) > case ArithRound: > case ArithFloor: > case ArithCeil: >+ case ArithTrunc: > case ArithSin: > case ArithCos: > case ArithLog: >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >index c57c56daa1fe370a39cebedcf92b2f41d329ae93..3ad03dcba23c9666de6d839b2661bd33f6d92032 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >@@ -4505,6 +4505,14 @@ void SpeculativeJIT::compileArithRounding(Node* node) > return; > } > >+ case ArithTrunc: { >+ FPRTemporary rounded(this); >+ FPRReg resultFPR = rounded.fpr(); >+ m_jit.roundTowardZeroDouble(valueFPR, resultFPR); >+ setResult(resultFPR); >+ return; >+ } >+ > default: > RELEASE_ASSERT_NOT_REACHED(); > } >@@ -4516,9 +4524,11 @@ void SpeculativeJIT::compileArithRounding(Node* node) > callOperation(jsRound, resultFPR, valueFPR); > else if (node->op() == ArithFloor) > callOperation(floor, resultFPR, valueFPR); >- else { >- ASSERT(node->op() == ArithCeil); >+ else if (node->op() == ArithCeil) > callOperation(ceil, resultFPR, valueFPR); >+ else { >+ ASSERT(node->op() == ArithTrunc); >+ callOperation(trunc, resultFPR, valueFPR); > } > m_jit.exceptionCheck(); > setResult(resultFPR); >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >index 45813a411889566c478ad64d022c138aea0b79fe..a6f90c7d076f752f6d36f7afe76e4e6c6b58ea76 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >@@ -2373,8 +2373,6 @@ class SpeculativeJIT { > void compileArithMod(Node*); > void compileArithPow(Node*); > void compileArithRounding(Node*); >- void compileArithFloor(Node*); >- void compileArithCeil(Node*); > void compileArithRandom(Node*); > void compileArithSqrt(Node*); > void compileArithLog(Node*); >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >index d7cf037a950ebd58635daedfc4e77eb7924bca86..51f3e474790fbbb148557b8d59c45621fd590ea5 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >@@ -2306,6 +2306,7 @@ void SpeculativeJIT::compile(Node* node) > case ArithRound: > case ArithFloor: > case ArithCeil: >+ case ArithTrunc: > compileArithRounding(node); > break; > >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >index 8a18194c5d09841b2ef1224aadc138f3fdb875f9..c4fde9fb04121b69ce1441fa532a78010ef3fd6e 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >@@ -2444,6 +2444,7 @@ void SpeculativeJIT::compile(Node* node) > case ArithRound: > case ArithFloor: > case ArithCeil: >+ case ArithTrunc: > compileArithRounding(node); > break; > >diff --git a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp >index 71a2fb2d3410a535a2b7c7db0c1cfa77fb37db69..2c8384892ced84fa439231f0283a27a6bee94d84 100644 >--- a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp >+++ b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp >@@ -99,6 +99,7 @@ inline CapabilityLevel canCompile(Node* node) > case ArithRound: > case ArithFloor: > case ArithCeil: >+ case ArithTrunc: > case ArithSqrt: > case ArithLog: > case ArithFRound: >diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >index 654bd6f916d9b8ccc073b747348bdd17c9a079af..212e1d4c2f015fcce57c6874aafeef7ca3d33048 100644 >--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >@@ -532,6 +532,9 @@ class LowerDFGToB3 { > case ArithCeil: > compileArithCeil(); > break; >+ case ArithTrunc: >+ compileArithTrunc(); >+ break; > case ArithSqrt: > compileArithSqrt(); > break; >@@ -1976,6 +1979,26 @@ class LowerDFGToB3 { > setDouble(integerValue); > } > >+ void compileArithTrunc() >+ { >+ LValue value = lowDouble(m_node->child1()); >+ LValue result; >+ if (MacroAssembler::supportsFloatingPointRounding()) { >+ PatchpointValue* patchpoint = m_out.patchpoint(Double); >+ patchpoint->appendSomeRegister(value); >+ patchpoint->setGenerator([&] (CCallHelpers& jit, const StackmapGenerationParams& params) { >+ jit.roundTowardZeroDouble(params[1].fpr(), params[0].fpr()); >+ }); >+ result = patchpoint; >+ } else >+ result = vmCall(m_out.doubleType, m_out.operation(trunc), value); >+ >+ if (producesInteger(m_node->arithRoundingMode())) >+ setInt32(convertDoubleToInt32(result, shouldCheckNegativeZero(m_node->arithRoundingMode()))); >+ else >+ setDouble(result); >+ } >+ > void compileArithSqrt() { setDouble(m_out.doubleSqrt(lowDouble(m_node->child1()))); } > > void compileArithLog() { setDouble(m_out.doubleLog(lowDouble(m_node->child1()))); } >diff --git a/Source/JavaScriptCore/jit/ThunkGenerators.cpp b/Source/JavaScriptCore/jit/ThunkGenerators.cpp >index 25d00140c0b5a0e7bff8f68a2debe7c446662d49..c99b92e6b1b1a93908f4e4baaa41b1ae8107ba6e 100644 >--- a/Source/JavaScriptCore/jit/ThunkGenerators.cpp >+++ b/Source/JavaScriptCore/jit/ThunkGenerators.cpp >@@ -740,6 +740,7 @@ MacroAssemblerCodeRef sqrtThunkGenerator(VM* vm) > // MSVC does not accept floor, etc, to be called directly from inline assembly, so we need to wrap these functions. > static double (_cdecl *floorFunction)(double) = floor; > static double (_cdecl *ceilFunction)(double) = ceil; >+static double (_cdecl *truncFunction)(double) = trunc; > static double (_cdecl *expFunction)(double) = exp; > static double (_cdecl *logFunction)(double) = log; > static double (_cdecl *jsRoundFunction)(double) = jsRound; >@@ -771,6 +772,7 @@ MacroAssemblerCodeRef sqrtThunkGenerator(VM* vm) > defineUnaryDoubleOpWrapper(log); > defineUnaryDoubleOpWrapper(floor); > defineUnaryDoubleOpWrapper(ceil); >+defineUnaryDoubleOpWrapper(trunc); > > static const double oneConstant = 1.0; > static const double negativeHalfConstant = -0.5; >@@ -842,6 +844,29 @@ MacroAssemblerCodeRef ceilThunkGenerator(VM* vm) > return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "ceil"); > } > >+MacroAssemblerCodeRef truncThunkGenerator(VM* vm) >+{ >+ SpecializedThunkJIT jit(vm, 1); >+ if (!UnaryDoubleOpWrapper(trunc) || !jit.supportsFloatingPoint()) >+ return MacroAssemblerCodeRef::createSelfManagedCodeRef(vm->jitStubs->ctiNativeCall(vm)); >+ MacroAssembler::Jump nonIntJump; >+ jit.loadInt32Argument(0, SpecializedThunkJIT::regT0, nonIntJump); >+ jit.returnInt32(SpecializedThunkJIT::regT0); >+ nonIntJump.link(&jit); >+ jit.loadDoubleArgument(0, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0); >+ if (jit.supportsFloatingPointRounding()) >+ jit.roundTowardZeroDouble(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT0); >+ else >+ jit.callDoubleToDoublePreservingReturn(UnaryDoubleOpWrapper(trunc)); >+ >+ SpecializedThunkJIT::JumpList doubleResult; >+ jit.branchConvertDoubleToInt32(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0, doubleResult, SpecializedThunkJIT::fpRegT1); >+ jit.returnInt32(SpecializedThunkJIT::regT0); >+ doubleResult.link(&jit); >+ jit.returnDouble(SpecializedThunkJIT::fpRegT0); >+ return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "trunc"); >+} >+ > MacroAssemblerCodeRef roundThunkGenerator(VM* vm) > { > SpecializedThunkJIT jit(vm, 1); >diff --git a/Source/JavaScriptCore/jit/ThunkGenerators.h b/Source/JavaScriptCore/jit/ThunkGenerators.h >index 9fb8abb44ea22dbd852756a9cb8a9dbceacd3789..82d47fa4687fbcda9b3aa428b14d903f30d4f893 100644 >--- a/Source/JavaScriptCore/jit/ThunkGenerators.h >+++ b/Source/JavaScriptCore/jit/ThunkGenerators.h >@@ -63,6 +63,7 @@ > MacroAssemblerCodeRef powThunkGenerator(VM*); > MacroAssemblerCodeRef imulThunkGenerator(VM*); > MacroAssemblerCodeRef randomThunkGenerator(VM*); >+MacroAssemblerCodeRef truncThunkGenerator(VM*); > > } > #endif // ENABLE(JIT) >diff --git a/Source/JavaScriptCore/runtime/CommonIdentifiers.h b/Source/JavaScriptCore/runtime/CommonIdentifiers.h >index 6bb5180b6e7ddd7c5b6c1d5331d7ae824d2e0232..9dea78c195a57a64284ebdbcb917a026ddf71757 100644 >--- a/Source/JavaScriptCore/runtime/CommonIdentifiers.h >+++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.h >@@ -315,6 +315,7 @@ > macro(InternalPromise) \ > macro(abs) \ > macro(floor) \ >+ macro(trunc) \ > macro(isFinite) \ > macro(isNaN) \ > macro(create) \ >diff --git a/Source/JavaScriptCore/runtime/Intrinsic.h b/Source/JavaScriptCore/runtime/Intrinsic.h >index d8de006ea5541a5ecd7ff411a1a0a61f85c86c3f..08fdeb447997225a72e761fcf9730b2347b9a045 100644 >--- a/Source/JavaScriptCore/runtime/Intrinsic.h >+++ b/Source/JavaScriptCore/runtime/Intrinsic.h >@@ -56,6 +56,7 @@ enum JS_EXPORT_PRIVATE Intrinsic { > IMulIntrinsic, > RandomIntrinsic, > FRoundIntrinsic, >+ TruncIntrinsic, > > // Getter intrinsics. > TypedArrayLengthIntrinsic, >diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp >index 175513ff178c8252e0364e7abbcd517e60acee47..0772c176811e4b1c54fef7ad819b9816dd09ebe1 100644 >--- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp >+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp >@@ -526,6 +526,7 @@ void JSGlobalObject::init(VM& vm) > JSFunction* privateFuncFloor = JSFunction::create(vm, this, 0, String(), mathProtoFuncFloor, FloorIntrinsic); > JSFunction* privateFuncIsFinite = JSFunction::create(vm, this, 0, String(), globalFuncIsFinite); > JSFunction* privateFuncIsNaN = JSFunction::create(vm, this, 0, String(), globalFuncIsNaN); >+ JSFunction* privateFuncTrunc = JSFunction::create(vm, this, 0, String(), mathProtoFuncTrunc, TruncIntrinsic); > > JSFunction* privateFuncGetTemplateObject = JSFunction::create(vm, this, 0, String(), getTemplateObject); > JSFunction* privateFuncToLength = JSFunction::createBuiltinFunction(vm, globalObjectToLengthCodeGenerator(vm), this); >@@ -563,6 +564,7 @@ void JSGlobalObject::init(VM& vm) > GlobalPropertyInfo(vm.propertyNames->StringPrivateName, stringConstructor, DontEnum | DontDelete | ReadOnly), > GlobalPropertyInfo(vm.propertyNames->absPrivateName, privateFuncAbs, DontEnum | DontDelete | ReadOnly), > GlobalPropertyInfo(vm.propertyNames->floorPrivateName, privateFuncFloor, DontEnum | DontDelete | ReadOnly), >+ GlobalPropertyInfo(vm.propertyNames->truncPrivateName, privateFuncTrunc, DontEnum | DontDelete | ReadOnly), > GlobalPropertyInfo(vm.propertyNames->isFinitePrivateName, privateFuncIsFinite, DontEnum | DontDelete | ReadOnly), > GlobalPropertyInfo(vm.propertyNames->isNaNPrivateName, privateFuncIsNaN, DontEnum | DontDelete | ReadOnly), > GlobalPropertyInfo(vm.propertyNames->PromisePrivateName, promiseConstructor, DontEnum | DontDelete | ReadOnly), >diff --git a/Source/JavaScriptCore/runtime/MathObject.cpp b/Source/JavaScriptCore/runtime/MathObject.cpp >index 997b0647e5935d6eafc76eebf49829f81e214fcd..66d31b74d9a876724a97ca50a9a63a18cc9636ec 100644 >--- a/Source/JavaScriptCore/runtime/MathObject.cpp >+++ b/Source/JavaScriptCore/runtime/MathObject.cpp >@@ -67,7 +67,6 @@ > EncodedJSValue JSC_HOST_CALL mathProtoFuncSqrt(ExecState*); > EncodedJSValue JSC_HOST_CALL mathProtoFuncTan(ExecState*); > EncodedJSValue JSC_HOST_CALL mathProtoFuncTanh(ExecState*); >-EncodedJSValue JSC_HOST_CALL mathProtoFuncTrunc(ExecState*); > EncodedJSValue JSC_HOST_CALL mathProtoFuncIMul(ExecState*); > > } >@@ -129,7 +128,7 @@ void MathObject::finishCreation(VM& vm, JSGlobalObject* globalObject) > putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "sqrt"), 1, mathProtoFuncSqrt, SqrtIntrinsic, DontEnum); > putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "tan"), 1, mathProtoFuncTan, NoIntrinsic, DontEnum); > putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "tanh"), 1, mathProtoFuncTanh, NoIntrinsic, DontEnum); >- putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "trunc"), 1, mathProtoFuncTrunc, NoIntrinsic, DontEnum); >+ putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "trunc"), 1, mathProtoFuncTrunc, TruncIntrinsic, DontEnum); > putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "imul"), 2, mathProtoFuncIMul, IMulIntrinsic, DontEnum); > } > >diff --git a/Source/JavaScriptCore/runtime/MathObject.h b/Source/JavaScriptCore/runtime/MathObject.h >index 9d1c181c0f1efaa20b35f7e85d8dfadbff46379b..1a133d554da440a75dd4166776ee78e8d2b2dfdf 100644 >--- a/Source/JavaScriptCore/runtime/MathObject.h >+++ b/Source/JavaScriptCore/runtime/MathObject.h >@@ -53,6 +53,7 @@ class MathObject : public JSNonFinalObject { > > EncodedJSValue JSC_HOST_CALL mathProtoFuncAbs(ExecState*); > EncodedJSValue JSC_HOST_CALL mathProtoFuncFloor(ExecState*); >+EncodedJSValue JSC_HOST_CALL mathProtoFuncTrunc(ExecState*); > > } // namespace JSC > >diff --git a/Source/JavaScriptCore/runtime/VM.cpp b/Source/JavaScriptCore/runtime/VM.cpp >index 556aac4624297e5251c57eaebcf18df6892d17c2..8264b110b32935f33413f034d76c663c7f75abe8 100644 >--- a/Source/JavaScriptCore/runtime/VM.cpp >+++ b/Source/JavaScriptCore/runtime/VM.cpp >@@ -479,6 +479,8 @@ static ThunkGenerator thunkGeneratorForIntrinsic(Intrinsic intrinsic) > return floorThunkGenerator; > case CeilIntrinsic: > return ceilThunkGenerator; >+ case TruncIntrinsic: >+ return truncThunkGenerator; > case RoundIntrinsic: > return roundThunkGenerator; > case ExpIntrinsic: >diff --git a/Source/JavaScriptCore/tests/stress/math-rounding-infinity.js b/Source/JavaScriptCore/tests/stress/math-rounding-infinity.js >index 21e021e00bc610850fc429b36d6c481cdef7fad0..a935380041314fbf2d011be6013222840831206d 100644 >--- a/Source/JavaScriptCore/tests/stress/math-rounding-infinity.js >+++ b/Source/JavaScriptCore/tests/stress/math-rounding-infinity.js >@@ -21,6 +21,12 @@ function testCeil(value) > } > noInline(testCeil); > >+function testTrunc(value) >+{ >+ return Math.trunc(value); >+} >+noInline(testTrunc); >+ > for (var i = 0; i < 1e4; ++i) { > shouldBe(testRound(Infinity), Infinity); > shouldBe(testRound(-Infinity), -Infinity); >@@ -28,4 +34,6 @@ function testCeil(value) > shouldBe(testFloor(-Infinity), -Infinity); > shouldBe(testCeil(Infinity), Infinity); > shouldBe(testCeil(-Infinity), -Infinity); >+ shouldBe(testTrunc(Infinity), Infinity); >+ shouldBe(testTrunc(-Infinity), -Infinity); > } >diff --git a/Source/JavaScriptCore/tests/stress/math-rounding-nan.js b/Source/JavaScriptCore/tests/stress/math-rounding-nan.js >index ecccdbc7f7d669e5e3d889f1452c8a3dca17cc58..de1e1344a06da05e2ad8df70552b7687cc241c09 100644 >--- a/Source/JavaScriptCore/tests/stress/math-rounding-nan.js >+++ b/Source/JavaScriptCore/tests/stress/math-rounding-nan.js >@@ -21,8 +21,15 @@ function testCeil(value) > } > noInline(testCeil); > >+function testTrunc(value) >+{ >+ return Math.trunc(value); >+} >+noInline(testTrunc); >+ > for (var i = 0; i < 1e4; ++i) { > shouldBe(Number.isNaN(testRound(NaN)), true); > shouldBe(Number.isNaN(testFloor(NaN)), true); > shouldBe(Number.isNaN(testCeil(NaN)), true); >+ shouldBe(Number.isNaN(testTrunc(NaN)), true); > } >diff --git a/Source/JavaScriptCore/tests/stress/math-rounding-negative-zero.js b/Source/JavaScriptCore/tests/stress/math-rounding-negative-zero.js >index abae27850c029902133c6094d1d5c2799bc57ab7..1a5baa24bea73303fd3fd1513cfd34661e32cb22 100644 >--- a/Source/JavaScriptCore/tests/stress/math-rounding-negative-zero.js >+++ b/Source/JavaScriptCore/tests/stress/math-rounding-negative-zero.js >@@ -67,4 +67,13 @@ function testRoundNonNegativeZero2(value) > shouldBe(1 / testRoundNonNegativeZero2(0.1), Infinity); > } > >+function testTrunc(value) >+{ >+ return Math.trunc(value); >+} >+noInline(testTrunc); > >+for (var i = 0; i < 1e4; ++i) { >+ shouldBe(1 / testTrunc(0.0), Infinity); >+ shouldBe(1 / testTrunc(-0.0), -Infinity); >+} >diff --git a/Source/JavaScriptCore/tests/stress/math-trunc-arith-rounding-mode.js b/Source/JavaScriptCore/tests/stress/math-trunc-arith-rounding-mode.js >new file mode 100644 >index 0000000000000000000000000000000000000000..ddfb1e3913978ca57056466e4865e7400c8fa926 >--- /dev/null >+++ b/Source/JavaScriptCore/tests/stress/math-trunc-arith-rounding-mode.js >@@ -0,0 +1,85 @@ >+function firstCareAboutZeroSecondDoesNot(a) { >+ var resultA = Math.trunc(a); >+ var resultB = Math.trunc(a)|0; >+ return { resultA:resultA, resultB:resultB }; >+} >+noInline(firstCareAboutZeroSecondDoesNot); >+ >+function firstDoNotCareAboutZeroSecondDoes(a) { >+ var resultA = Math.trunc(a)|0; >+ var resultB = Math.trunc(a); >+ return { resultA:resultA, resultB:resultB }; >+} >+noInline(firstDoNotCareAboutZeroSecondDoes); >+ >+// Warmup with doubles, but nothing that would trunc to -0 to ensure we never >+// see a double as result. The result must be integers, the input is kept to small values. >+function warmup() { >+ for (var i = 0; i < 1e4; ++i) { >+ firstCareAboutZeroSecondDoesNot(42.6 + i); >+ firstDoNotCareAboutZeroSecondDoes(42.4 + i); >+ } >+} >+warmup(); >+ >+function verifyNegativeZeroIsPreserved() { >+ for (var i = 0; i < 1e4; ++i) { >+ var result1 = firstCareAboutZeroSecondDoesNot(-0.1); >+ if (1 / result1.resultA !== -Infinity) { >+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(-0.1), resultA = " + result1.resultA); >+ } >+ if (1 / result1.resultB !== Infinity) { >+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(-0.1), resultB = " + result1.resultB); >+ } >+ var result2 = firstDoNotCareAboutZeroSecondDoes(-0.1); >+ if (1 / result2.resultA !== Infinity) { >+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(-0.1), resultA = " + result1.resultA); >+ } >+ if (1 / result2.resultB !== -Infinity) { >+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(-0.1), resultB = " + result1.resultB); >+ } >+ var result3 = firstCareAboutZeroSecondDoesNot(-0.0); >+ if (1 / result3.resultA !== -Infinity) { >+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(-0.0), resultA = " + result3.resultA); >+ } >+ if (1 / result3.resultB !== Infinity) { >+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(-0.0), resultB = " + result3.resultB); >+ } >+ var result4 = firstDoNotCareAboutZeroSecondDoes(-0.0); >+ if (1 / result4.resultA !== Infinity) { >+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(-0.0), resultA = " + result4.resultA); >+ } >+ if (1 / result4.resultB !== -Infinity) { >+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(-0.0), resultB = " + result4.resultB); >+ } >+ var result5 = firstCareAboutZeroSecondDoesNot(0.0); >+ if (1 / result5.resultA !== Infinity) { >+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(0.0), resultA = " + result5.resultA); >+ } >+ if (1 / result5.resultB !== Infinity) { >+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(0.0), resultB = " + result5.resultB); >+ } >+ var result6 = firstDoNotCareAboutZeroSecondDoes(0.0); >+ if (1 / result6.resultA !== Infinity) { >+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(0.0), resultA = " + result6.resultA); >+ } >+ if (1 / result6.resultB !== Infinity) { >+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(0.0), resultB = " + result6.resultB); >+ } >+ var result7 = firstCareAboutZeroSecondDoesNot(0.1); >+ if (1 / result7.resultA !== Infinity) { >+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(0.1), resultA = " + result7.resultA); >+ } >+ if (1 / result7.resultB !== Infinity) { >+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(0.1), resultB = " + result7.resultB); >+ } >+ var result8 = firstDoNotCareAboutZeroSecondDoes(0.1); >+ if (1 / result8.resultA !== Infinity) { >+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(0.1), resultA = " + result8.resultA); >+ } >+ if (1 / result8.resultB !== Infinity) { >+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(0.1), resultB = " + result8.resultB); >+ } >+ } >+} >+verifyNegativeZeroIsPreserved(); >diff --git a/Source/JavaScriptCore/tests/stress/math-trunc-basics.js b/Source/JavaScriptCore/tests/stress/math-trunc-basics.js >new file mode 100644 >index 0000000000000000000000000000000000000000..beced2f71f369979968bd175746bad00909c7116 >--- /dev/null >+++ b/Source/JavaScriptCore/tests/stress/math-trunc-basics.js >@@ -0,0 +1,256 @@ >+function mathTruncOnIntegers(value) >+{ >+ return Math.trunc(value); >+} >+noInline(mathTruncOnIntegers); >+ >+function mathTruncOnDoubles(value) >+{ >+ return Math.trunc(value); >+} >+noInline(mathTruncOnDoubles); >+ >+function mathTruncOnBooleans(value) >+{ >+ return Math.trunc(value); >+} >+noInline(mathTruncOnBooleans); >+ >+// The trivial cases first. >+for (var i = 1; i < 1e4; ++i) { >+ var truncedValue = mathTruncOnIntegers(i); >+ if (truncedValue !== i) >+ throw new Error("mathTruncOnIntegers(" + i + ") = " + truncedValue); >+ >+ var truncedValue = mathTruncOnIntegers(-i); >+ if (truncedValue !== -i) >+ throw new Error("mathTruncOnIntegers(" + -i + ") = " + truncedValue); >+ >+ var doubleLow = i + 0.4; >+ var truncedValue = mathTruncOnDoubles(doubleLow); >+ if (truncedValue !== i) >+ throw new Error("mathTruncOnDoubles(" + doubleLow + ") = " + truncedValue); >+ >+ var doubleHigh = i + 0.6; >+ var truncedValue = mathTruncOnDoubles(doubleHigh); >+ if (truncedValue !== i) >+ throw new Error("mathTruncOnDoubles(" + doubleHigh + ") = " + truncedValue); >+ >+ var doubleMid = i + 0.5; >+ var truncedValue = mathTruncOnDoubles(doubleMid); >+ if (truncedValue !== i) >+ throw new Error("mathTruncOnDoubles(" + doubleMid + ") = " + truncedValue); >+ >+ var truncedValue = mathTruncOnDoubles(-0.6); >+ if (truncedValue !== -0.0) >+ throw new Error("mathTruncOnDoubles(-0.6) = " + truncedValue); >+} >+ >+// Some more interesting cases, some of them well OSR exit when the return value is zero. >+for (var i = 0; i < 1e4; ++i) { >+ var truncedValue = mathTruncOnIntegers(i); >+ if (truncedValue !== i) >+ throw new Error("mathTruncOnIntegers(" + i + ") = " + truncedValue); >+ >+ var truncedValue = mathTruncOnIntegers(-i); >+ if (truncedValue !== -i) >+ throw new Error("mathTruncOnIntegers(-" + i + ") = " + truncedValue); >+ >+ var truncedValue = mathTruncOnDoubles(-0.4); >+ if (truncedValue !== -0.0) >+ throw new Error("mathTruncOnDoubles(-0.4) = " + truncedValue); >+ >+ var truncedValue = mathTruncOnDoubles(-0.5); >+ if (truncedValue !== -0.0) >+ throw new Error("mathTruncOnDoubles(-0.5) = " + truncedValue); >+ >+ var truncedValue = mathTruncOnDoubles(-0); >+ if (!(truncedValue === 0 && (1/truncedValue) === -Infinity)) >+ throw new Error("mathTruncOnDoubles(-0) = " + truncedValue); >+ >+ var truncedValue = mathTruncOnDoubles(NaN); >+ if (truncedValue === truncedValue) >+ throw new Error("mathTruncOnDoubles(NaN) = " + truncedValue); >+ >+ var truncedValue = mathTruncOnDoubles(Number.POSITIVE_INFINITY); >+ if (truncedValue !== Number.POSITIVE_INFINITY) >+ throw new Error("mathTruncOnDoubles(Number.POSITIVE_INFINITY) = " + truncedValue); >+ >+ var truncedValue = mathTruncOnDoubles(Number.NEGATIVE_INFINITY); >+ if (truncedValue !== Number.NEGATIVE_INFINITY) >+ throw new Error("mathTruncOnDoubles(Number.NEGATIVE_INFINITY) = " + truncedValue); >+ >+ var boolean = !!(i % 2); >+ var truncedBoolean = mathTruncOnBooleans(boolean); >+ if (truncedBoolean != boolean) >+ throw new Error("mathTruncOnDoubles(" + boolean + ") = " + truncedBoolean); >+} >+ >+function uselessMathTrunc(value) >+{ >+ return Math.trunc(value|0); >+} >+noInline(uselessMathTrunc); >+ >+for (var i = 0; i < 1e4; ++i) { >+ var truncedValue = uselessMathTrunc(i); >+ if (truncedValue !== i) >+ throw new Error("uselessMathTrunc(" + i + ") = " + truncedValue); >+ >+ var doubleLow = i + 0.4; >+ var truncedValue = uselessMathTrunc(doubleLow); >+ if (truncedValue !== i) >+ throw new Error("uselessMathTrunc(" + doubleLow + ") = " + truncedValue); >+ >+ var doubleHigh = i + 0.6; >+ var truncedValue = uselessMathTrunc(doubleHigh); >+ if (truncedValue !== i) >+ throw new Error("uselessMathTrunc(" + doubleHigh + ") = " + truncedValue); >+ >+ var doubleMid = i + 0.5; >+ var truncedValue = uselessMathTrunc(doubleMid); >+ if (truncedValue !== i) >+ throw new Error("uselessMathTrunc(" + doubleMid + ") = " + truncedValue); >+ >+ var truncedValue = uselessMathTrunc(-0.4); >+ if (truncedValue !== 0) >+ throw new Error("uselessMathTrunc(-0.4) = " + truncedValue); >+ >+ var truncedValue = uselessMathTrunc(-0.5); >+ if (truncedValue !== 0) >+ throw new Error("uselessMathTrunc(-0.5) = " + truncedValue); >+ >+ var truncedValue = uselessMathTrunc(-0.6); >+ if (truncedValue !== 0) >+ throw new Error("uselessMathTrunc(-0.6) = " + truncedValue); >+} >+ >+function mathTruncWithOverflow(value) >+{ >+ return Math.trunc(value); >+} >+noInline(mathTruncWithOverflow); >+ >+for (var i = 0; i < 1e4; ++i) { >+ var bigValue = 1000000000000; >+ var truncedValue = mathTruncWithOverflow(bigValue); >+ if (truncedValue !== bigValue) >+ throw new Error("mathTruncWithOverflow(" + bigValue + ") = " + truncedValue); >+} >+ >+function mathTruncConsumedAsDouble(value) >+{ >+ return Math.trunc(value) * 0.5; >+} >+noInline(mathTruncConsumedAsDouble); >+ >+for (var i = 0; i < 1e4; ++i) { >+ var doubleValue = i + 0.1; >+ var truncedValue = mathTruncConsumedAsDouble(doubleValue); >+ if (truncedValue !== (i * 0.5)) >+ throw new Error("mathTruncConsumedAsDouble(" + doubleValue + ") = " + truncedValue); >+ >+ var doubleValue = i + 0.6; >+ var truncedValue = mathTruncConsumedAsDouble(doubleValue); >+ if (truncedValue !== (i * 0.5)) >+ throw new Error("mathTruncConsumedAsDouble(" + doubleValue + ") = " + truncedValue); >+ >+} >+ >+function mathTruncDoesNotCareAboutMinusZero(value) >+{ >+ return Math.trunc(value)|0; >+} >+noInline(mathTruncDoesNotCareAboutMinusZero); >+ >+for (var i = 0; i < 1e4; ++i) { >+ var doubleMid = i + 0.5; >+ var truncedValue = mathTruncDoesNotCareAboutMinusZero(doubleMid); >+ if (truncedValue !== i) >+ throw new Error("mathTruncDoesNotCareAboutMinusZero(" + doubleMid + ") = " + truncedValue); >+} >+ >+ >+// *** Function arguments. *** >+function mathTruncNoArguments() >+{ >+ return Math.trunc(); >+} >+noInline(mathTruncNoArguments); >+ >+function mathTruncTooManyArguments(a, b, c) >+{ >+ return Math.trunc(a, b, c); >+} >+noInline(mathTruncTooManyArguments); >+ >+for (var i = 0; i < 1e4; ++i) { >+ var value = mathTruncNoArguments(); >+ if (value === value) >+ throw new Error("mathTruncNoArguments() = " + value); >+ >+ var value = mathTruncTooManyArguments(2.1, 3, 5); >+ if (value !== 2) >+ throw new Error("mathTruncTooManyArguments() = " + value); >+} >+ >+ >+// *** Constant as arguments. *** >+function testMathTruncOnConstants() >+{ >+ var value = Math.trunc(0); >+ if (value !== 0) >+ throw new Error("Math.trunc(0) = " + value); >+ var value = Math.trunc(-0); >+ if (!(value === 0 && (1/value) === -Infinity)) >+ throw new Error("Math.trunc(-0) = " + value); >+ var value = Math.trunc(1); >+ if (value !== 1) >+ throw new Error("Math.trunc(1) = " + value); >+ var value = Math.trunc(-1); >+ if (value !== -1) >+ throw new Error("Math.trunc(-1) = " + value); >+ var value = Math.trunc(42); >+ if (value !== 42) >+ throw new Error("Math.trunc(42) = " + value); >+ var value = Math.trunc(-42.2); >+ if (value !== -42) >+ throw new Error("Math.trunc(-42.2) = " + value); >+ var value = Math.trunc(NaN); >+ if (value === value) >+ throw new Error("Math.trunc(NaN) = " + value); >+ var value = Math.trunc(Number.POSITIVE_INFINITI); >+ if (value === value) >+ throw new Error("Math.trunc(Number.POSITIVE_INFINITI) = " + value); >+ var value = Math.trunc(Number.NEGATIVE_INFINITI); >+ if (value === value) >+ throw new Error("Math.trunc(Number.NEGATIVE_INFINITI) = " + value); >+ var value = Math.trunc(Math.E); >+ if (value !== 2) >+ throw new Error("Math.trunc(Math.E) = " + value); >+} >+noInline(testMathTruncOnConstants); >+ >+for (var i = 0; i < 1e4; ++i) { >+ testMathTruncOnConstants(); >+} >+ >+ >+// *** Struct transition. *** >+function mathTruncStructTransition(value) >+{ >+ return Math.trunc(value); >+} >+noInline(mathTruncStructTransition); >+ >+for (var i = 0; i < 1e4; ++i) { >+ var value = mathTruncStructTransition(42.5); >+ if (value !== 42) >+ throw new Error("mathTruncStructTransition(42.5) = " + value); >+} >+ >+Math.trunc = function() { return arguments[0] + 5; } >+ >+var value = mathTruncStructTransition(42); >+if (value !== 47) >+ throw new Error("mathTruncStructTransition(42) after transition = " + value); >diff --git a/Source/JavaScriptCore/tests/stress/math-trunc-should-be-truncate.js b/Source/JavaScriptCore/tests/stress/math-trunc-should-be-truncate.js >new file mode 100644 >index 0000000000000000000000000000000000000000..fb9ea9bba83b327a3f2f3b001530a58c080f1fe2 >--- /dev/null >+++ b/Source/JavaScriptCore/tests/stress/math-trunc-should-be-truncate.js >@@ -0,0 +1,12 @@ >+function mathTrunc(value) >+{ >+ return Math.trunc(value); >+} >+noInline(mathTrunc); >+ >+for (var i = 0; i < 1e4; ++i) { >+ var doubleMid = -9901 - 0.6; >+ var roundedValue = mathTrunc(doubleMid); >+ if (roundedValue !== -9901) >+ throw "mathRoundDoesNotCareAboutMinusZero(" + doubleMid + ") = " + roundedValue; >+} >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index 591b75dde21bfde7c4344a6d84481d2fa94b855b..fe995ecf76864b9ccf4a64d90fff39c1fc58e11e 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,21 @@ >+2016-03-31 Yusuke Suzuki <utatane.tea@gmail.com> >+ >+ [JSC] Add truncate operation (rounding to zero) >+ https://bugs.webkit.org/show_bug.cgi?id=156072 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * js/regress/many-foreach-calls-expected.txt: Added. >+ * js/regress/many-foreach-calls.html: Added. >+ * js/regress/math-trunc-expected.txt: Added. >+ * js/regress/math-trunc.html: Added. >+ * js/regress/script-tests/many-foreach-calls.js: Added. >+ (i.array.forEach): >+ * js/regress/script-tests/math-trunc.js: Added. >+ (mathTruncInt): >+ (mathTruncDouble): >+ (mathTruncMixed): >+ > 2016-03-31 Chris Dumez <cdumez@apple.com> > > REGRESSION (r191180): Safari does not send Referer Header to iframe src in certain situations >diff --git a/LayoutTests/js/regress/many-foreach-calls-expected.txt b/LayoutTests/js/regress/many-foreach-calls-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..7d0a4f7d4b40baac93f7987c6a400b074ec7b06a >--- /dev/null >+++ b/LayoutTests/js/regress/many-foreach-calls-expected.txt >@@ -0,0 +1,10 @@ >+JSRegress/many-foreach-calls >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+PASS no exception thrown >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >diff --git a/LayoutTests/js/regress/many-foreach-calls.html b/LayoutTests/js/regress/many-foreach-calls.html >new file mode 100644 >index 0000000000000000000000000000000000000000..bbe58506fe7c4051ca7766edbc7dca62a190bfd5 >--- /dev/null >+++ b/LayoutTests/js/regress/many-foreach-calls.html >@@ -0,0 +1,12 @@ >+<!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/many-foreach-calls.js"></script> >+<script src="../../resources/regress-post.js"></script> >+<script src="../../resources/js-test-post.js"></script> >+</body> >+</html> >diff --git a/LayoutTests/js/regress/math-trunc-expected.txt b/LayoutTests/js/regress/math-trunc-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..3b028e213cf56f16ab0c1e3692ca54f7859fb09a >--- /dev/null >+++ b/LayoutTests/js/regress/math-trunc-expected.txt >@@ -0,0 +1,10 @@ >+JSRegress/math-trunc >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+PASS no exception thrown >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >diff --git a/LayoutTests/js/regress/math-trunc.html b/LayoutTests/js/regress/math-trunc.html >new file mode 100644 >index 0000000000000000000000000000000000000000..7859fa7dd3ce7477a1c6cbfbb796dc29acb3f4a3 >--- /dev/null >+++ b/LayoutTests/js/regress/math-trunc.html >@@ -0,0 +1,12 @@ >+<!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/math-trunc.js"></script> >+<script src="../../resources/regress-post.js"></script> >+<script src="../../resources/js-test-post.js"></script> >+</body> >+</html> >diff --git a/LayoutTests/js/regress/script-tests/many-foreach-calls.js b/LayoutTests/js/regress/script-tests/many-foreach-calls.js >new file mode 100644 >index 0000000000000000000000000000000000000000..d2b38258791f4112a2299a5b52d0019bfa1d276c >--- /dev/null >+++ b/LayoutTests/js/regress/script-tests/many-foreach-calls.js >@@ -0,0 +1,7 @@ >+var sum = 0; >+var array = [1, 2, 3]; >+for (var i = 0; i < 1e5; ++i) { >+ array.forEach(function (value) { >+ sum += value; >+ }); >+} >diff --git a/LayoutTests/js/regress/script-tests/math-trunc.js b/LayoutTests/js/regress/script-tests/math-trunc.js >new file mode 100644 >index 0000000000000000000000000000000000000000..4791c5c1ea0eb3998f5c314290effff675854894 >--- /dev/null >+++ b/LayoutTests/js/regress/script-tests/math-trunc.js >@@ -0,0 +1,30 @@ >+function mathTruncInt(i) >+{ >+ return Math.trunc(i); >+} >+noInline(mathTruncInt); >+ >+for (var i = 0; i < 1e5; ++i) >+ mathTruncInt(i); >+ >+function mathTruncDouble(i) >+{ >+ return Math.trunc(i); >+} >+noInline(mathTruncDouble); >+ >+for (var i = 0; i < 1e5; ++i) >+ mathTruncDouble(i * 0.6); >+ >+function mathTruncMixed(i) >+{ >+ return Math.trunc(i); >+} >+noInline(mathTruncMixed); >+ >+for (var i = 0; i < 1e5; ++i) { >+ if (i % 2 === 0) >+ mathTruncDouble(i * 0.6); >+ else >+ mathTruncDouble(i); >+}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 156072
:
275331
|
275347
|
275394
|
275462