+2016-09-06 Benjamin Poulain <bpoulain@apple.com>
+
+ [JSC] Make ArithClz32 work with Cell arguments
+ https://bugs.webkit.org/show_bug.cgi?id=161369
+
+ Reviewed by Geoffrey Garen.
+
+ * stress/arith-clz32-on-various-types.js: Added.
+
2016-09-06 Commit Queue <commit-queue@webkit.org>
Unreviewed, rolling out r205504.
--- /dev/null
+"use strict";
+
+let validInputTestCases = [
+ // input as string, expected result as string.
+ ["undefined", "32"],
+ ["null", "32"],
+ ["0", "32"],
+ ["-0.", "32"],
+ ["4", "29"],
+ ["Math.PI", "30"],
+ ["Infinity", "32"],
+ ["-Infinity", "32"],
+ ["NaN", "32"],
+ ["\"WebKit\"", "32"],
+ ["\"4\"", "29"],
+ ["{ valueOf: () => { return 4; } }", "29"],
+];
+
+let validInputTypedTestCases = validInputTestCases.map((element) => { return [eval("(" + element[0] + ")"), eval(element[1])] });
+
+function isIdentical(result, expected)
+{
+ if (expected === expected) {
+ if (result !== expected)
+ return false;
+ if (!expected && 1 / expected === -Infinity && 1 / result !== -Infinity)
+ return false;
+
+ return true;
+ }
+ return result !== result;
+}
+
+
+// Test Math.clz32() without arguments.
+function opaqueClz32NoArgument() {
+ return Math.clz32();
+}
+noInline(opaqueClz32NoArgument);
+noOSRExitFuzzing(opaqueClz32NoArgument);
+
+function testNoArgument() {
+ for (let i = 0; i < 1e4; ++i) {
+ let output = opaqueClz32NoArgument();
+ if (output !== 32) {
+ throw "Failed opaqueClz32NoArgument";
+ }
+ }
+ if (numberOfDFGCompiles(opaqueClz32NoArgument) > 1)
+ throw "The call without arguments should never exit.";
+}
+testNoArgument();
+
+
+// Test Math.clz32() with a very polymorphic input. All test cases are seen at each iteration.
+function opaqueAllTypesClz32(argument) {
+ return Math.clz32(argument);
+}
+noInline(opaqueAllTypesClz32);
+noOSRExitFuzzing(opaqueAllTypesClz32);
+
+function testAllTypesCall() {
+ for (let i = 0; i < 1e3; ++i) {
+ for (let testCaseInput of validInputTypedTestCases) {
+ let output = opaqueAllTypesClz32(testCaseInput[0]);
+ if (!isIdentical(output, testCaseInput[1]))
+ throw "Failed testAllTypesCall for input " + testCaseInput[0] + " expected " + testCaseInput[1] + " got " + output;
+ }
+ }
+ if (numberOfDFGCompiles(opaqueAllTypesClz32) > 2)
+ throw "We should have detected clz32() was polymorphic and generated a generic version.";
+}
+testAllTypesCall();
+
+
+// Test Math.clz32() on a completely typed input. Every call see only one type.
+function testSingleTypeCall() {
+ for (let testCaseInput of validInputTestCases) {
+ eval(`
+ function opaqueClz32(argument) {
+ return Math.clz32(argument);
+ }
+ noInline(opaqueClz32);
+ noOSRExitFuzzing(opaqueClz32);
+
+ for (let i = 0; i < 1e4; ++i) {
+ if (!isIdentical(opaqueClz32(${testCaseInput[0]}), ${testCaseInput[1]})) {
+ throw "Failed testSingleTypeCall()";
+ }
+ }
+ if (numberOfDFGCompiles(opaqueClz32) > 1)
+ throw "We should have compiled a single clz32 for the expected type.";
+ `);
+ }
+}
+testSingleTypeCall();
+
+
+// Test Math.clz32() on constants
+function testConstant() {
+ for (let testCaseInput of validInputTestCases) {
+ eval(`
+ function opaqueClz32OnConstant() {
+ return Math.clz32(${testCaseInput[0]});
+ }
+ noInline(opaqueClz32OnConstant);
+ noOSRExitFuzzing(opaqueClz32OnConstant);
+
+ for (let i = 0; i < 1e4; ++i) {
+ if (!isIdentical(opaqueClz32OnConstant(), ${testCaseInput[1]})) {
+ throw "Failed testConstant()";
+ }
+ }
+ if (numberOfDFGCompiles(opaqueClz32OnConstant) > 1)
+ throw "We should have compiled a single clz32 for the expected type.";
+ `);
+ }
+}
+testConstant();
+
+
+// Verify we call valueOf() exactly once per call.
+function opaqueClz32ForSideEffects(argument) {
+ return Math.clz32(argument);
+}
+noInline(opaqueClz32ForSideEffects);
+noOSRExitFuzzing(opaqueClz32ForSideEffects);
+
+function testSideEffect() {
+ let testObject = {
+ counter: 0,
+ valueOf: function() { ++this.counter; return 16; }
+ };
+ let clz3216 = Math.clz32(16);
+ for (let i = 0; i < 1e4; ++i) {
+ if (opaqueClz32ForSideEffects(testObject) !== clz3216)
+ throw "Incorrect result in testSideEffect()";
+ }
+ if (testObject.counter !== 1e4)
+ throw "Failed testSideEffect()";
+ if (numberOfDFGCompiles(opaqueClz32ForSideEffects) > 1)
+ throw "opaqueClz32ForSideEffects() is predictable, it should only be compiled once.";
+}
+testSideEffect();
+
+
+// Verify clz32() is not subject to CSE if the argument has side effects.
+function opaqueClz32ForCSE(argument) {
+ return Math.clz32(argument) + Math.clz32(argument) + Math.clz32(argument);
+}
+noInline(opaqueClz32ForCSE);
+noOSRExitFuzzing(opaqueClz32ForCSE);
+
+function testCSE() {
+ let testObject = {
+ counter: 0,
+ valueOf: function() { ++this.counter; return 16; }
+ };
+ let clz3216 = Math.clz32(16);
+ let threeClz3216 = clz3216 + clz3216 + clz3216;
+ for (let i = 0; i < 1e4; ++i) {
+ if (opaqueClz32ForCSE(testObject) !== threeClz3216)
+ throw "Incorrect result in testCSE()";
+ }
+ if (testObject.counter !== 3e4)
+ throw "Failed testCSE()";
+ if (numberOfDFGCompiles(opaqueClz32ForCSE) > 1)
+ throw "opaqueClz32ForCSE() is predictable, it should only be compiled once.";
+}
+testCSE();
+
+
+// Verify clz32() is not subject to DCE if the argument has side effects.
+function opaqueClz32ForDCE(argument) {
+ Math.clz32(argument);
+}
+noInline(opaqueClz32ForDCE);
+noOSRExitFuzzing(opaqueClz32ForDCE);
+
+function testDCE() {
+ let testObject = {
+ counter: 0,
+ valueOf: function() { ++this.counter; return 16; }
+ };
+ for (let i = 0; i < 1e4; ++i) {
+ opaqueClz32ForDCE(testObject);
+ }
+ if (testObject.counter !== 1e4)
+ throw "Failed testDCE()";
+ if (numberOfDFGCompiles(opaqueClz32ForDCE) > 1)
+ throw "opaqueClz32ForDCE() is predictable, it should only be compiled once.";
+}
+testDCE();
+
+
+// Test exceptions in the argument.
+function testException() {
+ let counter = 0;
+ function opaqueClz32WithException(argument) {
+ let result = Math.clz32(argument);
+ ++counter;
+ return result;
+ }
+ noInline(opaqueClz32WithException);
+
+ let testObject = { valueOf: () => { return 64; } };
+ let clz3264 = Math.clz32(64);
+
+ // Warm up without exception.
+ for (let i = 0; i < 1e3; ++i) {
+ if (opaqueClz32WithException(testObject) !== clz3264)
+ throw "Incorrect result in opaqueClz32WithException()";
+ }
+
+ let testThrowObject = { valueOf: () => { throw testObject; return 64; } };
+
+ for (let i = 0; i < 1e2; ++i) {
+ try {
+ if (opaqueClz32WithException(testThrowObject) !== 8)
+ throw "This code should not be reached!!";
+ } catch (e) {
+ if (e !== testObject) {
+ throw "Wrong object thrown from opaqueClz32WithException."
+ }
+ }
+ }
+
+ if (counter !== 1e3) {
+ throw "Invalid count in testException()";
+ }
+}
+testException();
+2016-09-06 Benjamin Poulain <bpoulain@apple.com>
+
+ [JSC] Make ArithClz32 work with Cell arguments
+ https://bugs.webkit.org/show_bug.cgi?id=161369
+
+ Reviewed by Geoffrey Garen.
+
+ ArithClz32 was already working with all primitive types
+ thanks to the magic of ValueToInt32.
+ This patch adds support for cell arguments through a function
+ call.
+
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGOperations.cpp:
+ * dfg/DFGOperations.h:
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileArithClz32):
+ * dfg/DFGSpeculativeJIT.h:
+ (JSC::DFG::SpeculativeJIT::callOperation):
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::compileArithClz32):
+
2016-09-06 Mark Lam <mark.lam@apple.com>
Gardening: change to use old header guard to appease Win EWS.
case ArithClz32: {
JSValue operand = forNode(node->child1()).value();
- if (operand && operand.isNumber()) {
- uint32_t value = toUInt32(operand.asNumber());
+ if (Optional<double> number = operand.toNumberFromPrimitive()) {
+ uint32_t value = toUInt32(*number);
setConstant(node, jsNumber(clz32(value)));
break;
}
return;
case ArithIMul:
- case ArithClz32:
case ArithMin:
case ArithMax:
case ArithPow:
}
return;
+ case ArithClz32:
+ if (node->child1().useKind() == Int32Use || node->child1().useKind() == KnownInt32Use)
+ def(PureValue(node));
+ else {
+ read(World);
+ write(Heap);
+ }
+ return;
+
case BitAnd:
case BitOr:
case BitXor:
}
case ArithClz32: {
- fixIntConvertingEdge(node->child1());
- node->setArithMode(Arith::Unchecked);
+ if (node->child1()->shouldSpeculateNotCell()) {
+ fixIntConvertingEdge(node->child1());
+ node->clearFlags(NodeMustGenerate);
+ } else
+ fixEdge<UntypedUse>(node->child1());
break;
}
/* this is only true if we do the overflow check - hence the need to keep it alive. More */\
/* generally, we need to keep alive any operation whose checks cause filtration in AI. */\
macro(ArithAdd, NodeResultNumber | NodeMustGenerate) \
- macro(ArithClz32, NodeResultInt32) \
+ macro(ArithClz32, NodeResultInt32 | NodeMustGenerate) \
macro(ArithSub, NodeResultNumber | NodeMustGenerate) \
macro(ArithNegate, NodeResultNumber | NodeMustGenerate) \
macro(ArithMul, NodeResultNumber | NodeMustGenerate) \
JSValue op1 = JSValue::decode(encodedOp1);
double a = op1.toNumber(exec);
if (UNLIKELY(vm->exception()))
- return JSValue::encode(JSValue());
+ return PNaN;
return fabs(a);
}
+int32_t JIT_OPERATION operationArithClz32(ExecState* exec, EncodedJSValue encodedOp1)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ JSValue op1 = JSValue::decode(encodedOp1);
+ uint32_t value = op1.toUInt32(exec);
+ if (UNLIKELY(vm->exception()))
+ return 0;
+ return clz32(value);
+}
+
double JIT_OPERATION operationArithCos(ExecState* exec, EncodedJSValue encodedOp1)
{
VM* vm = &exec->vm();
JSValue op1 = JSValue::decode(encodedOp1);
double a = op1.toNumber(exec);
if (UNLIKELY(vm->exception()))
- return JSValue::encode(JSValue());
+ return PNaN;
return static_cast<float>(a);
}
JSValue op1 = JSValue::decode(encodedOp1);
double a = op1.toNumber(exec);
if (UNLIKELY(vm->exception()))
- return JSValue::encode(JSValue());
+ return PNaN;
return log(a);
}
JSValue op1 = JSValue::decode(encodedOp1);
double a = op1.toNumber(exec);
if (UNLIKELY(vm->exception()))
- return JSValue::encode(JSValue());
+ return PNaN;
return sin(a);
}
JSValue op1 = JSValue::decode(encodedOp1);
double a = op1.toNumber(exec);
if (UNLIKELY(vm->exception()))
- return JSValue::encode(JSValue());
+ return PNaN;
return sqrt(a);
}
EncodedJSValue JIT_OPERATION operationValueAddNotNumber(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationValueDiv(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
double JIT_OPERATION operationArithAbs(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
+int32_t JIT_OPERATION operationArithClz32(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
double JIT_OPERATION operationArithCos(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
double JIT_OPERATION operationArithFRound(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
double JIT_OPERATION operationArithLog(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
void SpeculativeJIT::compileArithClz32(Node* node)
{
- ASSERT_WITH_MESSAGE(node->child1().useKind() == Int32Use || node->child1().useKind() == KnownInt32Use, "The Fixup phase should have enforced a Int32 operand.");
- SpeculateInt32Operand value(this, node->child1());
- GPRTemporary result(this, Reuse, value);
- GPRReg valueReg = value.gpr();
+ if (node->child1().useKind() == Int32Use || node->child1().useKind() == KnownInt32Use) {
+ SpeculateInt32Operand value(this, node->child1());
+ GPRTemporary result(this, Reuse, value);
+ GPRReg valueReg = value.gpr();
+ GPRReg resultReg = result.gpr();
+ m_jit.countLeadingZeros32(valueReg, resultReg);
+ int32Result(resultReg, node);
+ return;
+ }
+ JSValueOperand op1(this, node->child1());
+ JSValueRegs op1Regs = op1.jsValueRegs();
+ GPRTemporary result(this);
GPRReg resultReg = result.gpr();
- m_jit.countLeadingZeros32(valueReg, resultReg);
+ flushRegisters();
+ callOperation(operationArithClz32, resultReg, op1Regs);
+ m_jit.exceptionCheck();
int32Result(resultReg, node);
}
return appendCall(operation);
}
+ JITCompiler::Call callOperation(Z_JITOperation_EJ operation, GPRReg result, JSValueRegs arg1)
+ {
+ m_jit.setupArgumentsWithExecState(arg1.payloadGPR());
+ return appendCallSetResult(operation, result);
+ }
+
JITCompiler::Call callOperation(Z_JITOperation_EJZZ operation, GPRReg result, GPRReg arg1, unsigned arg2, unsigned arg3)
{
m_jit.setupArgumentsWithExecState(arg1, TrustedImm32(arg2), TrustedImm32(arg3));
m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1.payloadGPR(), arg1.tagGPR(), arg2, EABI_32BIT_DUMMY_ARG arg3.payloadGPR(), arg3.tagGPR());
return appendCallSetResult(operation, result);
}
-
+ JITCompiler::Call callOperation(Z_JITOperation_EJ operation, GPRReg result, JSValueRegs arg1)
+ {
+ m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1.payloadGPR(), arg1.tagGPR());
+ return appendCallSetResult(operation, result);
+ }
JITCompiler::Call callOperation(Z_JITOperation_EJZZ operation, GPRReg result, JSValueRegs arg1, unsigned arg2, unsigned arg3)
{
m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1.payloadGPR(), arg1.tagGPR(), TrustedImm32(arg2), TrustedImm32(arg3));
void compileArithClz32()
{
- LValue operand = lowInt32(m_node->child1());
- setInt32(m_out.ctlz32(operand));
+ if (m_node->child1().useKind() == Int32Use || m_node->child1().useKind() == KnownInt32Use) {
+ LValue operand = lowInt32(m_node->child1());
+ setInt32(m_out.ctlz32(operand));
+ return;
+ }
+ DFG_ASSERT(m_graph, m_node, m_node->child1().useKind() == UntypedUse);
+ LValue argument = lowJSValue(m_node->child1());
+ LValue result = vmCall(Int32, m_out.operation(operationArithClz32), m_callFrame, argument);
+ setInt32(result);
}
void compileArithMul()