static void updateArithProfileForUnaryArithOp(Instruction* pc, JSValue result, JSValue operand) { ArithProfile& profile = *bitwise_cast<ArithProfile*>(&pc[3].u.operand); profile.observeLHS(operand); ASSERT(result.isNumber()); if (!result.isInt32()) { if (operand.isInt32()) profile.setObservedInt32Overflow(); double doubleVal = result.asNumber(); if (!doubleVal && std::signbit(doubleVal)) profile.setObservedNegZeroDouble(); else { profile.setObservedNonNegZeroDouble(); // The Int52 overflow check here intentionally omits 1ll << 51 as a valid negative Int52 value. // Therefore, we will get a false positive if the result is that value. This is intentionally // done to simplify the checking algorithm. static const int64_t int52OverflowPoint = (1ll << 51); int64_t int64Val = static_cast<int64_t>(std::abs(doubleVal)); if (int64Val >= int52OverflowPoint) profile.setObservedInt52Overflow(); } } }
static void updateResultProfileForBinaryArithOp(ExecState* exec, Instruction* pc, JSValue result, JSValue left, JSValue right) { CodeBlock* codeBlock = exec->codeBlock(); unsigned bytecodeOffset = codeBlock->bytecodeOffset(pc); ResultProfile* profile = codeBlock->ensureResultProfile(bytecodeOffset); if (result.isNumber()) { if (!result.isInt32()) { if (left.isInt32() && right.isInt32()) profile->setObservedInt32Overflow(); double doubleVal = result.asNumber(); if (!doubleVal && std::signbit(doubleVal)) profile->setObservedNegZeroDouble(); else { profile->setObservedNonNegZeroDouble(); // The Int52 overflow check here intentionally omits 1ll << 51 as a valid negative Int52 value. // Therefore, we will get a false positive if the result is that value. This is intentionally // done to simplify the checking algorithm. static const int64_t int52OverflowPoint = (1ll << 51); int64_t int64Val = static_cast<int64_t>(std::abs(doubleVal)); if (int64Val >= int52OverflowPoint) profile->setObservedInt52Overflow(); } } } else profile->setObservedNonNumber(); }
static inline void putByVal(ExecState* exec, JSValue baseValue, uint32_t index, JSValue value) { JSGlobalData* globalData = &exec->globalData(); if (isJSArray(baseValue)) { JSArray* array = asArray(baseValue); if (array->canSetIndex(index)) { array->setIndex(*globalData, index, value); return; } JSArray::putByIndex(array, exec, index, value, strict); return; } if (isJSByteArray(baseValue) && asByteArray(baseValue)->canAccessIndex(index)) { JSByteArray* byteArray = asByteArray(baseValue); // FIXME: the JITstub used to relink this to an optimized form! if (value.isInt32()) { byteArray->setIndex(index, value.asInt32()); return; } if (value.isNumber()) { byteArray->setIndex(index, value.asNumber()); return; } } baseValue.putByIndex(exec, index, value, strict); }
SpeculatedType speculationFromValue(JSValue value) { if (value.isEmpty()) return SpecEmpty; if (value.isInt32()) return SpecInt32; if (value.isDouble()) { double number = value.asNumber(); if (number == number) { int64_t asInt64 = static_cast<int64_t>(number); if (asInt64 == number && (asInt64 || !std::signbit(number)) && asInt64 < (static_cast<int64_t>(1) << 47) && asInt64 >= -(static_cast<int64_t>(1) << 47)) { return SpecInt48AsDouble; } return SpecNonIntAsDouble; } return SpecDoubleNaN; } if (value.isCell()) return speculationFromCell(value.asCell()); if (value.isBoolean()) return SpecBoolean; ASSERT(value.isUndefinedOrNull()); return SpecOther; }
static inline T toSmallerInt(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration) { typedef IntTypeLimits<T> LimitsTrait; // Fast path if the value is already a 32-bit signed integer in the right range. if (value.isInt32()) { int32_t d = value.asInt32(); if (d >= LimitsTrait::minValue && d <= LimitsTrait::maxValue) return static_cast<T>(d); if (configuration == EnforceRange) { throwTypeError(exec); return 0; } d %= LimitsTrait::numberOfValues; return static_cast<T>(d > LimitsTrait::maxValue ? d - LimitsTrait::numberOfValues : d); } double x = value.toNumber(exec); if (exec->hadException()) return 0; if (configuration == EnforceRange) return enforceRange(exec, x, LimitsTrait::minValue, LimitsTrait::maxValue); if (std::isnan(x) || std::isinf(x) || !x) return 0; x = x < 0 ? -floor(fabs(x)) : floor(fabs(x)); x = fmod(x, LimitsTrait::numberOfValues); return static_cast<T>(x > LimitsTrait::maxValue ? x - LimitsTrait::numberOfValues : x); }
void dumpImmediate(JSValue value) { if (value.isNull()) write(NullTag); else if (value.isUndefined()) write(UndefinedTag); else if (value.isNumber()) { if (value.isInt32()) { if (!value.asInt32()) write(ZeroTag); else if (value.asInt32() == 1) write(OneTag); else { write(IntTag); write(static_cast<uint32_t>(value.asInt32())); } } else { write(DoubleTag); write(value.asDouble()); } } else if (value.isBoolean()) { if (value.isTrue()) write(TrueTag); else write(FalseTag); } }
// http://www.w3.org/TR/WebIDL/#es-byte int8_t toInt8(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration) { // Fast path if the value is already a 32-bit signed integer in the right range. if (value.isInt32()) { int32_t d = value.asInt32(); if (d >= kMinInt8 && d <= kMaxInt8) return static_cast<int8_t>(d); if (configuration == EnforceRange) { throwTypeError(exec); return 0; } d %= 256; return static_cast<int8_t>(d > kMaxInt8 ? d - 256 : d); } double x = value.toNumber(exec); if (exec->hadException()) return 0; if (configuration == EnforceRange) return enforceRange(exec, x, kMinInt8, kMaxInt8); if (std::isnan(x) || std::isinf(x) || !x) return 0; x = x < 0 ? -floor(fabs(x)) : floor(fabs(x)); x = fmod(x, 256); // 2^8. return static_cast<int8_t>(x > kMaxInt8 ? x - 256 : x); }
char* newTypedArrayWithOneArgument( ExecState* exec, Structure* structure, EncodedJSValue encodedValue) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); JSValue value = JSValue::decode(encodedValue); if (JSArrayBuffer* jsBuffer = jsDynamicCast<JSArrayBuffer*>(value)) { RefPtr<ArrayBuffer> buffer = jsBuffer->impl(); if (buffer->byteLength() % ViewClass::elementSize) { vm.throwException(exec, createRangeError(exec, ASCIILiteral("ArrayBuffer length minus the byteOffset is not a multiple of the element size"))); return 0; } return bitwise_cast<char*>( ViewClass::create( exec, structure, buffer, 0, buffer->byteLength() / ViewClass::elementSize)); } if (JSObject* object = jsDynamicCast<JSObject*>(value)) { unsigned length = object->get(exec, vm.propertyNames->length).toUInt32(exec); if (exec->hadException()) return 0; ViewClass* result = ViewClass::createUninitialized(exec, structure, length); if (!result) return 0; if (!result->set(exec, object, 0, length)) return 0; return bitwise_cast<char*>(result); } int length; if (value.isInt32()) length = value.asInt32(); else if (!value.isNumber()) { vm.throwException(exec, createTypeError(exec, ASCIILiteral("Invalid array length argument"))); return 0; } else { length = static_cast<int>(value.asNumber()); if (length != value.asNumber()) { vm.throwException(exec, createTypeError(exec, ASCIILiteral("Invalid array length argument (fractional lengths not allowed)"))); return 0; } } if (length < 0) { vm.throwException(exec, createRangeError(exec, ASCIILiteral("Requested length is negative"))); return 0; } return bitwise_cast<char*>(ViewClass::create(exec, structure, length)); }
// http://www.w3.org/TR/WebIDL/#es-long int32_t toInt32EnforceRange(ExecState* exec, JSValue value) { if (value.isInt32()) return value.asInt32(); double x = value.toNumber(exec); if (exec->hadException()) return 0; return enforceRange(exec, x, kMinInt32, kMaxInt32); }
PredictedType predictionFromValue(JSValue value) { if (value.isInt32()) return PredictInt32; if (value.isDouble()) return PredictDouble; if (value.isCell()) return predictionFromCell(value.asCell()); if (value.isBoolean()) return PredictBoolean; ASSERT(value.isUndefinedOrNull()); return PredictOther; }
static inline int32_t extractRadixFromArgs(ExecState* exec) { JSValue radixValue = exec->argument(0); int32_t radix; if (radixValue.isInt32()) radix = radixValue.asInt32(); else if (radixValue.isUndefined()) radix = 10; else radix = static_cast<int32_t>(radixValue.toInteger(exec)); // nan -> 0 return radix; }
// ECMA-262 20.1.2.5 static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsSafeInteger(ExecState* exec) { JSValue argument = exec->argument(0); bool isInteger; if (argument.isInt32()) isInteger = true; else if (!argument.isDouble()) isInteger = false; else { double number = argument.asDouble(); isInteger = trunc(number) == number && std::abs(number) <= 9007199254740991.0; } return JSValue::encode(jsBoolean(isInteger)); }
// ECMA-262 20.1.2.3 static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsInteger(ExecState* exec) { JSValue argument = exec->argument(0); bool isInteger; if (argument.isInt32()) isInteger = true; else if (!argument.isDouble()) isInteger = false; else { double number = argument.asDouble(); isInteger = std::isfinite(number) && trunc(number) == number; } return JSValue::encode(jsBoolean(isInteger)); }
ALWAYS_INLINE static void operationPutByValInternal(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedProperty, EncodedJSValue encodedValue) { JSGlobalData* globalData = &exec->globalData(); JSValue baseValue = JSValue::decode(encodedBase); JSValue property = JSValue::decode(encodedProperty); JSValue value = JSValue::decode(encodedValue); if (LIKELY(property.isUInt32())) { uint32_t i = property.asUInt32(); if (isJSArray(globalData, baseValue)) { JSArray* jsArray = asArray(baseValue); if (jsArray->canSetIndex(i)) { jsArray->setIndex(*globalData, i, value); return; } jsArray->JSArray::put(exec, i, value); return; } if (isJSByteArray(globalData, baseValue) && asByteArray(baseValue)->canAccessIndex(i)) { JSByteArray* jsByteArray = asByteArray(baseValue); // FIXME: the JITstub used to relink this to an optimized form! if (value.isInt32()) { jsByteArray->setIndex(i, value.asInt32()); return; } double dValue = 0; if (value.getNumber(dValue)) { jsByteArray->setIndex(i, dValue); return; } } baseValue.put(exec, i, value); return; } // Don't put to an object if toString throws an exception. Identifier ident(exec, property.toString(exec)); if (!globalData->exception) { PutPropertySlot slot(strict); baseValue.put(exec, ident, value, slot); } }
inline bool predictionIsValid(JSGlobalData* globalData, JSValue value, PredictedType type) { // this takes into account only local variable predictions that get enforced // on SetLocal. if (isInt32Prediction(type)) return value.isInt32(); if (isArrayPrediction(type)) return isJSArray(globalData, value); if (isBooleanPrediction(type)) return value.isBoolean(); return true; }
TypedPointer IndexedAbstractHeap::baseIndex(Output& out, LValue base, LValue index, JSValue indexAsConstant, ptrdiff_t offset) { if (indexAsConstant.isInt32()) return out.address(base, at(indexAsConstant.asInt32()), offset); LValue result; if (m_canShift) { if (!m_scaleTerm) result = out.add(base, index); else result = out.add(base, out.shl(index, m_scaleTerm)); } else result = out.add(base, out.mul(index, m_scaleTerm)); return TypedPointer(atAnyIndex(), out.addPtr(result, m_offset + offset)); }
// http://www.w3.org/TR/WebIDL/#es-long-long int64_t toInt64(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration) { if (value.isInt32()) return value.asInt32(); double x = value.toNumber(exec); if (exec->hadException()) return 0; if (configuration == EnforceRange) return enforceRange(exec, x, -kJSMaxInteger, kJSMaxInteger); // Map NaNs and +/-Infinity to 0; convert finite values modulo 2^64. unsigned long long n; doubleToInteger(x, n); return n; }
PredictedType predictionFromValue(JSValue value) { if (value.isInt32()) return PredictInt32; if (value.isDouble()) { double number = value.asNumber(); if (number == number) return PredictDoubleReal; return PredictDoubleNaN; } if (value.isCell()) return predictionFromCell(value.asCell()); if (value.isBoolean()) return PredictBoolean; ASSERT(value.isUndefinedOrNull()); return PredictOther; }
void ValueProfile::computeStatistics(Statistics& statistics) const { for (unsigned i = 0; i < numberOfBuckets; ++i) { JSValue value = JSValue::decode(m_buckets[i]); if (!value) continue; statistics.samples++; if (value.isInt32()) statistics.int32s++; else if (value.isDouble()) statistics.doubles++; else if (value.isCell()) computeStatistics(value.asCell()->structure()->classInfo(), statistics); else if (value.isBoolean()) statistics.booleans++; } }
static ALWAYS_INLINE bool toThisNumber(JSValue thisValue, double& x) { if (thisValue.isInt32()) { x = thisValue.asInt32(); return true; } if (thisValue.isDouble()) { x = thisValue.asDouble(); return true; } if (thisValue.isCell() && thisValue.asCell()->type() == NumberObjectType) { x = static_cast<const NumberObject*>(thisValue.asCell())->internalValue().asNumber(); return true; } return false; }
bool isWithinPowerOfTwo(Node* node) { switch (node->op()) { case DoubleConstant: case JSConstant: case Int52Constant: { return isWithinPowerOfTwoForConstant<power>(node); } case BitAnd: { if (power > 31) return true; return isWithinPowerOfTwoNonRecursive<power>(node->child1().node()) || isWithinPowerOfTwoNonRecursive<power>(node->child2().node()); } case BitOr: case BitXor: case BitLShift: { return power > 31; } case BitRShift: case BitURShift: { if (power > 31) return true; Node* shiftAmount = node->child2().node(); if (!node->isNumberConstant()) return false; JSValue immediateValue = shiftAmount->asJSValue(); if (!immediateValue.isInt32()) return false; return immediateValue.asInt32() > 32 - power; } default: return false; } }
SpeculatedType speculationFromValue(JSValue value) { if (value.isEmpty()) return SpecEmpty; if (value.isInt32()) return SpecInt32; if (value.isDouble()) { double number = value.asNumber(); if (number != number) return SpecDoubleNaN; if (value.isMachineInt()) return SpecInt52AsDouble; return SpecNonIntAsDouble; } if (value.isCell()) return speculationFromCell(value.asCell()); if (value.isBoolean()) return SpecBoolean; ASSERT(value.isUndefinedOrNull()); return SpecOther; }
InferredType::Descriptor InferredType::Descriptor::forValue(JSValue value) { if (value.isBoolean()) return Boolean; if (value.isUndefinedOrNull()) return Other; if (value.isInt32()) return Int32; if (value.isNumber()) return Number; if (value.isCell()) { JSCell* cell = value.asCell(); if (cell->isString()) return String; if (cell->isSymbol()) return Symbol; if (cell->isObject()) { if (cell->structure()->transitionWatchpointSetIsStillValid()) return Descriptor(ObjectWithStructure, cell->structure()); return Object; } } return Top; }
void handleNode() { switch (m_node->op()) { case BitOr: handleCommutativity(); if (m_node->child2()->isConstant()) { JSValue op2 = m_graph.valueOfJSConstant(m_node->child2().node()); if (op2.isInt32() && !op2.asInt32()) { convertToIdentityOverChild1(); break; } } break; case BitXor: case BitAnd: handleCommutativity(); break; case BitLShift: case BitRShift: case BitURShift: if (m_node->child2()->isConstant()) { JSValue op2 = m_graph.valueOfJSConstant(m_node->child2().node()); if (op2.isInt32() && !(op2.asInt32() & 0x1f)) { convertToIdentityOverChild1(); break; } } break; case UInt32ToNumber: if (m_node->child1()->op() == BitURShift && m_node->child1()->child2()->isConstant()) { JSValue shiftAmount = m_graph.valueOfJSConstant( m_node->child1()->child2().node()); if (shiftAmount.isInt32() && (shiftAmount.asInt32() & 0x1f)) { m_node->convertToIdentity(); m_changed = true; break; } } break; case ArithAdd: handleCommutativity(); if (m_graph.isInt32Constant(m_node->child2().node())) { int32_t value = m_graph.valueOfInt32Constant( m_node->child2().node()); if (!value) { convertToIdentityOverChild1(); break; } } break; case ArithMul: handleCommutativity(); break; case ArithSub: if (m_graph.isInt32Constant(m_node->child2().node()) && m_node->isBinaryUseKind(Int32Use)) { int32_t value = m_graph.valueOfInt32Constant(m_node->child2().node()); if (-value != value) { m_node->setOp(ArithAdd); m_node->child2().setNode( m_insertionSet.insertConstant( m_nodeIndex, m_node->origin, jsNumber(-value))); m_changed = true; break; } } break; case GetArrayLength: if (JSArrayBufferView* view = m_graph.tryGetFoldableViewForChild1(m_node)) foldTypedArrayPropertyToConstant(view, jsNumber(view->length())); break; case GetTypedArrayByteOffset: if (JSArrayBufferView* view = m_graph.tryGetFoldableView(m_node->child1().node())) foldTypedArrayPropertyToConstant(view, jsNumber(view->byteOffset())); break; case GetIndexedPropertyStorage: if (JSArrayBufferView* view = m_graph.tryGetFoldableViewForChild1(m_node)) { if (view->mode() != FastTypedArray) { prepareToFoldTypedArray(view); m_node->convertToConstantStoragePointer(view->vector()); m_changed = true; break; } else { // FIXME: It would be awesome to be able to fold the property storage for // these GC-allocated typed arrays. For now it doesn't matter because the // most common use-cases for constant typed arrays involve large arrays with // aliased buffer views. // https://bugs.webkit.org/show_bug.cgi?id=125425 } } break; default: break; } }
bool foldConstants(BasicBlock* block) { bool changed = false; m_state.beginBasicBlock(block); for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) { if (!m_state.isValid()) break; Node* node = block->at(indexInBlock); bool alreadyHandled = false; bool eliminated = false; switch (node->op()) { case BooleanToNumber: { if (node->child1().useKind() == UntypedUse && !m_interpreter.needsTypeCheck(node->child1(), SpecBoolean)) node->child1().setUseKind(BooleanUse); break; } case CheckArgumentsNotCreated: { if (!isEmptySpeculation( m_state.variables().operand( m_graph.argumentsRegisterFor(node->origin.semantic)).m_type)) break; node->convertToPhantom(); eliminated = true; break; } case CheckStructure: case ArrayifyToStructure: { AbstractValue& value = m_state.forNode(node->child1()); StructureSet set; if (node->op() == ArrayifyToStructure) set = node->structure(); else set = node->structureSet(); if (value.m_structure.isSubsetOf(set)) { m_interpreter.execute(indexInBlock); // Catch the fact that we may filter on cell. node->convertToPhantom(); eliminated = true; break; } break; } case CheckArray: case Arrayify: { if (!node->arrayMode().alreadyChecked(m_graph, node, m_state.forNode(node->child1()))) break; node->convertToPhantom(); eliminated = true; break; } case PutStructure: { if (m_state.forNode(node->child1()).m_structure.onlyStructure() != node->transition()->next) break; node->convertToPhantom(); eliminated = true; break; } case CheckFunction: { if (m_state.forNode(node->child1()).value() != node->function()->value()) break; node->convertToPhantom(); eliminated = true; break; } case CheckInBounds: { JSValue left = m_state.forNode(node->child1()).value(); JSValue right = m_state.forNode(node->child2()).value(); if (left && right && left.isInt32() && right.isInt32() && static_cast<uint32_t>(left.asInt32()) < static_cast<uint32_t>(right.asInt32())) { node->convertToPhantom(); eliminated = true; break; } break; } case MultiGetByOffset: { Edge baseEdge = node->child1(); Node* base = baseEdge.node(); MultiGetByOffsetData& data = node->multiGetByOffsetData(); // First prune the variants, then check if the MultiGetByOffset can be // strength-reduced to a GetByOffset. AbstractValue baseValue = m_state.forNode(base); m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before. alreadyHandled = true; // Don't allow the default constant folder to do things to this. for (unsigned i = 0; i < data.variants.size(); ++i) { GetByIdVariant& variant = data.variants[i]; variant.structureSet().filter(baseValue); if (variant.structureSet().isEmpty()) { data.variants[i--] = data.variants.last(); data.variants.removeLast(); changed = true; } } if (data.variants.size() != 1) break; emitGetByOffset( indexInBlock, node, baseValue, data.variants[0], data.identifierNumber); changed = true; break; } case MultiPutByOffset: { Edge baseEdge = node->child1(); Node* base = baseEdge.node(); MultiPutByOffsetData& data = node->multiPutByOffsetData(); AbstractValue baseValue = m_state.forNode(base); m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before. alreadyHandled = true; // Don't allow the default constant folder to do things to this. for (unsigned i = 0; i < data.variants.size(); ++i) { PutByIdVariant& variant = data.variants[i]; variant.oldStructure().filter(baseValue); if (variant.oldStructure().isEmpty()) { data.variants[i--] = data.variants.last(); data.variants.removeLast(); changed = true; continue; } if (variant.kind() == PutByIdVariant::Transition && variant.oldStructure().onlyStructure() == variant.newStructure()) { variant = PutByIdVariant::replace( variant.oldStructure(), variant.offset()); changed = true; } } if (data.variants.size() != 1) break; emitPutByOffset( indexInBlock, node, baseValue, data.variants[0], data.identifierNumber); changed = true; break; } case GetById: case GetByIdFlush: { Edge childEdge = node->child1(); Node* child = childEdge.node(); unsigned identifierNumber = node->identifierNumber(); AbstractValue baseValue = m_state.forNode(child); m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before. alreadyHandled = true; // Don't allow the default constant folder to do things to this. if (baseValue.m_structure.isTop() || baseValue.m_structure.isClobbered() || (node->child1().useKind() == UntypedUse || (baseValue.m_type & ~SpecCell))) break; GetByIdStatus status = GetByIdStatus::computeFor( vm(), baseValue.m_structure.set(), m_graph.identifiers()[identifierNumber]); if (!status.isSimple()) break; for (unsigned i = status.numVariants(); i--;) { if (!status[i].constantChecks().isEmpty() || status[i].alternateBase()) { // FIXME: We could handle prototype cases. // https://bugs.webkit.org/show_bug.cgi?id=110386 break; } } if (status.numVariants() == 1) { emitGetByOffset(indexInBlock, node, baseValue, status[0], identifierNumber); changed = true; break; } if (!isFTL(m_graph.m_plan.mode)) break; MultiGetByOffsetData* data = m_graph.m_multiGetByOffsetData.add(); data->variants = status.variants(); data->identifierNumber = identifierNumber; node->convertToMultiGetByOffset(data); changed = true; break; } case PutById: case PutByIdDirect: case PutByIdFlush: { NodeOrigin origin = node->origin; Edge childEdge = node->child1(); Node* child = childEdge.node(); unsigned identifierNumber = node->identifierNumber(); ASSERT(childEdge.useKind() == CellUse); AbstractValue baseValue = m_state.forNode(child); m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before. alreadyHandled = true; // Don't allow the default constant folder to do things to this. if (baseValue.m_structure.isTop() || baseValue.m_structure.isClobbered()) break; PutByIdStatus status = PutByIdStatus::computeFor( vm(), m_graph.globalObjectFor(origin.semantic), baseValue.m_structure.set(), m_graph.identifiers()[identifierNumber], node->op() == PutByIdDirect); if (!status.isSimple()) break; ASSERT(status.numVariants()); if (status.numVariants() > 1 && !isFTL(m_graph.m_plan.mode)) break; changed = true; for (unsigned i = status.numVariants(); i--;) addChecks(origin, indexInBlock, status[i].constantChecks()); if (status.numVariants() == 1) { emitPutByOffset(indexInBlock, node, baseValue, status[0], identifierNumber); break; } ASSERT(isFTL(m_graph.m_plan.mode)); MultiPutByOffsetData* data = m_graph.m_multiPutByOffsetData.add(); data->variants = status.variants(); data->identifierNumber = identifierNumber; node->convertToMultiPutByOffset(data); break; } case ToPrimitive: { if (m_state.forNode(node->child1()).m_type & ~(SpecFullNumber | SpecBoolean | SpecString)) break; node->convertToIdentity(); changed = true; break; } case GetMyArgumentByVal: { InlineCallFrame* inlineCallFrame = node->origin.semantic.inlineCallFrame; JSValue value = m_state.forNode(node->child1()).m_value; if (inlineCallFrame && value && value.isInt32()) { int32_t index = value.asInt32(); if (index >= 0 && static_cast<size_t>(index + 1) < inlineCallFrame->arguments.size()) { // Roll the interpreter over this. m_interpreter.execute(indexInBlock); eliminated = true; int operand = inlineCallFrame->stackOffset + m_graph.baselineCodeBlockFor(inlineCallFrame)->argumentIndexAfterCapture(index); m_insertionSet.insertNode( indexInBlock, SpecNone, CheckArgumentsNotCreated, node->origin); m_insertionSet.insertNode( indexInBlock, SpecNone, Phantom, node->origin, node->children); node->convertToGetLocalUnlinked(VirtualRegister(operand)); break; } } break; } case Check: { alreadyHandled = true; m_interpreter.execute(indexInBlock); for (unsigned i = 0; i < AdjacencyList::Size; ++i) { Edge edge = node->children.child(i); if (!edge) break; if (edge.isProved() || edge.willNotHaveCheck()) { node->children.removeEdge(i--); changed = true; } } break; } default: break; } if (eliminated) { changed = true; continue; } if (alreadyHandled) continue; m_interpreter.execute(indexInBlock); if (!m_state.isValid()) { // If we invalidated then we shouldn't attempt to constant-fold. Here's an // example: // // c: JSConstant(4.2) // x: ValueToInt32(Check:Int32:@const) // // It would be correct for an analysis to assume that execution cannot // proceed past @x. Therefore, constant-folding @x could be rather bad. But, // the CFA may report that it found a constant even though it also reported // that everything has been invalidated. This will only happen in a couple of // the constant folding cases; most of them are also separately defensive // about such things. break; } if (!node->shouldGenerate() || m_state.didClobber() || node->hasConstant()) continue; // Interesting fact: this freezing that we do right here may turn an fragile value into // a weak value. See DFGValueStrength.h. FrozenValue* value = m_graph.freeze(m_state.forNode(node).value()); if (!*value) continue; NodeOrigin origin = node->origin; AdjacencyList children = node->children; m_graph.convertToConstant(node, value); if (!children.isEmpty()) { m_insertionSet.insertNode( indexInBlock, SpecNone, Phantom, origin, children); } changed = true; } m_state.reset(); m_insertionSet.execute(block); return changed; }
bool foldConstants(BasicBlock* block) { bool changed = false; m_state.beginBasicBlock(block); for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) { if (!m_state.isValid()) break; Node* node = block->at(indexInBlock); bool eliminated = false; switch (node->op()) { case BooleanToNumber: { if (node->child1().useKind() == UntypedUse && !m_interpreter.needsTypeCheck(node->child1(), SpecBoolean)) node->child1().setUseKind(BooleanUse); break; } case CheckArgumentsNotCreated: { if (!isEmptySpeculation( m_state.variables().operand( m_graph.argumentsRegisterFor(node->origin.semantic)).m_type)) break; node->convertToPhantom(); eliminated = true; break; } case CheckStructure: case ArrayifyToStructure: { AbstractValue& value = m_state.forNode(node->child1()); StructureSet set; if (node->op() == ArrayifyToStructure) set = node->structure(); else set = node->structureSet(); if (value.m_currentKnownStructure.isSubsetOf(set)) { m_interpreter.execute(indexInBlock); // Catch the fact that we may filter on cell. node->convertToPhantom(); eliminated = true; break; } StructureAbstractValue& structureValue = value.m_futurePossibleStructure; if (structureValue.isSubsetOf(set) && structureValue.hasSingleton()) { Structure* structure = structureValue.singleton(); m_interpreter.execute(indexInBlock); // Catch the fact that we may filter on cell. AdjacencyList children = node->children; children.removeEdge(0); if (!!children.child1()) m_insertionSet.insertNode(indexInBlock, SpecNone, Phantom, node->origin, children); node->children.setChild2(Edge()); node->children.setChild3(Edge()); node->convertToStructureTransitionWatchpoint(structure); eliminated = true; break; } break; } case CheckArray: case Arrayify: { if (!node->arrayMode().alreadyChecked(m_graph, node, m_state.forNode(node->child1()))) break; node->convertToPhantom(); eliminated = true; break; } case CheckFunction: { if (m_state.forNode(node->child1()).value() != node->function()) break; node->convertToPhantom(); eliminated = true; break; } case CheckInBounds: { JSValue left = m_state.forNode(node->child1()).value(); JSValue right = m_state.forNode(node->child2()).value(); if (left && right && left.isInt32() && right.isInt32() && static_cast<uint32_t>(left.asInt32()) < static_cast<uint32_t>(right.asInt32())) { node->convertToPhantom(); eliminated = true; break; } break; } case MultiGetByOffset: { Edge childEdge = node->child1(); Node* child = childEdge.node(); MultiGetByOffsetData& data = node->multiGetByOffsetData(); Structure* structure = m_state.forNode(child).bestProvenStructure(); if (!structure) break; for (unsigned i = data.variants.size(); i--;) { const GetByIdVariant& variant = data.variants[i]; if (!variant.structureSet().contains(structure)) continue; if (variant.chain()) break; emitGetByOffset(indexInBlock, node, structure, variant, data.identifierNumber); eliminated = true; break; } break; } case MultiPutByOffset: { Edge childEdge = node->child1(); Node* child = childEdge.node(); MultiPutByOffsetData& data = node->multiPutByOffsetData(); Structure* structure = m_state.forNode(child).bestProvenStructure(); if (!structure) break; for (unsigned i = data.variants.size(); i--;) { const PutByIdVariant& variant = data.variants[i]; if (variant.oldStructure() != structure) continue; emitPutByOffset(indexInBlock, node, structure, variant, data.identifierNumber); eliminated = true; break; } break; } case GetById: case GetByIdFlush: { Edge childEdge = node->child1(); Node* child = childEdge.node(); unsigned identifierNumber = node->identifierNumber(); if (childEdge.useKind() != CellUse) break; Structure* structure = m_state.forNode(child).bestProvenStructure(); if (!structure) break; GetByIdStatus status = GetByIdStatus::computeFor( vm(), structure, m_graph.identifiers()[identifierNumber]); if (!status.isSimple() || status.numVariants() != 1) { // FIXME: We could handle prototype cases. // https://bugs.webkit.org/show_bug.cgi?id=110386 break; } emitGetByOffset(indexInBlock, node, structure, status[0], identifierNumber); eliminated = true; break; } case PutById: case PutByIdDirect: { NodeOrigin origin = node->origin; Edge childEdge = node->child1(); Node* child = childEdge.node(); unsigned identifierNumber = node->identifierNumber(); ASSERT(childEdge.useKind() == CellUse); Structure* structure = m_state.forNode(child).bestProvenStructure(); if (!structure) break; PutByIdStatus status = PutByIdStatus::computeFor( vm(), m_graph.globalObjectFor(origin.semantic), structure, m_graph.identifiers()[identifierNumber], node->op() == PutByIdDirect); if (!status.isSimple()) break; if (status.numVariants() != 1) break; emitPutByOffset(indexInBlock, node, structure, status[0], identifierNumber); eliminated = true; break; } case ToPrimitive: { if (m_state.forNode(node->child1()).m_type & ~(SpecFullNumber | SpecBoolean | SpecString)) break; node->convertToIdentity(); break; } default: break; } if (eliminated) { changed = true; continue; } m_interpreter.execute(indexInBlock); if (!m_state.isValid()) { // If we invalidated then we shouldn't attempt to constant-fold. Here's an // example: // // c: JSConstant(4.2) // x: ValueToInt32(Check:Int32:@const) // // It would be correct for an analysis to assume that execution cannot // proceed past @x. Therefore, constant-folding @x could be rather bad. But, // the CFA may report that it found a constant even though it also reported // that everything has been invalidated. This will only happen in a couple of // the constant folding cases; most of them are also separately defensive // about such things. break; } if (!node->shouldGenerate() || m_state.didClobber() || node->hasConstant()) continue; JSValue value = m_state.forNode(node).value(); if (!value) continue; // Check if merging the abstract value of the constant into the abstract value // we've proven for this node wouldn't widen the proof. If it widens the proof // (i.e. says that the set contains more things in it than it previously did) // then we refuse to fold. AbstractValue oldValue = m_state.forNode(node); AbstractValue constantValue; constantValue.set(m_graph, value); constantValue.fixTypeForRepresentation(node); if (oldValue.merge(constantValue)) continue; NodeOrigin origin = node->origin; AdjacencyList children = node->children; if (node->op() == GetLocal) m_graph.dethread(); else ASSERT(!node->hasVariableAccessData(m_graph)); m_graph.convertToConstant(node, value); m_insertionSet.insertNode( indexInBlock, SpecNone, Phantom, origin, children); changed = true; } m_state.reset(); m_insertionSet.execute(block); return changed; }
// ECMA-262 20.1.2.2 static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsFinite(ExecState* exec) { JSValue argument = exec->argument(0); return JSValue::encode(jsBoolean(argument.isNumber() && (argument.isInt32() || std::isfinite(argument.asDouble())))); }
EncodedJSValue JSC_HOST_CALL numberProtoFuncToString(ExecState* exec) { JSValue thisValue = exec->hostThisValue(); JSValue v = thisValue.getJSNumber(); if (!v) return throwVMTypeError(exec); JSValue radixValue = exec->argument(0); int radix; if (radixValue.isInt32()) radix = radixValue.asInt32(); else if (radixValue.isUndefined()) radix = 10; else radix = static_cast<int>(radixValue.toInteger(exec)); // nan -> 0 if (radix == 10) return JSValue::encode(jsString(exec, v.toString(exec))); static const char* const digits = "0123456789abcdefghijklmnopqrstuvwxyz"; // Fast path for number to character conversion. if (radix == 36) { if (v.isInt32()) { int x = v.asInt32(); if (static_cast<unsigned>(x) < 36) { // Exclude negatives JSGlobalData* globalData = &exec->globalData(); return JSValue::encode(globalData->smallStrings.singleCharacterString(globalData, digits[x])); } } } if (radix < 2 || radix > 36) return throwVMError(exec, createRangeError(exec, "toString() radix argument must be between 2 and 36")); // INT_MAX results in 1024 characters left of the dot with radix 2 // give the same space on the right side. safety checks are in place // unless someone finds a precise rule. char s[2048 + 3]; const char* lastCharInString = s + sizeof(s) - 1; double x = v.uncheckedGetNumber(); if (isnan(x) || isinf(x)) return JSValue::encode(jsString(exec, UString::number(x))); bool isNegative = x < 0.0; if (isNegative) x = -x; double integerPart = floor(x); char* decimalPoint = s + sizeof(s) / 2; // convert integer portion char* p = decimalPoint; double d = integerPart; do { int remainderDigit = static_cast<int>(fmod(d, radix)); *--p = digits[remainderDigit]; d /= radix; } while ((d <= -1.0 || d >= 1.0) && s < p); if (isNegative) *--p = '-'; char* startOfResultString = p; ASSERT(s <= startOfResultString); d = x - integerPart; p = decimalPoint; const double epsilon = 0.001; // TODO: guessed. base on radix ? bool hasFractionalPart = (d < -epsilon || d > epsilon); if (hasFractionalPart) { *p++ = '.'; do { d *= radix; const int digit = static_cast<int>(d); *p++ = digits[digit]; d -= digit; } while ((d < -epsilon || d > epsilon) && p < lastCharInString); } *p = '\0'; ASSERT(p < s + sizeof(s)); return JSValue::encode(jsString(exec, startOfResultString)); }