#include "DFGSaneStringGetByValSlowPathGenerator.h"
#include "DFGSlowPathGenerator.h"
#include "DirectArguments.h"
+#include "JITAddGenerator.h"
+#include "JITSubGenerator.h"
#include "JSArrowFunction.h"
#include "JSCInlines.h"
#include "JSEnvironmentRecord.h"
const ClassInfo* expectedClassInfo = 0;
switch (node->arrayMode().type()) {
+ case Array::AnyTypedArray:
case Array::String:
RELEASE_ASSERT_NOT_REACHED(); // Should have been a Phantom(String:)
break;
stubInfo->patch.valueGPR = static_cast<int8_t>(resultGPR);
#if USE(JSVALUE32_64)
stubInfo->patch.valueTagGPR = static_cast<int8_t>(InvalidGPRReg);
+ stubInfo->patch.baseTagGPR = static_cast<int8_t>(InvalidGPRReg);
#endif
stubInfo->patch.usedRegisters = usedRegisters();
done.append(isNull);
DFG_TYPE_CHECK(JSValueRegs(op1GPR), node->child1(), ~SpecCell,
- m_jit.branchTest64(JITCompiler::NonZero, op1GPR, TrustedImm32(static_cast<int32_t>(~1))));
+ m_jit.branchTest64(JITCompiler::Zero, op1GPR, TrustedImm32(static_cast<int32_t>(TagBitBool))));
JITCompiler::Jump isFalse = m_jit.branch64(JITCompiler::Equal, op1GPR, TrustedImm64(ValueFalse));
static const double one = 1;
m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&one), resultFPR);
+ done.append(m_jit.jump());
done.append(isFalse);
isUndefined.link(&m_jit);
JITCompiler::Jump isFalse = m_jit.branchTest32(JITCompiler::Zero, op1PayloadGPR, TrustedImm32(1));
static const double one = 1;
m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&one), resultFPR);
+ done.append(m_jit.jump());
done.append(isFalse);
isUndefined.link(&m_jit);
blessedBooleanResult(scratchReg, node);
}
-void SpeculativeJIT::compileAdd(Node* node)
+void SpeculativeJIT::compileValueAdd(Node* node)
+{
+ if (isKnownNotNumber(node->child1().node()) || isKnownNotNumber(node->child2().node())) {
+ JSValueOperand left(this, node->child1());
+ JSValueOperand right(this, node->child2());
+ JSValueRegs leftRegs = left.jsValueRegs();
+ JSValueRegs rightRegs = right.jsValueRegs();
+#if USE(JSVALUE64)
+ GPRTemporary result(this);
+ JSValueRegs resultRegs = JSValueRegs(result.gpr());
+#else
+ GPRTemporary resultTag(this);
+ GPRTemporary resultPayload(this);
+ JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr());
+#endif
+ flushRegisters();
+ callOperation(operationValueAddNotNumber, resultRegs, leftRegs, rightRegs);
+ m_jit.exceptionCheck();
+
+ jsValueResult(resultRegs, node);
+ return;
+ }
+
+ bool leftIsConstInt32 = node->child1()->isInt32Constant();
+ bool rightIsConstInt32 = node->child2()->isInt32Constant();
+
+ // The DFG does not always fold the sum of 2 constant int operands together.
+ if (leftIsConstInt32 && rightIsConstInt32) {
+#if USE(JSVALUE64)
+ GPRTemporary result(this);
+ JSValueRegs resultRegs = JSValueRegs(result.gpr());
+#else
+ GPRTemporary resultTag(this);
+ GPRTemporary resultPayload(this);
+ JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr());
+#endif
+ int64_t leftConst = node->child1()->asInt32();
+ int64_t rightConst = node->child2()->asInt32();
+ int64_t resultConst = leftConst + rightConst;
+ m_jit.moveValue(JSValue(resultConst), resultRegs);
+ jsValueResult(resultRegs, node);
+ return;
+ }
+
+ Optional<JSValueOperand> left;
+ Optional<JSValueOperand> right;
+
+ JSValueRegs leftRegs;
+ JSValueRegs rightRegs;
+
+ FPRTemporary leftNumber(this);
+ FPRTemporary rightNumber(this);
+ FPRReg leftFPR = leftNumber.fpr();
+ FPRReg rightFPR = rightNumber.fpr();
+
+#if USE(JSVALUE64)
+ GPRTemporary result(this);
+ JSValueRegs resultRegs = JSValueRegs(result.gpr());
+ GPRTemporary scratch(this);
+ GPRReg scratchGPR = scratch.gpr();
+ FPRReg scratchFPR = InvalidFPRReg;
+#else
+ GPRTemporary resultTag(this);
+ GPRTemporary resultPayload(this);
+ JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr());
+ GPRReg scratchGPR = resultTag.gpr();
+ FPRTemporary fprScratch(this);
+ FPRReg scratchFPR = fprScratch.fpr();
+#endif
+
+ ResultType leftType = m_state.forNode(node->child1()).resultType();
+ ResultType rightType = m_state.forNode(node->child2()).resultType();
+ int32_t leftConstInt32 = 0;
+ int32_t rightConstInt32 = 0;
+
+ ASSERT(!leftIsConstInt32 || !rightIsConstInt32);
+
+ if (leftIsConstInt32) {
+ leftConstInt32 = node->child1()->asInt32();
+ right = JSValueOperand(this, node->child2());
+ rightRegs = right->jsValueRegs();
+ } else if (rightIsConstInt32) {
+ left = JSValueOperand(this, node->child1());
+ leftRegs = left->jsValueRegs();
+ rightConstInt32 = node->child2()->asInt32();
+ } else {
+ left = JSValueOperand(this, node->child1());
+ leftRegs = left->jsValueRegs();
+ right = JSValueOperand(this, node->child2());
+ rightRegs = right->jsValueRegs();
+ }
+
+ JITAddGenerator gen(resultRegs, leftRegs, rightRegs, leftType, rightType,
+ leftIsConstInt32, rightIsConstInt32, leftConstInt32, rightConstInt32,
+ leftFPR, rightFPR, scratchGPR, scratchFPR);
+ gen.generateFastPath(m_jit);
+
+ ASSERT(gen.didEmitFastPath());
+ gen.endJumpList().append(m_jit.jump());
+
+ gen.slowPathJumpList().link(&m_jit);
+
+ silentSpillAllRegisters(resultRegs);
+
+ if (leftIsConstInt32) {
+ leftRegs = resultRegs;
+ int64_t leftConst = node->child1()->asInt32();
+ m_jit.moveValue(JSValue(leftConst), leftRegs);
+ } else if (rightIsConstInt32) {
+ rightRegs = resultRegs;
+ int64_t rightConst = node->child2()->asInt32();
+ m_jit.moveValue(JSValue(rightConst), rightRegs);
+ }
+
+ callOperation(operationValueAdd, resultRegs, leftRegs, rightRegs);
+
+ silentFillAllRegisters(resultRegs);
+ m_jit.exceptionCheck();
+
+ gen.endJumpList().link(&m_jit);
+ jsValueResult(resultRegs, node);
+ return;
+}
+
+void SpeculativeJIT::compileArithAdd(Node* node)
{
switch (node->binaryUseKind()) {
case Int32Use: {
doubleResult(result.fpr(), node);
return;
}
-
+
+ case UntypedUse: {
+ JSValueOperand left(this, node->child1());
+ JSValueOperand right(this, node->child2());
+
+ JSValueRegs leftRegs = left.jsValueRegs();
+ JSValueRegs rightRegs = right.jsValueRegs();
+
+ ResultType leftType = m_state.forNode(node->child1()).resultType();
+ ResultType rightType = m_state.forNode(node->child2()).resultType();
+
+ FPRTemporary leftNumber(this);
+ FPRTemporary rightNumber(this);
+ FPRReg leftFPR = leftNumber.fpr();
+ FPRReg rightFPR = rightNumber.fpr();
+
+#if USE(JSVALUE64)
+ GPRTemporary result(this);
+ JSValueRegs resultRegs = JSValueRegs(result.gpr());
+ GPRTemporary scratch(this);
+ GPRReg scratchGPR = scratch.gpr();
+ FPRReg scratchFPR = InvalidFPRReg;
+#else
+ GPRTemporary resultTag(this);
+ GPRTemporary resultPayload(this);
+ JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr());
+ GPRReg scratchGPR = resultTag.gpr();
+ FPRTemporary fprScratch(this);
+ FPRReg scratchFPR = fprScratch.fpr();
+#endif
+
+ JITSubGenerator gen(resultRegs, leftRegs, rightRegs, leftType, rightType,
+ leftFPR, rightFPR, scratchGPR, scratchFPR);
+ gen.generateFastPath(m_jit);
+
+ ASSERT(gen.didEmitFastPath());
+ gen.endJumpList().append(m_jit.jump());
+
+ gen.slowPathJumpList().link(&m_jit);
+ silentSpillAllRegisters(resultRegs);
+ callOperation(operationValueSub, resultRegs, leftRegs, rightRegs);
+ silentFillAllRegisters(resultRegs);
+ m_jit.exceptionCheck();
+
+ gen.endJumpList().link(&m_jit);
+ jsValueResult(resultRegs, node);
+ return;
+ }
+
default:
RELEASE_ASSERT_NOT_REACHED();
return;
}
m_jit.move(op1GPR, eax.gpr());
- m_jit.assembler().cdq();
- m_jit.assembler().idivl_r(op2GPR);
+ m_jit.x86ConvertToDoubleWord32();
+ m_jit.x86Div32(op2GPR);
if (op2TempGPR != InvalidGPRReg)
unlock(op2TempGPR);
numeratorNonZero.link(&m_jit);
}
+ if (shouldCheckOverflow(node->arithMode()))
+ speculationCheck(Overflow, JSValueRegs(), nullptr, m_jit.branchTest32(MacroAssembler::Zero, op2GPR));
+
m_jit.assembler().sdiv<32>(quotient.gpr(), op1GPR, op2GPR);
// Check that there was no remainder. If there had been, then we'd be obligated to
if (node->child2()->isInt32Constant()) {
int32_t divisor = node->child2()->asInt32();
if (divisor > 1 && hasOneBitSet(divisor)) {
- unsigned logarithm = WTF::fastLog2(divisor);
+ unsigned logarithm = WTF::fastLog2(static_cast<uint32_t>(divisor));
GPRReg dividendGPR = op1.gpr();
GPRTemporary result(this);
GPRReg resultGPR = result.gpr();
m_jit.move(op1Gpr, eax.gpr());
m_jit.move(TrustedImm32(divisor), scratchGPR);
- m_jit.assembler().cdq();
- m_jit.assembler().idivl_r(scratchGPR);
+ m_jit.x86ConvertToDoubleWord32();
+ m_jit.x86Div32(scratchGPR);
if (shouldCheckNegativeZero(node->arithMode())) {
JITCompiler::Jump numeratorPositive = m_jit.branch32(JITCompiler::GreaterThanOrEqual, op1SaveGPR, TrustedImm32(0));
speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branchTest32(JITCompiler::Zero, edx.gpr()));
}
m_jit.move(op1GPR, eax.gpr());
- m_jit.assembler().cdq();
- m_jit.assembler().idivl_r(op2GPR);
+ m_jit.x86ConvertToDoubleWord32();
+ m_jit.x86Div32(op2GPR);
if (op2TempGPR != InvalidGPRReg)
unlock(op2TempGPR);
SpeculateDoubleOperand op1(this, node->child1());
FPRReg op1FPR = op1.fpr();
- if (!MacroAssembler::supportsFloatingPointSqrt() || !Options::enableArchitectureSpecificOptimizations()) {
+ if (!MacroAssembler::supportsFloatingPointSqrt() || !Options::useArchitectureSpecificOptimizations()) {
flushRegisters();
FPRResult result(this);
callOperation(sqrt, result.fpr(), op1FPR);
default:
ASSERT(isTypedView(node->arrayMode().typedArrayType()));
- m_jit.loadPtr(
- MacroAssembler::Address(baseReg, JSArrayBufferView::offsetOfVector()),
- storageReg);
+
+ JITCompiler::Jump fail = m_jit.loadTypedArrayVector(baseReg, storageReg);
+
+ addSlowPathGenerator(
+ slowPathCall(fail, this, operationGetArrayBufferVector, storageReg, baseReg));
break;
}
MacroAssembler::NotEqual,
MacroAssembler::Address(baseGPR, JSArrayBufferView::offsetOfMode()),
TrustedImm32(WastefulTypedArray));
-
+
m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()), dataGPR);
+ m_jit.removeSpaceBits(dataGPR);
m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSArrayBufferView::offsetOfVector()), vectorGPR);
+ JITCompiler::JumpList vectorReady;
+ vectorReady.append(m_jit.branchIfToSpace(vectorGPR));
+ vectorReady.append(m_jit.branchIfNotFastTypedArray(baseGPR));
+ m_jit.removeSpaceBits(vectorGPR);
+ vectorReady.link(&m_jit);
m_jit.loadPtr(MacroAssembler::Address(dataGPR, Butterfly::offsetOfArrayBuffer()), dataGPR);
m_jit.loadPtr(MacroAssembler::Address(dataGPR, ArrayBuffer::offsetOfData()), dataGPR);
m_jit.subPtr(dataGPR, vectorGPR);
m_jit.move(TrustedImmPtr(0), vectorGPR);
done.link(&m_jit);
-
+
int32Result(vectorGPR, node);
}
break;
}
default: {
- ASSERT(isTypedView(node->arrayMode().typedArrayType()));
+ ASSERT(node->arrayMode().isSomeTypedArrayView());
SpeculateCellOperand base(this, node->child1());
GPRTemporary result(this, Reuse, base);
GPRReg baseGPR = base.gpr();
cellResult(resultGPR, node);
}
+void SpeculativeJIT::compileCopyRest(Node* node)
+{
+ ASSERT(node->op() == CopyRest);
+
+ SpeculateCellOperand array(this, node->child1());
+ GPRTemporary argumentsStart(this);
+ SpeculateStrictInt32Operand arrayLength(this, node->child2());
+
+ GPRReg arrayGPR = array.gpr();
+ GPRReg argumentsStartGPR = argumentsStart.gpr();
+ GPRReg arrayLengthGPR = arrayLength.gpr();
+
+ CCallHelpers::Jump done = m_jit.branch32(MacroAssembler::Equal, arrayLengthGPR, TrustedImm32(0));
+
+ emitGetArgumentStart(node->origin.semantic, argumentsStartGPR);
+ silentSpillAllRegisters(argumentsStartGPR);
+ // Arguments: 0:exec, 1:JSCell* array, 2:arguments start, 3:number of arguments to skip, 4:array length
+ callOperation(operationCopyRest, arrayGPR, argumentsStartGPR, Imm32(node->numberOfArgumentsToSkip()), arrayLengthGPR);
+ silentFillAllRegisters(argumentsStartGPR);
+ m_jit.exceptionCheck();
+
+ done.link(&m_jit);
+
+ noResult(node);
+}
+
+void SpeculativeJIT::compileGetRestLength(Node* node)
+{
+ ASSERT(node->op() == GetRestLength);
+
+ GPRTemporary result(this);
+ GPRReg resultGPR = result.gpr();
+
+ emitGetLength(node->origin.semantic, resultGPR);
+ CCallHelpers::Jump hasNonZeroLength = m_jit.branch32(MacroAssembler::Above, resultGPR, Imm32(node->numberOfArgumentsToSkip()));
+ m_jit.move(TrustedImm32(0), resultGPR);
+ CCallHelpers::Jump done = m_jit.jump();
+ hasNonZeroLength.link(&m_jit);
+ if (node->numberOfArgumentsToSkip())
+ m_jit.sub32(TrustedImm32(node->numberOfArgumentsToSkip()), resultGPR);
+ done.link(&m_jit);
+ int32Result(resultGPR, node);
+}
+
void SpeculativeJIT::compileNotifyWrite(Node* node)
{
WatchpointSet* set = node->watchpointSet();
storageResult(scratchGPR1, node);
}
+void SpeculativeJIT::compileGetButterfly(Node* node)
+{
+ SpeculateCellOperand base(this, node->child1());
+ GPRTemporary result(this, Reuse, base);
+
+ GPRReg baseGPR = base.gpr();
+ GPRReg resultGPR = result.gpr();
+
+ m_jit.loadPtr(JITCompiler::Address(baseGPR, JSObject::butterflyOffset()), resultGPR);
+
+ switch (node->op()) {
+ case GetButterfly:
+ addSlowPathGenerator(
+ slowPathCall(
+ m_jit.branchIfNotToSpace(resultGPR),
+ this, operationGetButterfly, resultGPR, baseGPR));
+ break;
+
+ case GetButterflyReadOnly:
+ m_jit.removeSpaceBits(resultGPR);
+ break;
+
+ default:
+ DFG_CRASH(m_jit.graph(), node, "Bad node type");
+ break;
+ }
+
+ storageResult(resultGPR, node);
+}
+
GPRReg SpeculativeJIT::temporaryRegisterForPutByVal(GPRTemporary& temporary, ArrayMode arrayMode)
{
if (!putByValWillNeedExtraRegister(arrayMode))
ownerIsRememberedOrInEden.link(&m_jit);
}
+void SpeculativeJIT::compilePutAccessorById(Node* node)
+{
+ SpeculateCellOperand base(this, node->child1());
+ SpeculateCellOperand accessor(this, node->child2());
+
+ GPRReg baseGPR = base.gpr();
+ GPRReg accessorGPR = accessor.gpr();
+
+ flushRegisters();
+ callOperation(node->op() == PutGetterById ? operationPutGetterById : operationPutSetterById, NoResult, baseGPR, identifierUID(node->identifierNumber()), node->accessorAttributes(), accessorGPR);
+ m_jit.exceptionCheck();
+
+ noResult(node);
+}
+
+void SpeculativeJIT::compilePutGetterSetterById(Node* node)
+{
+ SpeculateCellOperand base(this, node->child1());
+ JSValueOperand getter(this, node->child2());
+ JSValueOperand setter(this, node->child3());
+
+#if USE(JSVALUE64)
+ GPRReg baseGPR = base.gpr();
+ GPRReg getterGPR = getter.gpr();
+ GPRReg setterGPR = setter.gpr();
+
+ flushRegisters();
+ callOperation(operationPutGetterSetter, NoResult, baseGPR, identifierUID(node->identifierNumber()), node->accessorAttributes(), getterGPR, setterGPR);
+#else
+ // These JSValues may be JSUndefined OR JSFunction*.
+ // At that time,
+ // 1. If the JSValue is JSUndefined, its payload becomes nullptr.
+ // 2. If the JSValue is JSFunction*, its payload becomes JSFunction*.
+ // So extract payload and pass it to operationPutGetterSetter. This hack is used as the same way in baseline JIT.
+ GPRReg baseGPR = base.gpr();
+ JSValueRegs getterRegs = getter.jsValueRegs();
+ JSValueRegs setterRegs = setter.jsValueRegs();
+
+ flushRegisters();
+ callOperation(operationPutGetterSetter, NoResult, baseGPR, identifierUID(node->identifierNumber()), node->accessorAttributes(), getterRegs.payloadGPR(), setterRegs.payloadGPR());
+#endif
+ m_jit.exceptionCheck();
+
+ noResult(node);
+}
+
+void SpeculativeJIT::compilePutAccessorByVal(Node* node)
+{
+ SpeculateCellOperand base(this, node->child1());
+ JSValueOperand subscript(this, node->child2());
+ SpeculateCellOperand accessor(this, node->child3());
+
+ auto operation = node->op() == PutGetterByVal ? operationPutGetterByVal : operationPutSetterByVal;
+#if USE(JSVALUE64)
+ GPRReg baseGPR = base.gpr();
+ GPRReg subscriptGPR = subscript.gpr();
+ GPRReg accessorGPR = accessor.gpr();
+
+ flushRegisters();
+ callOperation(operation, NoResult, baseGPR, subscriptGPR, node->accessorAttributes(), accessorGPR);
+#else
+ GPRReg baseGPR = base.gpr();
+ JSValueRegs subscriptRegs = subscript.jsValueRegs();
+ GPRReg accessorGPR = accessor.gpr();
+
+ flushRegisters();
+ callOperation(operation, NoResult, baseGPR, subscriptRegs.tagGPR(), subscriptRegs.payloadGPR(), node->accessorAttributes(), accessorGPR);
+#endif
+ m_jit.exceptionCheck();
+
+ noResult(node);
+}
+
} } // namespace JSC::DFG
#endif