#include "mozilla/Casting.h"
#include "jsmath.h"
#include "builtin/AtomicsObject.h"
#include "builtin/intl/Collator.h"
#include "builtin/intl/DateTimeFormat.h"
#include "builtin/intl/NumberFormat.h"
#include "builtin/intl/PluralRules.h"
#include "builtin/intl/RelativeTimeFormat.h"
#include "builtin/MapObject.h"
#include "builtin/String.h"
#include "builtin/TestingFunctions.h"
#include "builtin/TypedObject.h"
#include "jit/BaselineInspector.h"
#include "jit/InlinableNatives.h"
#include "jit/IonBuilder.h"
#include "jit/Lowering.h"
#include "jit/MIR.h"
#include "jit/MIRGraph.h"
#include "vm/ArgumentsObject.h"
#include "vm/ArrayBufferObject.h"
#include "vm/JSObject.h"
#include "vm/ProxyObject.h"
#include "vm/SelfHosting.h"
#include "vm/SharedArrayObject.h"
#include "vm/TypedArrayObject.h"
#include "wasm/WasmInstance.h"
#include "jit/shared/Lowering-shared-inl.h"
#include "vm/JSScript-inl.h"
#include "vm/NativeObject-inl.h"
#include "vm/StringObject-inl.h"
#include "vm/UnboxedObject-inl.h"
using mozilla::ArrayLength;
using mozilla::AssertedCast;
using mozilla::Maybe;
using JS::DoubleNaNValue;
using JS::TrackedOutcome;
namespace js {
namespace jit {
static bool CanInlineCrossRealm(InlinableNative native) {
switch (native) {
case InlinableNative::MathAbs:
case InlinableNative::MathFloor:
case InlinableNative::MathCeil:
case InlinableNative::MathRound:
case InlinableNative::MathClz32:
case InlinableNative::MathSqrt:
case InlinableNative::MathATan2:
case InlinableNative::MathHypot:
case InlinableNative::MathMax:
case InlinableNative::MathMin:
case InlinableNative::MathPow:
case InlinableNative::MathImul:
case InlinableNative::MathFRound:
case InlinableNative::MathTrunc:
case InlinableNative::MathSign:
case InlinableNative::MathSin:
case InlinableNative::MathTan:
case InlinableNative::MathCos:
case InlinableNative::MathExp:
case InlinableNative::MathLog:
case InlinableNative::MathASin:
case InlinableNative::MathATan:
case InlinableNative::MathACos:
case InlinableNative::MathLog10:
case InlinableNative::MathLog2:
case InlinableNative::MathLog1P:
case InlinableNative::MathExpM1:
case InlinableNative::MathCosH:
case InlinableNative::MathSinH:
case InlinableNative::MathTanH:
case InlinableNative::MathACosH:
case InlinableNative::MathASinH:
case InlinableNative::MathATanH:
case InlinableNative::MathCbrt:
case InlinableNative::Boolean:
return true;
case InlinableNative::Array:
return true;
case InlinableNative::MathRandom:
return false;
case InlinableNative::IntlGuardToCollator:
case InlinableNative::IntlGuardToDateTimeFormat:
case InlinableNative::IntlGuardToNumberFormat:
case InlinableNative::IntlGuardToPluralRules:
case InlinableNative::IntlGuardToRelativeTimeFormat:
case InlinableNative::IsRegExpObject:
case InlinableNative::RegExpMatcher:
case InlinableNative::RegExpSearcher:
case InlinableNative::RegExpTester:
case InlinableNative::RegExpPrototypeOptimizable:
case InlinableNative::RegExpInstanceOptimizable:
case InlinableNative::GetFirstDollarIndex:
case InlinableNative::IntrinsicNewArrayIterator:
case InlinableNative::IntrinsicNewStringIterator:
case InlinableNative::IntrinsicNewRegExpStringIterator:
case InlinableNative::IntrinsicStringReplaceString:
case InlinableNative::IntrinsicStringSplitString:
case InlinableNative::IntrinsicUnsafeSetReservedSlot:
case InlinableNative::IntrinsicUnsafeGetReservedSlot:
case InlinableNative::IntrinsicUnsafeGetObjectFromReservedSlot:
case InlinableNative::IntrinsicUnsafeGetInt32FromReservedSlot:
case InlinableNative::IntrinsicUnsafeGetStringFromReservedSlot:
case InlinableNative::IntrinsicUnsafeGetBooleanFromReservedSlot:
case InlinableNative::IntrinsicIsCallable:
case InlinableNative::IntrinsicIsConstructor:
case InlinableNative::IntrinsicToObject:
case InlinableNative::IntrinsicIsObject:
case InlinableNative::IntrinsicIsCrossRealmArrayConstructor:
case InlinableNative::IntrinsicToInteger:
case InlinableNative::IntrinsicToString:
case InlinableNative::IntrinsicIsConstructing:
case InlinableNative::IntrinsicSubstringKernel:
case InlinableNative::IntrinsicGuardToArrayIterator:
case InlinableNative::IntrinsicGuardToMapIterator:
case InlinableNative::IntrinsicGuardToSetIterator:
case InlinableNative::IntrinsicGuardToStringIterator:
case InlinableNative::IntrinsicGuardToRegExpStringIterator:
case InlinableNative::IntrinsicObjectHasPrototype:
case InlinableNative::IntrinsicFinishBoundFunctionInit:
case InlinableNative::IntrinsicIsPackedArray:
case InlinableNative::IntrinsicGuardToMapObject:
case InlinableNative::IntrinsicGetNextMapEntryForIterator:
case InlinableNative::IntrinsicGuardToSetObject:
case InlinableNative::IntrinsicGetNextSetEntryForIterator:
case InlinableNative::IntrinsicGuardToArrayBuffer:
case InlinableNative::IntrinsicArrayBufferByteLength:
case InlinableNative::IntrinsicPossiblyWrappedArrayBufferByteLength:
case InlinableNative::IntrinsicGuardToSharedArrayBuffer:
case InlinableNative::IntrinsicIsTypedArrayConstructor:
case InlinableNative::IntrinsicIsTypedArray:
case InlinableNative::IntrinsicIsPossiblyWrappedTypedArray:
case InlinableNative::IntrinsicPossiblyWrappedTypedArrayLength:
case InlinableNative::IntrinsicTypedArrayLength:
case InlinableNative::IntrinsicTypedArrayByteOffset:
case InlinableNative::IntrinsicTypedArrayElementShift:
case InlinableNative::IntrinsicSetDisjointTypedElements:
case InlinableNative::IntrinsicObjectIsTypedObject:
case InlinableNative::IntrinsicObjectIsTransparentTypedObject:
case InlinableNative::IntrinsicObjectIsOpaqueTypedObject:
case InlinableNative::IntrinsicObjectIsTypeDescr:
case InlinableNative::IntrinsicTypeDescrIsSimpleType:
case InlinableNative::IntrinsicTypeDescrIsArrayType:
case InlinableNative::IntrinsicSetTypedObjectOffset:
case InlinableNative::IntrinsicArrayIteratorPrototypeOptimizable:
MOZ_CRASH("Unexpected cross-realm intrinsic call");
case InlinableNative::TestBailout:
case InlinableNative::TestAssertFloat32:
case InlinableNative::TestAssertRecoveredOnBailout:
return false;
case InlinableNative::ArrayIsArray:
case InlinableNative::ArrayJoin:
case InlinableNative::ArrayPop:
case InlinableNative::ArrayShift:
case InlinableNative::ArrayPush:
case InlinableNative::ArraySlice:
case InlinableNative::AtomicsCompareExchange:
case InlinableNative::AtomicsExchange:
case InlinableNative::AtomicsLoad:
case InlinableNative::AtomicsStore:
case InlinableNative::AtomicsAdd:
case InlinableNative::AtomicsSub:
case InlinableNative::AtomicsAnd:
case InlinableNative::AtomicsOr:
case InlinableNative::AtomicsXor:
case InlinableNative::AtomicsIsLockFree:
case InlinableNative::ReflectGetPrototypeOf:
case InlinableNative::String:
case InlinableNative::StringCharCodeAt:
case InlinableNative::StringFromCharCode:
case InlinableNative::StringFromCodePoint:
case InlinableNative::StringCharAt:
case InlinableNative::StringToLowerCase:
case InlinableNative::StringToUpperCase:
case InlinableNative::Object:
case InlinableNative::ObjectCreate:
case InlinableNative::ObjectIs:
case InlinableNative::ObjectToString:
case InlinableNative::TypedArrayConstructor:
return false;
case InlinableNative::Limit:
break;
}
MOZ_CRASH("Unknown native");
}
IonBuilder::InliningResult IonBuilder::inlineNativeCall(CallInfo& callInfo,
JSFunction* target) {
MOZ_ASSERT(target->isNative());
if (!optimizationInfo().inlineNative()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineDisabledIon);
return InliningStatus_NotInlined;
}
bool isWasmCall = target->isWasmOptimized();
if (!isWasmCall &&
(!target->hasJitInfo() ||
target->jitInfo()->type() != JSJitInfo::InlinableNative)) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeNoSpecialization);
return InliningStatus_NotInlined;
}
if (callInfo.constructing() && callInfo.getNewTarget() != callInfo.fun()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineUnexpectedNewTarget);
return InliningStatus_NotInlined;
}
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadType);
if (shouldAbortOnPreliminaryGroups(callInfo.thisArg())) {
return InliningStatus_NotInlined;
}
for (size_t i = 0; i < callInfo.argc(); i++) {
if (shouldAbortOnPreliminaryGroups(callInfo.getArg(i))) {
return InliningStatus_NotInlined;
}
}
if (isWasmCall) {
return inlineWasmCall(callInfo, target);
}
InlinableNative inlNative = target->jitInfo()->inlinableNative;
if (target->realm() != script()->realm() && !CanInlineCrossRealm(inlNative)) {
trackOptimizationOutcome(TrackedOutcome::CantInlineCrossRealm);
return InliningStatus_NotInlined;
}
switch (inlNative) {
case InlinableNative::Array:
return inlineArray(callInfo, target->realm());
case InlinableNative::ArrayIsArray:
return inlineArrayIsArray(callInfo);
case InlinableNative::ArrayJoin:
return inlineArrayJoin(callInfo);
case InlinableNative::ArrayPop:
return inlineArrayPopShift(callInfo, MArrayPopShift::Pop);
case InlinableNative::ArrayShift:
return inlineArrayPopShift(callInfo, MArrayPopShift::Shift);
case InlinableNative::ArrayPush:
return inlineArrayPush(callInfo);
case InlinableNative::ArraySlice:
return inlineArraySlice(callInfo);
case InlinableNative::IntrinsicNewArrayIterator:
return inlineNewIterator(callInfo, MNewIterator::ArrayIterator);
case InlinableNative::IntrinsicArrayIteratorPrototypeOptimizable:
return inlineArrayIteratorPrototypeOptimizable(callInfo);
case InlinableNative::AtomicsCompareExchange:
return inlineAtomicsCompareExchange(callInfo);
case InlinableNative::AtomicsExchange:
return inlineAtomicsExchange(callInfo);
case InlinableNative::AtomicsLoad:
return inlineAtomicsLoad(callInfo);
case InlinableNative::AtomicsStore:
return inlineAtomicsStore(callInfo);
case InlinableNative::AtomicsAdd:
case InlinableNative::AtomicsSub:
case InlinableNative::AtomicsAnd:
case InlinableNative::AtomicsOr:
case InlinableNative::AtomicsXor:
return inlineAtomicsBinop(callInfo, inlNative);
case InlinableNative::AtomicsIsLockFree:
return inlineAtomicsIsLockFree(callInfo);
case InlinableNative::Boolean:
return inlineBoolean(callInfo);
case InlinableNative::IntlGuardToCollator:
return inlineGuardToClass(callInfo, &CollatorObject::class_);
case InlinableNative::IntlGuardToDateTimeFormat:
return inlineGuardToClass(callInfo, &DateTimeFormatObject::class_);
case InlinableNative::IntlGuardToNumberFormat:
return inlineGuardToClass(callInfo, &NumberFormatObject::class_);
case InlinableNative::IntlGuardToPluralRules:
return inlineGuardToClass(callInfo, &PluralRulesObject::class_);
case InlinableNative::IntlGuardToRelativeTimeFormat:
return inlineGuardToClass(callInfo, &RelativeTimeFormatObject::class_);
case InlinableNative::MathAbs:
return inlineMathAbs(callInfo);
case InlinableNative::MathFloor:
return inlineMathFloor(callInfo);
case InlinableNative::MathCeil:
return inlineMathCeil(callInfo);
case InlinableNative::MathRound:
return inlineMathRound(callInfo);
case InlinableNative::MathClz32:
return inlineMathClz32(callInfo);
case InlinableNative::MathSqrt:
return inlineMathSqrt(callInfo);
case InlinableNative::MathATan2:
return inlineMathAtan2(callInfo);
case InlinableNative::MathHypot:
return inlineMathHypot(callInfo);
case InlinableNative::MathMax:
return inlineMathMinMax(callInfo, true );
case InlinableNative::MathMin:
return inlineMathMinMax(callInfo, false );
case InlinableNative::MathPow:
return inlineMathPow(callInfo);
case InlinableNative::MathRandom:
return inlineMathRandom(callInfo);
case InlinableNative::MathImul:
return inlineMathImul(callInfo);
case InlinableNative::MathFRound:
return inlineMathFRound(callInfo);
case InlinableNative::MathTrunc:
return inlineMathTrunc(callInfo);
case InlinableNative::MathSign:
return inlineMathSign(callInfo);
case InlinableNative::MathSin:
return inlineMathFunction(callInfo, MMathFunction::Sin);
case InlinableNative::MathTan:
return inlineMathFunction(callInfo, MMathFunction::Tan);
case InlinableNative::MathCos:
return inlineMathFunction(callInfo, MMathFunction::Cos);
case InlinableNative::MathExp:
return inlineMathFunction(callInfo, MMathFunction::Exp);
case InlinableNative::MathLog:
return inlineMathFunction(callInfo, MMathFunction::Log);
case InlinableNative::MathASin:
return inlineMathFunction(callInfo, MMathFunction::ASin);
case InlinableNative::MathATan:
return inlineMathFunction(callInfo, MMathFunction::ATan);
case InlinableNative::MathACos:
return inlineMathFunction(callInfo, MMathFunction::ACos);
case InlinableNative::MathLog10:
return inlineMathFunction(callInfo, MMathFunction::Log10);
case InlinableNative::MathLog2:
return inlineMathFunction(callInfo, MMathFunction::Log2);
case InlinableNative::MathLog1P:
return inlineMathFunction(callInfo, MMathFunction::Log1P);
case InlinableNative::MathExpM1:
return inlineMathFunction(callInfo, MMathFunction::ExpM1);
case InlinableNative::MathCosH:
return inlineMathFunction(callInfo, MMathFunction::CosH);
case InlinableNative::MathSinH:
return inlineMathFunction(callInfo, MMathFunction::SinH);
case InlinableNative::MathTanH:
return inlineMathFunction(callInfo, MMathFunction::TanH);
case InlinableNative::MathACosH:
return inlineMathFunction(callInfo, MMathFunction::ACosH);
case InlinableNative::MathASinH:
return inlineMathFunction(callInfo, MMathFunction::ASinH);
case InlinableNative::MathATanH:
return inlineMathFunction(callInfo, MMathFunction::ATanH);
case InlinableNative::MathCbrt:
return inlineMathFunction(callInfo, MMathFunction::Cbrt);
case InlinableNative::ReflectGetPrototypeOf:
return inlineReflectGetPrototypeOf(callInfo);
case InlinableNative::RegExpMatcher:
return inlineRegExpMatcher(callInfo);
case InlinableNative::RegExpSearcher:
return inlineRegExpSearcher(callInfo);
case InlinableNative::RegExpTester:
return inlineRegExpTester(callInfo);
case InlinableNative::IsRegExpObject:
return inlineIsRegExpObject(callInfo);
case InlinableNative::RegExpPrototypeOptimizable:
return inlineRegExpPrototypeOptimizable(callInfo);
case InlinableNative::RegExpInstanceOptimizable:
return inlineRegExpInstanceOptimizable(callInfo);
case InlinableNative::GetFirstDollarIndex:
return inlineGetFirstDollarIndex(callInfo);
case InlinableNative::IntrinsicNewRegExpStringIterator:
return inlineNewIterator(callInfo, MNewIterator::RegExpStringIterator);
case InlinableNative::String:
return inlineStringObject(callInfo);
case InlinableNative::StringCharCodeAt:
return inlineStrCharCodeAt(callInfo);
case InlinableNative::StringFromCharCode:
return inlineStrFromCharCode(callInfo);
case InlinableNative::StringFromCodePoint:
return inlineStrFromCodePoint(callInfo);
case InlinableNative::StringCharAt:
return inlineStrCharAt(callInfo);
case InlinableNative::StringToLowerCase:
return inlineStringConvertCase(callInfo, MStringConvertCase::LowerCase);
case InlinableNative::StringToUpperCase:
return inlineStringConvertCase(callInfo, MStringConvertCase::UpperCase);
case InlinableNative::IntrinsicStringReplaceString:
return inlineStringReplaceString(callInfo);
case InlinableNative::IntrinsicStringSplitString:
return inlineStringSplitString(callInfo);
case InlinableNative::IntrinsicNewStringIterator:
return inlineNewIterator(callInfo, MNewIterator::StringIterator);
case InlinableNative::Object:
return inlineObject(callInfo);
case InlinableNative::ObjectCreate:
return inlineObjectCreate(callInfo);
case InlinableNative::ObjectIs:
return inlineObjectIs(callInfo);
case InlinableNative::ObjectToString:
return inlineObjectToString(callInfo);
case InlinableNative::TestBailout:
return inlineBailout(callInfo);
case InlinableNative::TestAssertFloat32:
return inlineAssertFloat32(callInfo);
case InlinableNative::TestAssertRecoveredOnBailout:
return inlineAssertRecoveredOnBailout(callInfo);
case InlinableNative::IntrinsicUnsafeSetReservedSlot:
return inlineUnsafeSetReservedSlot(callInfo);
case InlinableNative::IntrinsicUnsafeGetReservedSlot:
return inlineUnsafeGetReservedSlot(callInfo, MIRType::Value);
case InlinableNative::IntrinsicUnsafeGetObjectFromReservedSlot:
return inlineUnsafeGetReservedSlot(callInfo, MIRType::Object);
case InlinableNative::IntrinsicUnsafeGetInt32FromReservedSlot:
return inlineUnsafeGetReservedSlot(callInfo, MIRType::Int32);
case InlinableNative::IntrinsicUnsafeGetStringFromReservedSlot:
return inlineUnsafeGetReservedSlot(callInfo, MIRType::String);
case InlinableNative::IntrinsicUnsafeGetBooleanFromReservedSlot:
return inlineUnsafeGetReservedSlot(callInfo, MIRType::Boolean);
case InlinableNative::IntrinsicIsCallable:
return inlineIsCallable(callInfo);
case InlinableNative::IntrinsicIsConstructor:
return inlineIsConstructor(callInfo);
case InlinableNative::IntrinsicToObject:
return inlineToObject(callInfo);
case InlinableNative::IntrinsicIsObject:
return inlineIsObject(callInfo);
case InlinableNative::IntrinsicIsCrossRealmArrayConstructor:
return inlineIsCrossRealmArrayConstructor(callInfo);
case InlinableNative::IntrinsicToInteger:
return inlineToInteger(callInfo);
case InlinableNative::IntrinsicToString:
return inlineToString(callInfo);
case InlinableNative::IntrinsicIsConstructing:
return inlineIsConstructing(callInfo);
case InlinableNative::IntrinsicSubstringKernel:
return inlineSubstringKernel(callInfo);
case InlinableNative::IntrinsicGuardToArrayIterator:
return inlineGuardToClass(callInfo, &ArrayIteratorObject::class_);
case InlinableNative::IntrinsicGuardToMapIterator:
return inlineGuardToClass(callInfo, &MapIteratorObject::class_);
case InlinableNative::IntrinsicGuardToSetIterator:
return inlineGuardToClass(callInfo, &SetIteratorObject::class_);
case InlinableNative::IntrinsicGuardToStringIterator:
return inlineGuardToClass(callInfo, &StringIteratorObject::class_);
case InlinableNative::IntrinsicGuardToRegExpStringIterator:
return inlineGuardToClass(callInfo, &RegExpStringIteratorObject::class_);
case InlinableNative::IntrinsicObjectHasPrototype:
return inlineObjectHasPrototype(callInfo);
case InlinableNative::IntrinsicFinishBoundFunctionInit:
return inlineFinishBoundFunctionInit(callInfo);
case InlinableNative::IntrinsicIsPackedArray:
return inlineIsPackedArray(callInfo);
case InlinableNative::IntrinsicGuardToMapObject:
return inlineGuardToClass(callInfo, &MapObject::class_);
case InlinableNative::IntrinsicGetNextMapEntryForIterator:
return inlineGetNextEntryForIterator(callInfo,
MGetNextEntryForIterator::Map);
case InlinableNative::IntrinsicGuardToSetObject:
return inlineGuardToClass(callInfo, &SetObject::class_);
case InlinableNative::IntrinsicGetNextSetEntryForIterator:
return inlineGetNextEntryForIterator(callInfo,
MGetNextEntryForIterator::Set);
case InlinableNative::IntrinsicGuardToArrayBuffer:
return inlineGuardToClass(callInfo, &ArrayBufferObject::class_);
case InlinableNative::IntrinsicArrayBufferByteLength:
return inlineArrayBufferByteLength(callInfo);
case InlinableNative::IntrinsicPossiblyWrappedArrayBufferByteLength:
return inlinePossiblyWrappedArrayBufferByteLength(callInfo);
case InlinableNative::IntrinsicGuardToSharedArrayBuffer:
return inlineGuardToClass(callInfo, &SharedArrayBufferObject::class_);
case InlinableNative::TypedArrayConstructor:
return inlineTypedArray(callInfo, target->native());
case InlinableNative::IntrinsicIsTypedArrayConstructor:
return inlineIsTypedArrayConstructor(callInfo);
case InlinableNative::IntrinsicIsTypedArray:
return inlineIsTypedArray(callInfo);
case InlinableNative::IntrinsicIsPossiblyWrappedTypedArray:
return inlineIsPossiblyWrappedTypedArray(callInfo);
case InlinableNative::IntrinsicPossiblyWrappedTypedArrayLength:
return inlinePossiblyWrappedTypedArrayLength(callInfo);
case InlinableNative::IntrinsicTypedArrayLength:
return inlineTypedArrayLength(callInfo);
case InlinableNative::IntrinsicTypedArrayByteOffset:
return inlineTypedArrayByteOffset(callInfo);
case InlinableNative::IntrinsicTypedArrayElementShift:
return inlineTypedArrayElementShift(callInfo);
case InlinableNative::IntrinsicSetDisjointTypedElements:
return inlineSetDisjointTypedElements(callInfo);
case InlinableNative::IntrinsicObjectIsTypedObject:
return inlineHasClass(callInfo, &OutlineTransparentTypedObject::class_,
&OutlineOpaqueTypedObject::class_,
&InlineTransparentTypedObject::class_,
&InlineOpaqueTypedObject::class_);
case InlinableNative::IntrinsicObjectIsTransparentTypedObject:
return inlineHasClass(callInfo, &OutlineTransparentTypedObject::class_,
&InlineTransparentTypedObject::class_);
case InlinableNative::IntrinsicObjectIsOpaqueTypedObject:
return inlineHasClass(callInfo, &OutlineOpaqueTypedObject::class_,
&InlineOpaqueTypedObject::class_);
case InlinableNative::IntrinsicObjectIsTypeDescr:
return inlineObjectIsTypeDescr(callInfo);
case InlinableNative::IntrinsicTypeDescrIsSimpleType:
return inlineHasClass(callInfo, &ScalarTypeDescr::class_,
&ReferenceTypeDescr::class_);
case InlinableNative::IntrinsicTypeDescrIsArrayType:
return inlineHasClass(callInfo, &ArrayTypeDescr::class_);
case InlinableNative::IntrinsicSetTypedObjectOffset:
return inlineSetTypedObjectOffset(callInfo);
case InlinableNative::Limit:
break;
}
MOZ_CRASH("Shouldn't get here");
}
IonBuilder::InliningResult IonBuilder::inlineNativeGetter(CallInfo& callInfo,
JSFunction* target) {
MOZ_ASSERT(target->isNative());
JSNative native = target->native();
if (!optimizationInfo().inlineNative()) {
return InliningStatus_NotInlined;
}
MDefinition* thisArg = callInfo.thisArg();
TemporaryTypeSet* thisTypes = thisArg->resultTypeSet();
MOZ_ASSERT(callInfo.argc() == 0);
if (!thisTypes) {
return InliningStatus_NotInlined;
}
if (TypedArrayObject::isOriginalLengthGetter(native)) {
if (thisTypes->forAllClasses(constraints(), IsTypedArrayClass) !=
TemporaryTypeSet::ForAllResult::ALL_TRUE) {
return InliningStatus_NotInlined;
}
MInstruction* length = addTypedArrayLength(thisArg);
current->push(length);
return InliningStatus_Inlined;
}
if (TypedArrayObject::isOriginalByteOffsetGetter(native)) {
if (thisTypes->forAllClasses(constraints(), IsTypedArrayClass) !=
TemporaryTypeSet::ForAllResult::ALL_TRUE) {
return InliningStatus_NotInlined;
}
MInstruction* byteOffset = addTypedArrayByteOffset(thisArg);
current->push(byteOffset);
return InliningStatus_Inlined;
}
RegExpFlag mask = NoFlags;
if (RegExpObject::isOriginalFlagGetter(native, &mask)) {
const Class* clasp = thisTypes->getKnownClass(constraints());
if (clasp != &RegExpObject::class_) {
return InliningStatus_NotInlined;
}
MLoadFixedSlot* flags =
MLoadFixedSlot::New(alloc(), thisArg, RegExpObject::flagsSlot());
current->add(flags);
flags->setResultType(MIRType::Int32);
MConstant* maskConst = MConstant::New(alloc(), Int32Value(mask));
current->add(maskConst);
MBitAnd* maskedFlag = MBitAnd::New(alloc(), flags, maskConst);
maskedFlag->setInt32Specialization();
current->add(maskedFlag);
MDefinition* result = convertToBoolean(maskedFlag);
current->push(result);
return InliningStatus_Inlined;
}
return InliningStatus_NotInlined;
}
IonBuilder::InliningResult IonBuilder::inlineNonFunctionCall(CallInfo& callInfo,
JSObject* target) {
if (callInfo.constructing() && callInfo.getNewTarget() != callInfo.fun()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineUnexpectedNewTarget);
return InliningStatus_NotInlined;
}
Realm* targetRealm = JS::GetObjectRealmOrNull(target);
if (!targetRealm || targetRealm != script()->realm()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineCrossRealm);
return InliningStatus_NotInlined;
}
if (callInfo.constructing() &&
target->constructHook() == TypedObject::construct) {
return inlineConstructTypedObject(callInfo, &target->as<TypeDescr>());
}
return InliningStatus_NotInlined;
}
TemporaryTypeSet* IonBuilder::getInlineReturnTypeSet() {
return bytecodeTypes(pc);
}
MIRType IonBuilder::getInlineReturnType() {
TemporaryTypeSet* returnTypes = getInlineReturnTypeSet();
return returnTypes->getKnownMIRType();
}
IonBuilder::InliningResult IonBuilder::inlineMathFunction(
CallInfo& callInfo, MMathFunction::Function function) {
if (callInfo.constructing()) {
return InliningStatus_NotInlined;
}
if (callInfo.argc() != 1) {
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Double) {
return InliningStatus_NotInlined;
}
if (!IsNumberType(callInfo.getArg(0)->type())) {
return InliningStatus_NotInlined;
}
callInfo.fun()->setImplicitlyUsedUnchecked();
callInfo.thisArg()->setImplicitlyUsedUnchecked();
MMathFunction* ins =
MMathFunction::New(alloc(), callInfo.getArg(0), function);
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineArray(CallInfo& callInfo,
Realm* targetRealm) {
uint32_t initLength = 0;
JSObject* templateObject =
inspector->getTemplateObjectForNative(pc, ArrayConstructor);
if (!templateObject) {
templateObject = inspector->getTemplateObjectForNative(pc, array_construct);
}
if (!templateObject) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeNoTemplateObj);
return InliningStatus_NotInlined;
}
if (templateObject->nonCCWRealm() != targetRealm) {
trackOptimizationOutcome(TrackedOutcome::CantInlineCrossRealm);
return InliningStatus_NotInlined;
}
if (callInfo.argc() >= 2) {
initLength = callInfo.argc();
TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(templateObject);
if (!key->unknownProperties()) {
HeapTypeSetKey elemTypes = key->property(JSID_VOID);
for (uint32_t i = 0; i < initLength; i++) {
MDefinition* value = callInfo.getArg(i);
if (!TypeSetIncludes(elemTypes.maybeTypes(), value->type(),
value->resultTypeSet())) {
elemTypes.freeze(constraints());
return InliningStatus_NotInlined;
}
}
}
}
if (callInfo.argc() == 1) {
MDefinition* arg = callInfo.getArg(0);
if (arg->type() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
if (!arg->isConstant()) {
callInfo.setImplicitlyUsedUnchecked();
MNewArrayDynamicLength* ins = MNewArrayDynamicLength::New(
alloc(), constraints(), templateObject,
templateObject->group()->initialHeap(constraints()), arg);
current->add(ins);
current->push(ins);
MOZ_TRY(resumeAfter(ins));
return InliningStatus_Inlined;
}
trackOptimizationOutcome(TrackedOutcome::ArrayRange);
initLength = arg->toConstant()->toInt32();
if (initLength > NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
return InliningStatus_NotInlined;
}
MOZ_ASSERT(initLength <= INT32_MAX);
if (initLength != templateObject->as<ArrayObject>().length()) {
return InliningStatus_NotInlined;
}
if (initLength > ArrayObject::EagerAllocationMaxLength) {
return InliningStatus_NotInlined;
}
}
callInfo.setImplicitlyUsedUnchecked();
MOZ_TRY(jsop_newarray(templateObject, initLength));
MDefinition* array = current->peek(-1);
if (callInfo.argc() >= 2) {
for (uint32_t i = 0; i < initLength; i++) {
if (!alloc().ensureBallast()) {
return abort(AbortReason::Alloc);
}
MDefinition* value = callInfo.getArg(i);
MOZ_TRY(initializeArrayElement(array, i, value,
false));
}
MInstruction* setLength = setInitializedLength(array, initLength);
MOZ_TRY(resumeAfter(setLength));
}
return InliningStatus_Inlined;
}
static bool IsArrayClass(const Class* clasp) {
return clasp == &ArrayObject::class_;
}
IonBuilder::InliningResult IonBuilder::inlineArrayIsArray(CallInfo& callInfo) {
if (callInfo.constructing() || callInfo.argc() != 1) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Boolean) {
return InliningStatus_NotInlined;
}
MDefinition* arg = callInfo.getArg(0);
if (!arg->mightBeType(MIRType::Object)) {
pushConstant(BooleanValue(false));
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
using ForAllResult = TemporaryTypeSet::ForAllResult;
TemporaryTypeSet* types = arg->resultTypeSet();
if (arg->type() == MIRType::Object && types &&
types->forAllClasses(constraints(), IsProxyClass) ==
ForAllResult::ALL_FALSE) {
ForAllResult result = types->forAllClasses(constraints(), IsArrayClass);
if (result == ForAllResult::ALL_FALSE || result == ForAllResult::ALL_TRUE) {
pushConstant(BooleanValue(result == ForAllResult::ALL_TRUE));
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
MOZ_ASSERT(result == ForAllResult::MIXED);
MHasClass* hasClass = MHasClass::New(alloc(), arg, &ArrayObject::class_);
current->add(hasClass);
current->push(hasClass);
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
MIsArray* isArray = MIsArray::New(alloc(), arg);
current->add(isArray);
current->push(isArray);
MOZ_TRY(resumeAfter(isArray));
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineArrayPopShift(
CallInfo& callInfo, MArrayPopShift::Mode mode) {
if (callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
MIRType returnType = getInlineReturnType();
if (returnType == MIRType::Undefined || returnType == MIRType::Null) {
return InliningStatus_NotInlined;
}
if (callInfo.thisArg()->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
ObjectGroupFlags unhandledFlags =
OBJECT_FLAG_SPARSE_INDEXES | OBJECT_FLAG_LENGTH_OVERFLOW |
OBJECT_FLAG_ITERATED | OBJECT_FLAG_NON_EXTENSIBLE_ELEMENTS;
MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
TemporaryTypeSet* thisTypes = obj->resultTypeSet();
if (!thisTypes) {
return InliningStatus_NotInlined;
}
const Class* clasp = thisTypes->getKnownClass(constraints());
if (clasp != &ArrayObject::class_) {
return InliningStatus_NotInlined;
}
if (thisTypes->hasObjectFlags(constraints(), unhandledFlags)) {
trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
return InliningStatus_NotInlined;
}
bool hasIndexedProperty;
MOZ_TRY_VAR(hasIndexedProperty,
ArrayPrototypeHasIndexedProperty(this, script()));
if (hasIndexedProperty) {
trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
obj = addMaybeCopyElementsForWrite(obj, false);
TemporaryTypeSet* returnTypes = getInlineReturnTypeSet();
bool needsHoleCheck =
thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED);
bool maybeUndefined = returnTypes->hasType(TypeSet::UndefinedType());
BarrierKind barrier = PropertyReadNeedsTypeBarrier(
analysisContext, alloc(), constraints(), obj, nullptr, returnTypes);
if (barrier != BarrierKind::NoBarrier) {
returnType = MIRType::Value;
}
MArrayPopShift* ins =
MArrayPopShift::New(alloc(), obj, mode, needsHoleCheck, maybeUndefined);
current->add(ins);
current->push(ins);
ins->setResultType(returnType);
MOZ_TRY(resumeAfter(ins));
MOZ_TRY(pushTypeBarrier(ins, returnTypes, barrier));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineArrayJoin(CallInfo& callInfo) {
if (callInfo.argc() != 1 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::String) {
return InliningStatus_NotInlined;
}
if (callInfo.thisArg()->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (callInfo.getArg(0)->type() != MIRType::String) {
return InliningStatus_NotInlined;
}
bool optimizeForArray = ([&]() {
TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
if (!thisTypes) {
return false;
}
const Class* clasp = thisTypes->getKnownClass(constraints());
if (clasp != &ArrayObject::class_) {
return false;
}
return true;
})();
callInfo.setImplicitlyUsedUnchecked();
MArrayJoin* ins = MArrayJoin::New(alloc(), callInfo.thisArg(),
callInfo.getArg(0), optimizeForArray);
current->add(ins);
current->push(ins);
MOZ_TRY(resumeAfter(ins));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineArrayPush(CallInfo& callInfo) {
const uint32_t inlineArgsLimit = 10;
if (callInfo.argc() < 1 || callInfo.argc() > inlineArgsLimit ||
callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
if (callInfo.argc() != 1) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
for (uint32_t i = 0; i < callInfo.argc(); i++) {
MDefinition* value = callInfo.getArg(i);
if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj,
nullptr, &value,
false)) {
trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
return InliningStatus_NotInlined;
}
}
if (getInlineReturnType() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
if (obj->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
TemporaryTypeSet* thisTypes = obj->resultTypeSet();
if (!thisTypes) {
return InliningStatus_NotInlined;
}
const Class* clasp = thisTypes->getKnownClass(constraints());
if (clasp != &ArrayObject::class_) {
return InliningStatus_NotInlined;
}
if (thisTypes->hasObjectFlags(
constraints(),
OBJECT_FLAG_SPARSE_INDEXES | OBJECT_FLAG_LENGTH_OVERFLOW)) {
trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
return InliningStatus_NotInlined;
}
bool hasIndexedProperty;
MOZ_TRY_VAR(hasIndexedProperty,
ArrayPrototypeHasIndexedProperty(this, script()));
if (hasIndexedProperty) {
trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
return InliningStatus_NotInlined;
}
TemporaryTypeSet::DoubleConversion conversion =
thisTypes->convertDoubleElements(constraints());
if (conversion == TemporaryTypeSet::AmbiguousDoubleConversion) {
trackOptimizationOutcome(TrackedOutcome::ArrayDoubleConversion);
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
bool toDouble = conversion == TemporaryTypeSet::AlwaysConvertToDoubles ||
conversion == TemporaryTypeSet::MaybeConvertToDoubles;
obj = addMaybeCopyElementsForWrite(obj, false);
MResumePoint* lastRp = nullptr;
MInstruction* truncate = nullptr;
if (callInfo.argc() > 1) {
MInstruction* elements = MElements::New(alloc(), obj);
MInstruction* length = MArrayLength::New(alloc(), elements);
truncate = MSetArrayLength::New(alloc(), obj, length);
truncate->setRecoveredOnBailout();
current->add(elements);
current->add(length);
current->add(truncate);
MOZ_TRY(callInfo.pushPriorCallStack(this, current));
}
MInstruction* ins = nullptr;
for (uint32_t i = 0; i < callInfo.argc(); i++) {
MDefinition* value = callInfo.getArg(i);
if (toDouble) {
MInstruction* valueDouble = MToDouble::New(alloc(), value);
current->add(valueDouble);
value = valueDouble;
}
if (needsPostBarrier(value)) {
MInstruction* elements = MElements::New(alloc(), obj);
current->add(elements);
MInstruction* initLength = MInitializedLength::New(alloc(), elements);
current->add(initLength);
current->add(
MPostWriteElementBarrier::New(alloc(), obj, value, initLength));
}
ins = MArrayPush::New(alloc(), obj, value);
current->add(ins);
if (callInfo.argc() > 1) {
MOZ_TRY(resumeAt(ins, pc));
ins->resumePoint()->addStore(alloc(), truncate, lastRp);
lastRp = ins->resumePoint();
}
}
if (callInfo.argc() > 1) {
callInfo.popPriorCallStack(current);
}
current->push(ins);
if (callInfo.argc() > 1) {
ins = MNop::New(alloc());
current->add(ins);
}
MOZ_TRY(resumeAfter(ins));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineArraySlice(CallInfo& callInfo) {
if (callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
if (getInlineReturnType() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (obj->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (callInfo.argc() > 0) {
if (callInfo.getArg(0)->type() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
if (callInfo.argc() > 1) {
if (callInfo.getArg(1)->type() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
}
}
TemporaryTypeSet* thisTypes = obj->resultTypeSet();
if (!thisTypes) {
return InliningStatus_NotInlined;
}
const Class* clasp = thisTypes->getKnownClass(constraints());
if (clasp != &ArrayObject::class_) {
return InliningStatus_NotInlined;
}
if (thisTypes->hasObjectFlags(
constraints(),
OBJECT_FLAG_SPARSE_INDEXES | OBJECT_FLAG_LENGTH_OVERFLOW)) {
trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
return InliningStatus_NotInlined;
}
bool hasIndexedProperty;
MOZ_TRY_VAR(hasIndexedProperty,
ArrayPrototypeHasIndexedProperty(this, script()));
if (hasIndexedProperty) {
trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
return InliningStatus_NotInlined;
}
for (unsigned i = 0; i < thisTypes->getObjectCount(); i++) {
TypeSet::ObjectKey* key = thisTypes->getObject(i);
if (key && key->isSingleton()) {
return InliningStatus_NotInlined;
}
}
JSObject* templateObj =
inspector->getTemplateObjectForNative(pc, js::array_slice);
if (!templateObj) {
return InliningStatus_NotInlined;
}
if (!templateObj->is<ArrayObject>()) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MDefinition* begin;
if (callInfo.argc() > 0) {
begin = callInfo.getArg(0);
} else {
begin = constant(Int32Value(0));
}
MDefinition* end;
if (callInfo.argc() > 1) {
end = callInfo.getArg(1);
} else if (clasp == &ArrayObject::class_) {
MElements* elements = MElements::New(alloc(), obj);
current->add(elements);
end = MArrayLength::New(alloc(), elements);
current->add(end->toInstruction());
}
MArraySlice* ins =
MArraySlice::New(alloc(), obj, begin, end, templateObj,
templateObj->group()->initialHeap(constraints()));
current->add(ins);
current->push(ins);
MOZ_TRY(resumeAfter(ins));
MOZ_TRY(pushTypeBarrier(ins, getInlineReturnTypeSet(), BarrierKind::TypeSet));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineBoolean(CallInfo& callInfo) {
if (callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Boolean) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
if (callInfo.argc() > 0) {
MDefinition* result = convertToBoolean(callInfo.getArg(0));
current->push(result);
} else {
pushConstant(BooleanValue(false));
}
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineNewIterator(
CallInfo& callInfo, MNewIterator::Type type) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 0);
JSObject* templateObject = nullptr;
switch (type) {
case MNewIterator::ArrayIterator:
templateObject = inspector->getTemplateObjectForNative(
pc, js::intrinsic_NewArrayIterator);
MOZ_ASSERT_IF(templateObject, templateObject->is<ArrayIteratorObject>());
break;
case MNewIterator::StringIterator:
templateObject = inspector->getTemplateObjectForNative(
pc, js::intrinsic_NewStringIterator);
MOZ_ASSERT_IF(templateObject, templateObject->is<StringIteratorObject>());
break;
case MNewIterator::RegExpStringIterator:
templateObject = inspector->getTemplateObjectForNative(
pc, js::intrinsic_NewRegExpStringIterator);
MOZ_ASSERT_IF(templateObject,
templateObject->is<RegExpStringIteratorObject>());
break;
}
if (!templateObject) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MConstant* templateConst =
MConstant::NewConstraintlessObject(alloc(), templateObject);
current->add(templateConst);
MNewIterator* ins =
MNewIterator::New(alloc(), constraints(), templateConst, type);
current->add(ins);
current->push(ins);
MOZ_TRY(resumeAfter(ins));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineArrayIteratorPrototypeOptimizable(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 0);
if (!ensureArrayIteratorPrototypeNextNotModified()) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
pushConstant(BooleanValue(true));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineMathAbs(CallInfo& callInfo) {
if (callInfo.argc() != 1 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
MIRType returnType = getInlineReturnType();
MIRType argType = callInfo.getArg(0)->type();
if (!IsNumberType(argType)) {
return InliningStatus_NotInlined;
}
if (argType != returnType &&
!(IsFloatingPointType(argType) && returnType == MIRType::Int32) &&
!(argType == MIRType::Float32 && returnType == MIRType::Double)) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MIRType absType = (argType == MIRType::Float32) ? MIRType::Double : argType;
MInstruction* ins = MAbs::New(alloc(), callInfo.getArg(0), absType);
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineMathFloor(CallInfo& callInfo) {
if (callInfo.argc() != 1 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
MIRType argType = callInfo.getArg(0)->type();
MIRType returnType = getInlineReturnType();
if (argType == MIRType::Int32 && returnType == MIRType::Int32) {
callInfo.setImplicitlyUsedUnchecked();
MLimitedTruncate* ins = MLimitedTruncate::New(
alloc(), callInfo.getArg(0), MDefinition::IndirectTruncate);
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
if (IsFloatingPointType(argType)) {
if (returnType == MIRType::Int32) {
callInfo.setImplicitlyUsedUnchecked();
MFloor* ins = MFloor::New(alloc(), callInfo.getArg(0));
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
if (returnType == MIRType::Double) {
callInfo.setImplicitlyUsedUnchecked();
MInstruction* ins = nullptr;
if (MNearbyInt::HasAssemblerSupport(RoundingMode::Down)) {
ins = MNearbyInt::New(alloc(), callInfo.getArg(0), argType,
RoundingMode::Down);
} else {
ins = MMathFunction::New(alloc(), callInfo.getArg(0),
MMathFunction::Floor);
}
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
}
return InliningStatus_NotInlined;
}
IonBuilder::InliningResult IonBuilder::inlineMathCeil(CallInfo& callInfo) {
if (callInfo.argc() != 1 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
MIRType argType = callInfo.getArg(0)->type();
MIRType returnType = getInlineReturnType();
if (argType == MIRType::Int32 && returnType == MIRType::Int32) {
callInfo.setImplicitlyUsedUnchecked();
MLimitedTruncate* ins = MLimitedTruncate::New(
alloc(), callInfo.getArg(0), MDefinition::IndirectTruncate);
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
if (IsFloatingPointType(argType)) {
if (returnType == MIRType::Int32) {
callInfo.setImplicitlyUsedUnchecked();
MCeil* ins = MCeil::New(alloc(), callInfo.getArg(0));
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
if (returnType == MIRType::Double) {
callInfo.setImplicitlyUsedUnchecked();
MInstruction* ins = nullptr;
if (MNearbyInt::HasAssemblerSupport(RoundingMode::Up)) {
ins = MNearbyInt::New(alloc(), callInfo.getArg(0), argType,
RoundingMode::Up);
} else {
ins = MMathFunction::New(alloc(), callInfo.getArg(0),
MMathFunction::Ceil);
}
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
}
return InliningStatus_NotInlined;
}
IonBuilder::InliningResult IonBuilder::inlineMathClz32(CallInfo& callInfo) {
if (callInfo.argc() != 1 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
MIRType returnType = getInlineReturnType();
if (returnType != MIRType::Int32) {
return InliningStatus_NotInlined;
}
if (!IsNumberType(callInfo.getArg(0)->type())) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MClz* ins = MClz::New(alloc(), callInfo.getArg(0), MIRType::Int32);
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineMathRound(CallInfo& callInfo) {
if (callInfo.argc() != 1 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
MIRType returnType = getInlineReturnType();
MIRType argType = callInfo.getArg(0)->type();
if (argType == MIRType::Int32 && returnType == MIRType::Int32) {
callInfo.setImplicitlyUsedUnchecked();
MLimitedTruncate* ins = MLimitedTruncate::New(
alloc(), callInfo.getArg(0), MDefinition::IndirectTruncate);
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
if (IsFloatingPointType(argType) && returnType == MIRType::Int32) {
callInfo.setImplicitlyUsedUnchecked();
MRound* ins = MRound::New(alloc(), callInfo.getArg(0));
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
if (IsFloatingPointType(argType) && returnType == MIRType::Double) {
callInfo.setImplicitlyUsedUnchecked();
MMathFunction* ins =
MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Round);
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
return InliningStatus_NotInlined;
}
IonBuilder::InliningResult IonBuilder::inlineMathSqrt(CallInfo& callInfo) {
if (callInfo.argc() != 1 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
MIRType argType = callInfo.getArg(0)->type();
if (getInlineReturnType() != MIRType::Double) {
return InliningStatus_NotInlined;
}
if (!IsNumberType(argType)) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MSqrt* sqrt = MSqrt::New(alloc(), callInfo.getArg(0), MIRType::Double);
current->add(sqrt);
current->push(sqrt);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineMathAtan2(CallInfo& callInfo) {
if (callInfo.argc() != 2 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Double) {
return InliningStatus_NotInlined;
}
MIRType argType0 = callInfo.getArg(0)->type();
MIRType argType1 = callInfo.getArg(1)->type();
if (!IsNumberType(argType0) || !IsNumberType(argType1)) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MAtan2* atan2 = MAtan2::New(alloc(), callInfo.getArg(0), callInfo.getArg(1));
current->add(atan2);
current->push(atan2);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineMathHypot(CallInfo& callInfo) {
if (callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
uint32_t argc = callInfo.argc();
if (argc < 2 || argc > 4) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Double) {
return InliningStatus_NotInlined;
}
MDefinitionVector vector(alloc());
if (!vector.reserve(argc)) {
return InliningStatus_NotInlined;
}
for (uint32_t i = 0; i < argc; ++i) {
MDefinition* arg = callInfo.getArg(i);
if (!IsNumberType(arg->type())) {
return InliningStatus_NotInlined;
}
vector.infallibleAppend(arg);
}
callInfo.setImplicitlyUsedUnchecked();
MHypot* hypot = MHypot::New(alloc(), vector);
if (!hypot) {
return InliningStatus_NotInlined;
}
current->add(hypot);
current->push(hypot);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineMathPow(CallInfo& callInfo) {
if (callInfo.argc() != 2 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
bool emitted = false;
MOZ_TRY(powTrySpecialized(&emitted, callInfo.getArg(0), callInfo.getArg(1),
getInlineReturnType()));
if (!emitted) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineMathRandom(CallInfo& callInfo) {
if (callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Double) {
return InliningStatus_NotInlined;
}
script()->realm()->getOrCreateRandomNumberGenerator();
callInfo.setImplicitlyUsedUnchecked();
MRandom* rand = MRandom::New(alloc());
current->add(rand);
current->push(rand);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineMathImul(CallInfo& callInfo) {
if (callInfo.argc() != 2 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
MIRType returnType = getInlineReturnType();
if (returnType != MIRType::Int32) {
return InliningStatus_NotInlined;
}
if (!IsNumberType(callInfo.getArg(0)->type())) {
return InliningStatus_NotInlined;
}
if (!IsNumberType(callInfo.getArg(1)->type())) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MInstruction* first = MTruncateToInt32::New(alloc(), callInfo.getArg(0));
current->add(first);
MInstruction* second = MTruncateToInt32::New(alloc(), callInfo.getArg(1));
current->add(second);
MMul* ins = MMul::New(alloc(), first, second, MIRType::Int32, MMul::Integer);
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineMathFRound(CallInfo& callInfo) {
if (callInfo.argc() != 1 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
TemporaryTypeSet* returned = getInlineReturnTypeSet();
if (returned->empty()) {
returned->addType(TypeSet::DoubleType(), alloc_->lifoAlloc());
} else {
MIRType returnType = getInlineReturnType();
if (!IsNumberType(returnType)) {
return InliningStatus_NotInlined;
}
}
MIRType arg = callInfo.getArg(0)->type();
if (!IsNumberType(arg)) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MToFloat32* ins = MToFloat32::New(alloc(), callInfo.getArg(0));
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineMathTrunc(CallInfo& callInfo) {
if (callInfo.argc() != 1 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
MIRType argType = callInfo.getArg(0)->type();
MIRType returnType = getInlineReturnType();
if (argType == MIRType::Int32 && returnType == MIRType::Int32) {
callInfo.setImplicitlyUsedUnchecked();
MLimitedTruncate* ins = MLimitedTruncate::New(
alloc(), callInfo.getArg(0), MDefinition::IndirectTruncate);
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
if (IsFloatingPointType(argType)) {
if (returnType == MIRType::Int32) {
callInfo.setImplicitlyUsedUnchecked();
MTrunc* ins = MTrunc::New(alloc(), callInfo.getArg(0));
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
if (returnType == MIRType::Double) {
callInfo.setImplicitlyUsedUnchecked();
MInstruction* ins = nullptr;
if (MNearbyInt::HasAssemblerSupport(RoundingMode::TowardsZero)) {
ins = MNearbyInt::New(alloc(), callInfo.getArg(0), argType,
RoundingMode::TowardsZero);
} else {
ins = MMathFunction::New(alloc(), callInfo.getArg(0),
MMathFunction::Trunc);
}
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
}
return InliningStatus_NotInlined;
}
IonBuilder::InliningResult IonBuilder::inlineMathSign(CallInfo& callInfo) {
if (callInfo.argc() != 1 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
MIRType argType = callInfo.getArg(0)->type();
MIRType returnType = getInlineReturnType();
if (returnType != MIRType::Int32 && returnType != MIRType::Double) {
return InliningStatus_NotInlined;
}
if (!IsFloatingPointType(argType) &&
!(argType == MIRType::Int32 && returnType == MIRType::Int32)) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
auto* ins = MSign::New(alloc(), callInfo.getArg(0), returnType);
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineMathMinMax(CallInfo& callInfo,
bool max) {
if (callInfo.argc() < 1 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
MIRType returnType = getInlineReturnType();
if (!IsNumberType(returnType)) {
return InliningStatus_NotInlined;
}
MDefinitionVector int32_cases(alloc());
for (unsigned i = 0; i < callInfo.argc(); i++) {
MDefinition* arg = callInfo.getArg(i);
switch (arg->type()) {
case MIRType::Int32:
if (!int32_cases.append(arg)) {
return abort(AbortReason::Alloc);
}
break;
case MIRType::Double:
case MIRType::Float32:
if (arg->isConstant()) {
double cte = arg->toConstant()->numberToDouble();
if (cte >= INT32_MAX && !max) {
break;
}
if (cte <= INT32_MIN && max) {
break;
}
}
returnType = MIRType::Double;
break;
default:
return InliningStatus_NotInlined;
}
}
if (int32_cases.length() == 0) {
returnType = MIRType::Double;
}
callInfo.setImplicitlyUsedUnchecked();
MDefinitionVector& cases =
(returnType == MIRType::Int32) ? int32_cases : callInfo.argv();
if (cases.length() == 1) {
MLimitedTruncate* limit =
MLimitedTruncate::New(alloc(), cases[0], MDefinition::NoTruncate);
current->add(limit);
current->push(limit);
return InliningStatus_Inlined;
}
MMinMax* last = MMinMax::New(alloc(), cases[0], cases[1], returnType, max);
current->add(last);
for (unsigned i = 2; i < cases.length(); i++) {
MMinMax* ins =
MMinMax::New(alloc().fallible(), last, cases[i], returnType, max);
if (!ins) {
return abort(AbortReason::Alloc);
}
current->add(ins);
last = ins;
}
current->push(last);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineStringObject(CallInfo& callInfo) {
if (callInfo.argc() != 1 || !callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
if (callInfo.getArg(0)->mightBeType(MIRType::Object)) {
return InliningStatus_NotInlined;
}
JSObject* templateObj =
inspector->getTemplateObjectForNative(pc, StringConstructor);
if (!templateObj) {
return InliningStatus_NotInlined;
}
MOZ_ASSERT(templateObj->is<StringObject>());
callInfo.setImplicitlyUsedUnchecked();
MNewStringObject* ins =
MNewStringObject::New(alloc(), callInfo.getArg(0), templateObj);
current->add(ins);
current->push(ins);
MOZ_TRY(resumeAfter(ins));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineConstantStringSplitString(
CallInfo& callInfo) {
if (!callInfo.getArg(0)->isConstant()) {
return InliningStatus_NotInlined;
}
if (!callInfo.getArg(1)->isConstant()) {
return InliningStatus_NotInlined;
}
MConstant* strval = callInfo.getArg(0)->toConstant();
if (strval->type() != MIRType::String) {
return InliningStatus_NotInlined;
}
MConstant* sepval = callInfo.getArg(1)->toConstant();
if (strval->type() != MIRType::String) {
return InliningStatus_NotInlined;
}
JSString* stringStr = nullptr;
JSString* stringSep = nullptr;
ArrayObject* templateObject = nullptr;
if (!inspector->isOptimizableConstStringSplit(pc, &stringStr, &stringSep,
&templateObject)) {
return InliningStatus_NotInlined;
}
MOZ_ASSERT(stringStr);
MOZ_ASSERT(stringSep);
MOZ_ASSERT(templateObject);
if (strval->toString() != stringStr) {
return InliningStatus_NotInlined;
}
if (sepval->toString() != stringSep) {
return InliningStatus_NotInlined;
}
TypeSet::ObjectKey* retType = TypeSet::ObjectKey::get(templateObject);
if (retType->unknownProperties()) {
return InliningStatus_NotInlined;
}
HeapTypeSetKey key = retType->property(JSID_VOID);
if (!key.maybeTypes()) {
return InliningStatus_NotInlined;
}
if (!key.maybeTypes()->hasType(TypeSet::StringType())) {
return InliningStatus_NotInlined;
}
uint32_t initLength = templateObject->length();
if (templateObject->getDenseInitializedLength() != initLength) {
return InliningStatus_NotInlined;
}
Vector<MConstant*, 0, SystemAllocPolicy> arrayValues;
for (uint32_t i = 0; i < initLength; i++) {
Value str = templateObject->getDenseElement(i);
MOZ_ASSERT(str.toString()->isAtom());
MConstant* value = MConstant::New(alloc().fallible(), str, constraints());
if (!value) {
return abort(AbortReason::Alloc);
}
if (!TypeSetIncludes(key.maybeTypes(), value->type(),
value->resultTypeSet())) {
return InliningStatus_NotInlined;
}
if (!arrayValues.append(value)) {
return abort(AbortReason::Alloc);
}
}
callInfo.setImplicitlyUsedUnchecked();
TemporaryTypeSet::DoubleConversion conversion =
getInlineReturnTypeSet()->convertDoubleElements(constraints());
if (conversion == TemporaryTypeSet::AlwaysConvertToDoubles) {
return InliningStatus_NotInlined;
}
MOZ_TRY(jsop_newarray(templateObject, initLength));
MDefinition* array = current->peek(-1);
if (!initLength) {
MOZ_TRY(resumeAfter(array->toNewArray()));
return InliningStatus_Inlined;
}
for (uint32_t i = 0; i < initLength; i++) {
if (!alloc().ensureBallast()) {
return abort(AbortReason::Alloc);
}
MConstant* value = arrayValues[i];
current->add(value);
MOZ_TRY(
initializeArrayElement(array, i, value, false));
}
MInstruction* setLength = setInitializedLength(array, initLength);
MOZ_TRY(resumeAfter(setLength));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineStringSplitString(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 2);
MDefinition* strArg = callInfo.getArg(0);
MDefinition* sepArg = callInfo.getArg(1);
if (strArg->type() != MIRType::String) {
return InliningStatus_NotInlined;
}
if (sepArg->type() != MIRType::String) {
return InliningStatus_NotInlined;
}
IonBuilder::InliningStatus resultConstStringSplit;
MOZ_TRY_VAR(resultConstStringSplit,
inlineConstantStringSplitString(callInfo));
if (resultConstStringSplit != InliningStatus_NotInlined) {
return resultConstStringSplit;
}
JSContext* cx = TlsContext.get();
ObjectGroup* group = ObjectGroupRealm::getStringSplitStringGroup(cx);
if (!group) {
return InliningStatus_NotInlined;
}
TypeSet::ObjectKey* retKey = TypeSet::ObjectKey::get(group);
if (retKey->unknownProperties()) {
return InliningStatus_NotInlined;
}
HeapTypeSetKey key = retKey->property(JSID_VOID);
if (!key.maybeTypes()) {
return InliningStatus_NotInlined;
}
if (!key.maybeTypes()->hasType(TypeSet::StringType())) {
key.freeze(constraints());
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MStringSplit* ins =
MStringSplit::New(alloc(), constraints(), strArg, sepArg, group);
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineObjectHasPrototype(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 2);
MDefinition* objArg = callInfo.getArg(0);
MDefinition* protoArg = callInfo.getArg(1);
if (objArg->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (protoArg->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
TemporaryTypeSet* objTypes = objArg->resultTypeSet();
if (!objTypes || objTypes->unknownObject() ||
objTypes->getObjectCount() != 1) {
return InliningStatus_NotInlined;
}
TypeSet::ObjectKey* objKey = objTypes->getObject(0);
if (!objKey || !objKey->hasStableClassAndProto(constraints())) {
return InliningStatus_NotInlined;
}
if (!objKey->isSingleton() || !objKey->singleton()->is<NativeObject>()) {
return InliningStatus_NotInlined;
}
JSObject* obj = &objKey->singleton()->as<NativeObject>();
if (obj->hasUncacheableProto()) {
return InliningStatus_NotInlined;
}
JSObject* actualProto = checkNurseryObject(objKey->proto().toObjectOrNull());
if (actualProto == nullptr) {
return InliningStatus_NotInlined;
}
TemporaryTypeSet* protoTypes = protoArg->resultTypeSet();
if (!protoTypes || protoTypes->unknownObject() ||
protoTypes->getObjectCount() != 1) {
return InliningStatus_NotInlined;
}
TypeSet::ObjectKey* protoKey = protoTypes->getObject(0);
if (!protoKey || !protoKey->hasStableClassAndProto(constraints())) {
return InliningStatus_NotInlined;
}
if (!protoKey->isSingleton() || !protoKey->singleton()->is<NativeObject>()) {
return InliningStatus_NotInlined;
}
JSObject* proto = &protoKey->singleton()->as<NativeObject>();
pushConstant(BooleanValue(proto == actualProto));
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineFinishBoundFunctionInit(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 3);
MOZ_ASSERT(BytecodeIsPopped(pc));
MDefinition* boundFunction = callInfo.getArg(0);
MDefinition* targetFunction = callInfo.getArg(1);
MDefinition* argCount = callInfo.getArg(2);
if (boundFunction->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (targetFunction->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (argCount->type() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
auto* ins = MFinishBoundFunctionInit::New(alloc(), boundFunction,
targetFunction, argCount);
current->add(ins);
pushConstant(UndefinedValue());
MOZ_TRY(resumeAfter(ins));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineIsPackedArray(CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
if (getInlineReturnType() != MIRType::Boolean) {
return InliningStatus_NotInlined;
}
MDefinition* array = callInfo.getArg(0);
if (array->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
TemporaryTypeSet* arrayTypes = array->resultTypeSet();
if (!arrayTypes) {
return InliningStatus_NotInlined;
}
const Class* clasp = arrayTypes->getKnownClass(constraints());
if (clasp != &ArrayObject::class_) {
return InliningStatus_NotInlined;
}
ObjectGroupFlags unhandledFlags = OBJECT_FLAG_SPARSE_INDEXES |
OBJECT_FLAG_LENGTH_OVERFLOW |
OBJECT_FLAG_NON_PACKED;
if (arrayTypes->hasObjectFlags(constraints(), unhandledFlags)) {
trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
auto* ins = MIsPackedArray::New(alloc(), array);
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineReflectGetPrototypeOf(
CallInfo& callInfo) {
if (callInfo.argc() != 1 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
MDefinition* target = callInfo.getArg(0);
if (target->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
auto* ins = MGetPrototypeOf::New(alloc(), target);
current->add(ins);
current->push(ins);
MOZ_TRY(resumeAfter(ins));
MOZ_TRY(pushTypeBarrier(ins, getInlineReturnTypeSet(), BarrierKind::TypeSet));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineStrCharCodeAt(CallInfo& callInfo) {
if (callInfo.argc() != 1 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
if (callInfo.thisArg()->type() != MIRType::String &&
callInfo.thisArg()->type() != MIRType::Value) {
return InliningStatus_NotInlined;
}
MIRType argType = callInfo.getArg(0)->type();
if (argType != MIRType::Int32 && argType != MIRType::Double) {
return InliningStatus_NotInlined;
}
InliningStatus constInlineStatus;
MOZ_TRY_VAR(constInlineStatus, inlineConstantCharCodeAt(callInfo));
if (constInlineStatus != InliningStatus_NotInlined) {
return constInlineStatus;
}
callInfo.setImplicitlyUsedUnchecked();
MInstruction* index = MToNumberInt32::New(alloc(), callInfo.getArg(0));
current->add(index);
MStringLength* length = MStringLength::New(alloc(), callInfo.thisArg());
current->add(length);
index = addBoundsCheck(index, length);
MCharCodeAt* charCode = MCharCodeAt::New(alloc(), callInfo.thisArg(), index);
current->add(charCode);
current->push(charCode);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineConstantCharCodeAt(
CallInfo& callInfo) {
if (!callInfo.thisArg()->maybeConstantValue() ||
!callInfo.getArg(0)->maybeConstantValue()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
return InliningStatus_NotInlined;
}
MConstant* strval = callInfo.thisArg()->maybeConstantValue();
MConstant* idxval = callInfo.getArg(0)->maybeConstantValue();
if (strval->type() != MIRType::String || idxval->type() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
JSString* str = strval->toString();
if (!str->isLinear()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
return InliningStatus_NotInlined;
}
int32_t idx = idxval->toInt32();
if (idx < 0 || (uint32_t(idx) >= str->length())) {
trackOptimizationOutcome(TrackedOutcome::OutOfBounds);
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
JSLinearString& linstr = str->asLinear();
char16_t ch = linstr.latin1OrTwoByteChar(idx);
MConstant* result = MConstant::New(alloc(), Int32Value(ch));
current->add(result);
current->push(result);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineStrFromCharCode(
CallInfo& callInfo) {
if (callInfo.argc() != 1 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::String) {
return InliningStatus_NotInlined;
}
MDefinition* codeUnit = callInfo.getArg(0);
if (codeUnit->type() != MIRType::Int32) {
if (codeUnit->mightBeType(MIRType::Object) ||
codeUnit->mightBeType(MIRType::Symbol) ||
codeUnit->mightBeType(MIRType::BigInt)) {
return InliningStatus_NotInlined;
}
codeUnit = MTruncateToInt32::New(alloc(), codeUnit);
current->add(codeUnit->toInstruction());
}
callInfo.setImplicitlyUsedUnchecked();
MFromCharCode* string = MFromCharCode::New(alloc(), codeUnit);
current->add(string);
current->push(string);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineStrFromCodePoint(
CallInfo& callInfo) {
if (callInfo.argc() != 1 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::String) {
return InliningStatus_NotInlined;
}
if (callInfo.getArg(0)->type() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MFromCodePoint* string = MFromCodePoint::New(alloc(), callInfo.getArg(0));
current->add(string);
current->push(string);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineStrCharAt(CallInfo& callInfo) {
if (callInfo.argc() != 1 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::String) {
return InliningStatus_NotInlined;
}
if (callInfo.thisArg()->type() != MIRType::String) {
return InliningStatus_NotInlined;
}
MIRType argType = callInfo.getArg(0)->type();
if (argType != MIRType::Int32 && argType != MIRType::Double) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MInstruction* index = MToNumberInt32::New(alloc(), callInfo.getArg(0));
current->add(index);
MStringLength* length = MStringLength::New(alloc(), callInfo.thisArg());
current->add(length);
index = addBoundsCheck(index, length);
MCharCodeAt* charCode = MCharCodeAt::New(alloc(), callInfo.thisArg(), index);
current->add(charCode);
MFromCharCode* string = MFromCharCode::New(alloc(), charCode);
current->add(string);
current->push(string);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineStringConvertCase(
CallInfo& callInfo, MStringConvertCase::Mode mode) {
if (callInfo.argc() != 0 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::String) {
return InliningStatus_NotInlined;
}
if (callInfo.thisArg()->type() != MIRType::String) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
auto* ins = MStringConvertCase::New(alloc(), callInfo.thisArg(), mode);
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineRegExpMatcher(CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 3);
MDefinition* rxArg = callInfo.getArg(0);
MDefinition* strArg = callInfo.getArg(1);
MDefinition* lastIndexArg = callInfo.getArg(2);
if (rxArg->type() != MIRType::Object && rxArg->type() != MIRType::Value) {
return InliningStatus_NotInlined;
}
TemporaryTypeSet* rxTypes = rxArg->resultTypeSet();
const Class* clasp =
rxTypes ? rxTypes->getKnownClass(constraints()) : nullptr;
if (clasp != &RegExpObject::class_) {
return InliningStatus_NotInlined;
}
if (strArg->type() != MIRType::String && strArg->type() != MIRType::Value) {
return InliningStatus_NotInlined;
}
if (lastIndexArg->type() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
JSContext* cx = TlsContext.get();
if (!cx->realm()->jitRealm()->ensureRegExpMatcherStubExists(cx)) {
cx->clearPendingException(); return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MInstruction* matcher =
MRegExpMatcher::New(alloc(), rxArg, strArg, lastIndexArg);
current->add(matcher);
current->push(matcher);
MOZ_TRY(resumeAfter(matcher));
MOZ_TRY(
pushTypeBarrier(matcher, getInlineReturnTypeSet(), BarrierKind::TypeSet));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineRegExpSearcher(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 3);
MDefinition* rxArg = callInfo.getArg(0);
MDefinition* strArg = callInfo.getArg(1);
MDefinition* lastIndexArg = callInfo.getArg(2);
if (rxArg->type() != MIRType::Object && rxArg->type() != MIRType::Value) {
return InliningStatus_NotInlined;
}
TemporaryTypeSet* regexpTypes = rxArg->resultTypeSet();
const Class* clasp =
regexpTypes ? regexpTypes->getKnownClass(constraints()) : nullptr;
if (clasp != &RegExpObject::class_) {
return InliningStatus_NotInlined;
}
if (strArg->type() != MIRType::String && strArg->type() != MIRType::Value) {
return InliningStatus_NotInlined;
}
if (lastIndexArg->type() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
JSContext* cx = TlsContext.get();
if (!cx->realm()->jitRealm()->ensureRegExpSearcherStubExists(cx)) {
cx->clearPendingException(); return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MInstruction* searcher =
MRegExpSearcher::New(alloc(), rxArg, strArg, lastIndexArg);
current->add(searcher);
current->push(searcher);
MOZ_TRY(resumeAfter(searcher));
MOZ_TRY(pushTypeBarrier(searcher, getInlineReturnTypeSet(),
BarrierKind::TypeSet));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineRegExpTester(CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 3);
MDefinition* rxArg = callInfo.getArg(0);
MDefinition* strArg = callInfo.getArg(1);
MDefinition* lastIndexArg = callInfo.getArg(2);
if (rxArg->type() != MIRType::Object && rxArg->type() != MIRType::Value) {
return InliningStatus_NotInlined;
}
TemporaryTypeSet* rxTypes = rxArg->resultTypeSet();
const Class* clasp =
rxTypes ? rxTypes->getKnownClass(constraints()) : nullptr;
if (clasp != &RegExpObject::class_) {
return InliningStatus_NotInlined;
}
if (strArg->type() != MIRType::String && strArg->type() != MIRType::Value) {
return InliningStatus_NotInlined;
}
if (lastIndexArg->type() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
JSContext* cx = TlsContext.get();
if (!cx->realm()->jitRealm()->ensureRegExpTesterStubExists(cx)) {
cx->clearPendingException(); return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MInstruction* tester =
MRegExpTester::New(alloc(), rxArg, strArg, lastIndexArg);
current->add(tester);
current->push(tester);
MOZ_TRY(resumeAfter(tester));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineIsRegExpObject(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
if (getInlineReturnType() != MIRType::Boolean) {
return InliningStatus_NotInlined;
}
MDefinition* arg = callInfo.getArg(0);
bool isRegExpObjectKnown = false;
bool isRegExpObjectConstant;
if (arg->type() == MIRType::Object) {
TemporaryTypeSet* types = arg->resultTypeSet();
const Class* clasp = types ? types->getKnownClass(constraints()) : nullptr;
if (clasp) {
isRegExpObjectKnown = true;
isRegExpObjectConstant = (clasp == &RegExpObject::class_);
}
} else if (!arg->mightBeType(MIRType::Object)) {
isRegExpObjectKnown = true;
isRegExpObjectConstant = false;
} else if (arg->type() != MIRType::Value) {
return InliningStatus_NotInlined;
}
if (isRegExpObjectKnown) {
pushConstant(BooleanValue(isRegExpObjectConstant));
} else {
MHasClass* hasClass = MHasClass::New(alloc(), arg, &RegExpObject::class_);
current->add(hasClass);
current->push(hasClass);
}
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineRegExpPrototypeOptimizable(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
MDefinition* protoArg = callInfo.getArg(0);
if (protoArg->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Boolean) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MInstruction* opt = MRegExpPrototypeOptimizable::New(alloc(), protoArg);
current->add(opt);
current->push(opt);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineRegExpInstanceOptimizable(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 2);
MDefinition* rxArg = callInfo.getArg(0);
MDefinition* protoArg = callInfo.getArg(1);
if (rxArg->type() != MIRType::Object && rxArg->type() != MIRType::Value) {
return InliningStatus_NotInlined;
}
if (protoArg->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Boolean) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MInstruction* opt = MRegExpInstanceOptimizable::New(alloc(), rxArg, protoArg);
current->add(opt);
current->push(opt);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineGetFirstDollarIndex(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
MDefinition* strArg = callInfo.getArg(0);
if (strArg->type() != MIRType::String) {
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MInstruction* ins = MGetFirstDollarIndex::New(alloc(), strArg);
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineStringReplaceString(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 3);
if (getInlineReturnType() != MIRType::String) {
return InliningStatus_NotInlined;
}
MDefinition* strArg = callInfo.getArg(0);
MDefinition* patArg = callInfo.getArg(1);
MDefinition* replArg = callInfo.getArg(2);
if (strArg->type() != MIRType::String) {
return InliningStatus_NotInlined;
}
if (patArg->type() != MIRType::String) {
return InliningStatus_NotInlined;
}
if (replArg->type() != MIRType::String) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MInstruction* cte = MStringReplace::New(alloc(), strArg, patArg, replArg);
current->add(cte);
current->push(cte);
if (cte->isEffectful()) {
MOZ_TRY(resumeAfter(cte));
}
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineSubstringKernel(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 3);
if (getInlineReturnType() != MIRType::String) {
return InliningStatus_NotInlined;
}
if (callInfo.getArg(0)->type() != MIRType::String) {
return InliningStatus_NotInlined;
}
if (callInfo.getArg(1)->type() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
if (callInfo.getArg(2)->type() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MSubstr* substr = MSubstr::New(alloc(), callInfo.getArg(0),
callInfo.getArg(1), callInfo.getArg(2));
current->add(substr);
current->push(substr);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineObject(CallInfo& callInfo) {
if (callInfo.argc() != 1 || callInfo.constructing()) {
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Object) {
return InliningStatus_NotInlined;
}
MDefinition* arg = callInfo.getArg(0);
if (arg->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
current->push(arg);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineObjectCreate(CallInfo& callInfo) {
if (callInfo.argc() != 1 || callInfo.constructing()) {
return InliningStatus_NotInlined;
}
JSObject* templateObject =
inspector->getTemplateObjectForNative(pc, obj_create);
if (!templateObject) {
return InliningStatus_NotInlined;
}
MOZ_ASSERT(templateObject->is<PlainObject>());
MOZ_ASSERT(!templateObject->isSingleton());
MDefinition* arg = callInfo.getArg(0);
if (JSObject* proto = templateObject->staticPrototype()) {
if (IsInsideNursery(proto)) {
return InliningStatus_NotInlined;
}
TemporaryTypeSet* types = arg->resultTypeSet();
if (!types || types->maybeSingleton() != proto) {
return InliningStatus_NotInlined;
}
MOZ_ASSERT(types->getKnownMIRType() == MIRType::Object);
} else {
if (arg->type() != MIRType::Null) {
return InliningStatus_NotInlined;
}
}
callInfo.setImplicitlyUsedUnchecked();
bool emitted = false;
MOZ_TRY(newObjectTryTemplateObject(&emitted, templateObject));
MOZ_ASSERT(emitted);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineObjectIs(CallInfo& callInfo) {
if (callInfo.argc() < 2 || callInfo.constructing()) {
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Boolean) {
return InliningStatus_NotInlined;
}
MDefinition* left = callInfo.getArg(0);
MDefinition* right = callInfo.getArg(1);
MIRType leftType = left->type();
MIRType rightType = right->type();
auto mightBeFloatingPointType = [](MDefinition* def) {
return def->mightBeType(MIRType::Double) ||
def->mightBeType(MIRType::Float32);
};
bool strictEq;
bool incompatibleTypes = false;
if (leftType == rightType) {
strictEq = leftType != MIRType::Value ? !IsFloatingPointType(leftType)
: (!mightBeFloatingPointType(left) &&
!mightBeFloatingPointType(right));
} else if (leftType == MIRType::Value) {
strictEq = !IsNumberType(rightType) || !mightBeFloatingPointType(left);
} else if (rightType == MIRType::Value) {
strictEq = !IsNumberType(leftType) || !mightBeFloatingPointType(right);
} else if (IsNumberType(leftType) && IsNumberType(rightType)) {
strictEq = false;
} else {
incompatibleTypes = true;
}
if (incompatibleTypes) {
pushConstant(BooleanValue(false));
} else if (strictEq) {
MOZ_TRY(jsop_compare(JSOP_STRICTEQ, left, right));
} else {
MSameValue* ins = MSameValue::New(alloc(), left, right);
if (IsNumberType(leftType) && rightType == MIRType::Value) {
ins->swapOperands();
}
current->add(ins);
current->push(ins);
}
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineObjectToString(
CallInfo& callInfo) {
if (callInfo.constructing() || callInfo.argc() != 0) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::String) {
return InliningStatus_NotInlined;
}
MDefinition* arg = callInfo.thisArg();
if (arg->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
TemporaryTypeSet* types = arg->resultTypeSet();
if (!types || types->unknownObject()) {
return InliningStatus_NotInlined;
}
using ForAllResult = TemporaryTypeSet::ForAllResult;
if (types->forAllClasses(constraints(), IsProxyClass) !=
ForAllResult::ALL_FALSE) {
return InliningStatus_NotInlined;
}
jsid toStringTag =
SYMBOL_TO_JSID(realm->runtime()->wellKnownSymbols().toStringTag);
bool res;
MOZ_TRY_VAR(res, testNotDefinedProperty(arg, toStringTag));
if (!res) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
if (const Class* knownClass = types->getKnownClass(constraints())) {
if (knownClass == &PlainObject::class_ ||
knownClass == &UnboxedPlainObject::class_) {
pushConstant(StringValue(names().objectObject));
return InliningStatus_Inlined;
}
if (IsArrayClass(knownClass)) {
pushConstant(StringValue(names().objectArray));
return InliningStatus_Inlined;
}
if (knownClass == &JSFunction::class_) {
pushConstant(StringValue(names().objectFunction));
return InliningStatus_Inlined;
}
}
MObjectClassToString* toString = MObjectClassToString::New(alloc(), arg);
current->add(toString);
current->push(toString);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineHasClass(CallInfo& callInfo,
const Class* clasp1,
const Class* clasp2,
const Class* clasp3,
const Class* clasp4) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
if (callInfo.getArg(0)->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Boolean) {
return InliningStatus_NotInlined;
}
TemporaryTypeSet* types = callInfo.getArg(0)->resultTypeSet();
const Class* knownClass =
types ? types->getKnownClass(constraints()) : nullptr;
if (knownClass) {
pushConstant(BooleanValue(knownClass == clasp1 || knownClass == clasp2 ||
knownClass == clasp3 || knownClass == clasp4));
} else {
MHasClass* hasClass1 = MHasClass::New(alloc(), callInfo.getArg(0), clasp1);
current->add(hasClass1);
if (!clasp2 && !clasp3 && !clasp4) {
current->push(hasClass1);
} else {
const Class* remaining[] = {clasp2, clasp3, clasp4};
MDefinition* last = hasClass1;
for (size_t i = 0; i < ArrayLength(remaining); i++) {
MHasClass* hasClass =
MHasClass::New(alloc(), callInfo.getArg(0), remaining[i]);
current->add(hasClass);
MBitOr* either = MBitOr::New(alloc(), last, hasClass);
either->infer(inspector, pc);
current->add(either);
last = either;
}
MDefinition* result = convertToBoolean(last);
current->push(result);
}
}
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineGuardToClass(CallInfo& callInfo,
const Class* clasp) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
if (callInfo.getArg(0)->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Object) {
return InliningStatus_NotInlined;
}
TemporaryTypeSet* types = callInfo.getArg(0)->resultTypeSet();
const Class* knownClass =
types ? types->getKnownClass(constraints()) : nullptr;
if (knownClass && knownClass == clasp) {
current->push(callInfo.getArg(0));
} else {
MGuardToClass* guardToClass =
MGuardToClass::New(alloc(), callInfo.getArg(0), clasp);
current->add(guardToClass);
current->push(guardToClass);
}
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineGetNextEntryForIterator(
CallInfo& callInfo, MGetNextEntryForIterator::Mode mode) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 2);
MDefinition* iterArg = callInfo.getArg(0);
MDefinition* resultArg = callInfo.getArg(1);
if (iterArg->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (resultArg->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
TemporaryTypeSet* resultTypes = resultArg->resultTypeSet();
const Class* resultClasp =
resultTypes ? resultTypes->getKnownClass(constraints()) : nullptr;
if (resultClasp != &ArrayObject::class_) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MInstruction* next =
MGetNextEntryForIterator::New(alloc(), iterArg, resultArg, mode);
current->add(next);
current->push(next);
MOZ_TRY(resumeAfter(next));
return InliningStatus_Inlined;
}
static bool IsArrayBufferObject(CompilerConstraintList* constraints,
MDefinition* def) {
MOZ_ASSERT(def->type() == MIRType::Object);
TemporaryTypeSet* types = def->resultTypeSet();
if (!types) {
return false;
}
return types->getKnownClass(constraints) == &ArrayBufferObject::class_;
}
IonBuilder::InliningResult IonBuilder::inlineArrayBufferByteLength(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
MDefinition* objArg = callInfo.getArg(0);
if (objArg->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
MInstruction* ins = addArrayBufferByteLength(objArg);
current->push(ins);
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
IonBuilder::InliningResult
IonBuilder::inlinePossiblyWrappedArrayBufferByteLength(CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
MDefinition* objArg = callInfo.getArg(0);
if (objArg->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
if (!IsArrayBufferObject(constraints(), objArg)) {
return InliningStatus_NotInlined;
}
MInstruction* ins = addArrayBufferByteLength(objArg);
current->push(ins);
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineTypedArray(CallInfo& callInfo,
Native native) {
if (!callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (callInfo.argc() == 0 || callInfo.argc() > 3) {
return InliningStatus_NotInlined;
}
JSObject* templateObject = inspector->getTemplateObjectForNative(pc, native);
if (!templateObject) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeNoTemplateObj);
return InliningStatus_NotInlined;
}
MOZ_ASSERT(templateObject->is<TypedArrayObject>());
TypedArrayObject* obj = &templateObject->as<TypedArrayObject>();
if (templateObject->isSingleton()) {
return InliningStatus_NotInlined;
}
MDefinition* arg = callInfo.getArg(0);
MInstruction* ins;
if (arg->type() == MIRType::Int32) {
if (!arg->isConstant()) {
ins = MNewTypedArrayDynamicLength::New(
alloc(), constraints(), templateObject,
templateObject->group()->initialHeap(constraints()), arg);
} else {
int32_t providedLen = arg->maybeConstantValue()->toInt32();
if (providedLen <= 0) {
return InliningStatus_NotInlined;
}
uint32_t len = AssertedCast<uint32_t>(providedLen);
if (obj->length() != len) {
return InliningStatus_NotInlined;
}
MConstant* templateConst =
MConstant::NewConstraintlessObject(alloc(), obj);
current->add(templateConst);
ins = MNewTypedArray::New(alloc(), constraints(), templateConst,
obj->group()->initialHeap(constraints()));
}
} else if (arg->type() == MIRType::Object) {
TemporaryTypeSet* types = arg->resultTypeSet();
if (!types) {
return InliningStatus_NotInlined;
}
if (types->forAllClasses(constraints(), IsProxyClass) !=
TemporaryTypeSet::ForAllResult::ALL_FALSE) {
return InliningStatus_NotInlined;
}
auto IsArrayBufferMaybeSharedClass = [](const Class* clasp) {
return clasp == &ArrayBufferObject::class_ ||
clasp == &SharedArrayBufferObject::class_;
};
switch (
types->forAllClasses(constraints(), IsArrayBufferMaybeSharedClass)) {
case TemporaryTypeSet::ForAllResult::ALL_FALSE:
ins = MNewTypedArrayFromArray::New(
alloc(), constraints(), templateObject,
templateObject->group()->initialHeap(constraints()), arg);
break;
case TemporaryTypeSet::ForAllResult::ALL_TRUE:
MDefinition* byteOffset;
if (callInfo.argc() > 1) {
byteOffset = callInfo.getArg(1);
} else {
byteOffset = constant(UndefinedValue());
}
MDefinition* length;
if (callInfo.argc() > 2) {
length = callInfo.getArg(2);
} else {
length = constant(UndefinedValue());
}
ins = MNewTypedArrayFromArrayBuffer::New(
alloc(), constraints(), templateObject,
templateObject->group()->initialHeap(constraints()), arg,
byteOffset, length);
break;
case TemporaryTypeSet::ForAllResult::EMPTY:
case TemporaryTypeSet::ForAllResult::MIXED:
return InliningStatus_NotInlined;
}
} else {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
current->add(ins);
current->push(ins);
MOZ_TRY(resumeAfter(ins));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineIsTypedArrayConstructor(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
if (getInlineReturnType() != MIRType::Boolean) {
return InliningStatus_NotInlined;
}
if (callInfo.getArg(0)->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
TemporaryTypeSet* types = callInfo.getArg(0)->resultTypeSet();
if (!types || types->unknownObject() || types->getObjectCount() == 0) {
return InliningStatus_NotInlined;
}
for (unsigned i = 0; i < types->getObjectCount(); i++) {
JSObject* singleton = types->getSingleton(i);
if (!singleton || !IsTypedArrayConstructor(singleton)) {
return InliningStatus_NotInlined;
}
}
callInfo.setImplicitlyUsedUnchecked();
pushConstant(BooleanValue(true));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineIsTypedArrayHelper(
CallInfo& callInfo, WrappingBehavior wrappingBehavior) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
if (callInfo.getArg(0)->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Boolean) {
return InliningStatus_NotInlined;
}
TemporaryTypeSet* types = callInfo.getArg(0)->resultTypeSet();
if (!types) {
return InliningStatus_NotInlined;
}
auto isPossiblyWrapped = [this, wrappingBehavior, types]() {
if (wrappingBehavior != AllowWrappedTypedArrays) {
return false;
}
switch (types->forAllClasses(constraints(), IsProxyClass)) {
case TemporaryTypeSet::ForAllResult::ALL_FALSE:
case TemporaryTypeSet::ForAllResult::EMPTY:
break;
case TemporaryTypeSet::ForAllResult::ALL_TRUE:
case TemporaryTypeSet::ForAllResult::MIXED:
return true;
}
return false;
};
bool result = false;
bool isConstant = true;
bool possiblyWrapped = false;
switch (types->forAllClasses(constraints(), IsTypedArrayClass)) {
case TemporaryTypeSet::ForAllResult::ALL_FALSE:
if (isPossiblyWrapped()) {
return InliningStatus_NotInlined;
}
MOZ_FALLTHROUGH;
case TemporaryTypeSet::ForAllResult::EMPTY:
result = false;
break;
case TemporaryTypeSet::ForAllResult::ALL_TRUE:
result = true;
break;
case TemporaryTypeSet::ForAllResult::MIXED:
isConstant = false;
possiblyWrapped = isPossiblyWrapped();
break;
}
if (isConstant) {
pushConstant(BooleanValue(result));
} else {
auto* ins =
MIsTypedArray::New(alloc(), callInfo.getArg(0), possiblyWrapped);
current->add(ins);
current->push(ins);
if (possiblyWrapped) {
MOZ_TRY(resumeAfter(ins));
}
}
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineIsTypedArray(CallInfo& callInfo) {
return inlineIsTypedArrayHelper(callInfo, RejectWrappedTypedArrays);
}
IonBuilder::InliningResult IonBuilder::inlineIsPossiblyWrappedTypedArray(
CallInfo& callInfo) {
return inlineIsTypedArrayHelper(callInfo, AllowWrappedTypedArrays);
}
static bool IsTypedArrayObject(CompilerConstraintList* constraints,
MDefinition* def) {
MOZ_ASSERT(def->type() == MIRType::Object);
TemporaryTypeSet* types = def->resultTypeSet();
if (!types) {
return false;
}
return types->forAllClasses(constraints, IsTypedArrayClass) ==
TemporaryTypeSet::ForAllResult::ALL_TRUE;
}
IonBuilder::InliningResult IonBuilder::inlinePossiblyWrappedTypedArrayLength(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
if (callInfo.getArg(0)->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
if (!IsTypedArrayObject(constraints(), callInfo.getArg(0))) {
return InliningStatus_NotInlined;
}
MInstruction* length = addTypedArrayLength(callInfo.getArg(0));
current->push(length);
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineTypedArrayLength(
CallInfo& callInfo) {
return inlinePossiblyWrappedTypedArrayLength(callInfo);
}
IonBuilder::InliningResult IonBuilder::inlineTypedArrayByteOffset(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
if (callInfo.getArg(0)->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
if (!IsTypedArrayObject(constraints(), callInfo.getArg(0))) {
return InliningStatus_NotInlined;
}
MInstruction* byteOffset = addTypedArrayByteOffset(callInfo.getArg(0));
current->push(byteOffset);
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineTypedArrayElementShift(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
if (callInfo.getArg(0)->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
if (!IsTypedArrayObject(constraints(), callInfo.getArg(0))) {
return InliningStatus_NotInlined;
}
auto* ins = MTypedArrayElementShift::New(alloc(), callInfo.getArg(0));
current->add(ins);
current->push(ins);
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineSetDisjointTypedElements(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 3);
MDefinition* target = callInfo.getArg(0);
if (target->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Undefined) {
return InliningStatus_NotInlined;
}
MDefinition* targetOffset = callInfo.getArg(1);
MOZ_ASSERT(targetOffset->type() == MIRType::Int32);
MDefinition* sourceTypedArray = callInfo.getArg(2);
if (sourceTypedArray->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (!IsTypedArrayObject(constraints(), target) ||
!IsTypedArrayObject(constraints(), sourceTypedArray)) {
return InliningStatus_NotInlined;
}
auto sets = MSetDisjointTypedElements::New(alloc(), target, targetOffset,
sourceTypedArray);
current->add(sets);
pushConstant(UndefinedValue());
MOZ_TRY(resumeAfter(sets));
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineObjectIsTypeDescr(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
if (callInfo.getArg(0)->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
if (getInlineReturnType() != MIRType::Boolean) {
return InliningStatus_NotInlined;
}
TemporaryTypeSet* types = callInfo.getArg(0)->resultTypeSet();
if (!types) {
return InliningStatus_NotInlined;
}
bool result = false;
switch (types->forAllClasses(constraints(), IsTypeDescrClass)) {
case TemporaryTypeSet::ForAllResult::ALL_FALSE:
case TemporaryTypeSet::ForAllResult::EMPTY:
result = false;
break;
case TemporaryTypeSet::ForAllResult::ALL_TRUE:
result = true;
break;
case TemporaryTypeSet::ForAllResult::MIXED:
return InliningStatus_NotInlined;
}
pushConstant(BooleanValue(result));
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineSetTypedObjectOffset(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 2);
MDefinition* typedObj = callInfo.getArg(0);
MDefinition* offset = callInfo.getArg(1);
if (getInlineReturnType() != MIRType::Undefined) {
return InliningStatus_NotInlined;
}
TemporaryTypeSet* types = typedObj->resultTypeSet();
if (typedObj->type() != MIRType::Object || !types) {
return InliningStatus_NotInlined;
}
switch (types->forAllClasses(constraints(), IsTypedObjectClass)) {
case TemporaryTypeSet::ForAllResult::ALL_FALSE:
case TemporaryTypeSet::ForAllResult::EMPTY:
case TemporaryTypeSet::ForAllResult::MIXED:
return InliningStatus_NotInlined;
case TemporaryTypeSet::ForAllResult::ALL_TRUE:
break;
}
if (offset->type() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MInstruction* ins = MSetTypedObjectOffset::New(alloc(), typedObj, offset);
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineUnsafeSetReservedSlot(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 3);
if (getInlineReturnType() != MIRType::Undefined) {
return InliningStatus_NotInlined;
}
MDefinition* obj = callInfo.getArg(0);
if (obj->type() != MIRType::Object && obj->type() != MIRType::Value) {
return InliningStatus_NotInlined;
}
MDefinition* arg = callInfo.getArg(1);
if (arg->type() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
if (!arg->isConstant()) {
return InliningStatus_NotInlined;
}
uint32_t slot = uint32_t(arg->toConstant()->toInt32());
if (slot >= NativeObject::MAX_FIXED_SLOTS) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MStoreFixedSlot* store =
MStoreFixedSlot::NewBarriered(alloc(), obj, slot, callInfo.getArg(2));
current->add(store);
current->push(store);
if (needsPostBarrier(callInfo.getArg(2))) {
current->add(MPostWriteBarrier::New(alloc(), obj, callInfo.getArg(2)));
}
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineUnsafeGetReservedSlot(
CallInfo& callInfo, MIRType knownValueType) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 2);
MDefinition* obj = callInfo.getArg(0);
if (obj->type() != MIRType::Object && obj->type() != MIRType::Value) {
return InliningStatus_NotInlined;
}
MDefinition* arg = callInfo.getArg(1);
if (arg->type() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
if (!arg->isConstant()) {
return InliningStatus_NotInlined;
}
uint32_t slot = uint32_t(arg->toConstant()->toInt32());
if (slot >= NativeObject::MAX_FIXED_SLOTS) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MLoadFixedSlot* load = MLoadFixedSlot::New(alloc(), obj, slot);
current->add(load);
current->push(load);
if (knownValueType != MIRType::Value) {
MOZ_ASSERT_IF(!getInlineReturnTypeSet()->empty(),
getInlineReturnType() == knownValueType);
load->setResultType(knownValueType);
}
MOZ_TRY(
pushTypeBarrier(load, getInlineReturnTypeSet(), BarrierKind::TypeSet));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineIsCallable(CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
if (getInlineReturnType() != MIRType::Boolean) {
return InliningStatus_NotInlined;
}
MDefinition* arg = callInfo.getArg(0);
bool isCallableKnown = false;
bool isCallableConstant;
if (arg->type() == MIRType::Object) {
TemporaryTypeSet* types = arg->resultTypeSet();
const Class* clasp = types ? types->getKnownClass(constraints()) : nullptr;
if (clasp && !clasp->isProxy()) {
isCallableKnown = true;
isCallableConstant = clasp->nonProxyCallable();
}
} else if (!arg->mightBeType(MIRType::Object)) {
isCallableKnown = true;
isCallableConstant = false;
} else if (arg->type() != MIRType::Value) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
if (isCallableKnown) {
MConstant* constant =
MConstant::New(alloc(), BooleanValue(isCallableConstant));
current->add(constant);
current->push(constant);
return InliningStatus_Inlined;
}
MIsCallable* isCallable = MIsCallable::New(alloc(), arg);
current->add(isCallable);
current->push(isCallable);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineIsConstructor(CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
if (getInlineReturnType() != MIRType::Boolean) {
return InliningStatus_NotInlined;
}
if (callInfo.getArg(0)->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MIsConstructor* ins = MIsConstructor::New(alloc(), callInfo.getArg(0));
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineIsObject(CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
if (getInlineReturnType() != MIRType::Boolean) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
if (callInfo.getArg(0)->type() == MIRType::Object) {
pushConstant(BooleanValue(true));
} else {
MIsObject* isObject = MIsObject::New(alloc(), callInfo.getArg(0));
current->add(isObject);
current->push(isObject);
}
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineToObject(CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
if (getInlineReturnType() != MIRType::Object) {
return InliningStatus_NotInlined;
}
MDefinition* object = callInfo.getArg(0);
if (object->type() != MIRType::Object && object->type() != MIRType::Value) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
if (object->type() == MIRType::Object) {
current->push(object);
} else {
auto* ins = MToObject::New(alloc(), object);
current->add(ins);
current->push(ins);
MOZ_TRY(
pushTypeBarrier(ins, getInlineReturnTypeSet(), BarrierKind::TypeSet));
}
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineIsCrossRealmArrayConstructor(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
if (getInlineReturnType() != MIRType::Boolean) {
return InliningStatus_NotInlined;
}
MDefinition* arg = callInfo.getArg(0);
if (arg->type() != MIRType::Object) {
return InliningStatus_NotInlined;
}
TemporaryTypeSet* types = arg->resultTypeSet();
Realm* realm = types->getKnownRealm(constraints());
if (!realm || realm != script()->realm()) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
pushConstant(BooleanValue(false));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineToInteger(CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
MDefinition* input = callInfo.getArg(0);
if (input->mightBeType(MIRType::Object) ||
input->mightBeType(MIRType::String) ||
input->mightBeType(MIRType::Symbol) ||
input->mightBeType(MIRType::BigInt) ||
input->mightBeType(MIRType::Undefined) || input->mightBeMagicType()) {
return InliningStatus_NotInlined;
}
MOZ_ASSERT(input->type() == MIRType::Value ||
input->type() == MIRType::Null ||
input->type() == MIRType::Boolean || IsNumberType(input->type()));
if (getInlineReturnType() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
auto* toInt32 = MToNumberInt32::New(alloc(), callInfo.getArg(0));
current->add(toInt32);
current->push(toInt32);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineToString(CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 1);
if (getInlineReturnType() != MIRType::String) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MToString* toString = MToString::New(alloc(), callInfo.getArg(0));
current->add(toString);
current->push(toString);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineBailout(CallInfo& callInfo) {
callInfo.setImplicitlyUsedUnchecked();
current->add(MBail::New(alloc()));
MConstant* undefined = MConstant::New(alloc(), UndefinedValue());
current->add(undefined);
current->push(undefined);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineAssertFloat32(CallInfo& callInfo) {
if (callInfo.argc() != 2) {
return InliningStatus_NotInlined;
}
MDefinition* secondArg = callInfo.getArg(1);
MOZ_ASSERT(secondArg->type() == MIRType::Boolean);
MOZ_ASSERT(secondArg->isConstant());
bool mustBeFloat32 = secondArg->toConstant()->toBoolean();
current->add(MAssertFloat32::New(alloc(), callInfo.getArg(0), mustBeFloat32));
MConstant* undefined = MConstant::New(alloc(), UndefinedValue());
current->add(undefined);
current->push(undefined);
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineAssertRecoveredOnBailout(
CallInfo& callInfo) {
if (callInfo.argc() != 2) {
return InliningStatus_NotInlined;
}
if (JitOptions.disableRecoverIns) {
return InliningStatus_NotInlined;
}
if (JitOptions.checkRangeAnalysis) {
current->push(constant(UndefinedValue()));
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
MDefinition* secondArg = callInfo.getArg(1);
MOZ_ASSERT(secondArg->type() == MIRType::Boolean);
MOZ_ASSERT(secondArg->isConstant());
bool mustBeRecovered = secondArg->toConstant()->toBoolean();
MAssertRecoveredOnBailout* assert = MAssertRecoveredOnBailout::New(
alloc(), callInfo.getArg(0), mustBeRecovered);
current->add(assert);
current->push(assert);
MNop* nop = MNop::New(alloc());
current->add(nop);
MOZ_TRY(resumeAfter(nop));
current->add(MEncodeSnapshot::New(alloc()));
current->pop();
current->push(constant(UndefinedValue()));
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineAtomicsCompareExchange(
CallInfo& callInfo) {
if (callInfo.argc() != 4 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
MDefinition* oldval = callInfo.getArg(2);
if (oldval->mightBeType(MIRType::Object) ||
oldval->mightBeType(MIRType::Symbol) ||
oldval->mightBeType(MIRType::BigInt)) {
return InliningStatus_NotInlined;
}
MDefinition* newval = callInfo.getArg(3);
if (newval->mightBeType(MIRType::Object) ||
newval->mightBeType(MIRType::Symbol) ||
newval->mightBeType(MIRType::BigInt)) {
return InliningStatus_NotInlined;
}
Scalar::Type arrayType;
bool requiresCheck = false;
if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck)) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MInstruction* elements;
MDefinition* index;
atomicsCheckBounds(callInfo, &elements, &index);
if (requiresCheck) {
addSharedTypedArrayGuard(callInfo.getArg(0));
}
MCompareExchangeTypedArrayElement* cas =
MCompareExchangeTypedArrayElement::New(alloc(), elements, index,
arrayType, oldval, newval);
cas->setResultType(getInlineReturnType());
current->add(cas);
current->push(cas);
MOZ_TRY(resumeAfter(cas));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineAtomicsExchange(
CallInfo& callInfo) {
if (callInfo.argc() != 3 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
MDefinition* value = callInfo.getArg(2);
if (value->mightBeType(MIRType::Object) ||
value->mightBeType(MIRType::Symbol) ||
value->mightBeType(MIRType::BigInt)) {
return InliningStatus_NotInlined;
}
Scalar::Type arrayType;
bool requiresCheck = false;
if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck)) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MInstruction* elements;
MDefinition* index;
atomicsCheckBounds(callInfo, &elements, &index);
if (requiresCheck) {
addSharedTypedArrayGuard(callInfo.getArg(0));
}
MInstruction* exchange = MAtomicExchangeTypedArrayElement::New(
alloc(), elements, index, value, arrayType);
exchange->setResultType(getInlineReturnType());
current->add(exchange);
current->push(exchange);
MOZ_TRY(resumeAfter(exchange));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineAtomicsLoad(CallInfo& callInfo) {
if (callInfo.argc() != 2 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
Scalar::Type arrayType;
bool requiresCheck = false;
if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck)) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MInstruction* elements;
MDefinition* index;
atomicsCheckBounds(callInfo, &elements, &index);
if (requiresCheck) {
addSharedTypedArrayGuard(callInfo.getArg(0));
}
MLoadUnboxedScalar* load = MLoadUnboxedScalar::New(
alloc(), elements, index, arrayType, DoesRequireMemoryBarrier);
load->setResultType(getInlineReturnType());
current->add(load);
current->push(load);
MOZ_TRY(resumeAfter(load));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineAtomicsStore(CallInfo& callInfo) {
if (callInfo.argc() != 3 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
MDefinition* value = callInfo.getArg(2);
if (!BytecodeIsPopped(pc) && value->type() != MIRType::Int32) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadType);
return InliningStatus_NotInlined;
}
if (value->mightBeType(MIRType::Object) ||
value->mightBeType(MIRType::Symbol) ||
value->mightBeType(MIRType::BigInt)) {
return InliningStatus_NotInlined;
}
Scalar::Type arrayType;
bool requiresCheck = false;
if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck,
DontCheckAtomicResult)) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MInstruction* elements;
MDefinition* index;
atomicsCheckBounds(callInfo, &elements, &index);
if (requiresCheck) {
addSharedTypedArrayGuard(callInfo.getArg(0));
}
MDefinition* toWrite = value;
if (toWrite->type() != MIRType::Int32) {
toWrite = MTruncateToInt32::New(alloc(), toWrite);
current->add(toWrite->toInstruction());
}
MStoreUnboxedScalar* store = MStoreUnboxedScalar::New(
alloc(), elements, index, toWrite, arrayType,
MStoreUnboxedScalar::TruncateInput, DoesRequireMemoryBarrier);
current->add(store);
current->push(value);
MOZ_TRY(resumeAfter(store));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineAtomicsBinop(
CallInfo& callInfo, InlinableNative target) {
if (callInfo.argc() != 3 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
MDefinition* value = callInfo.getArg(2);
if (value->mightBeType(MIRType::Object) ||
value->mightBeType(MIRType::Symbol) ||
value->mightBeType(MIRType::BigInt)) {
return InliningStatus_NotInlined;
}
Scalar::Type arrayType;
bool requiresCheck = false;
if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck)) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
if (requiresCheck) {
addSharedTypedArrayGuard(callInfo.getArg(0));
}
MInstruction* elements;
MDefinition* index;
atomicsCheckBounds(callInfo, &elements, &index);
AtomicOp k = AtomicFetchAddOp;
switch (target) {
case InlinableNative::AtomicsAdd:
k = AtomicFetchAddOp;
break;
case InlinableNative::AtomicsSub:
k = AtomicFetchSubOp;
break;
case InlinableNative::AtomicsAnd:
k = AtomicFetchAndOp;
break;
case InlinableNative::AtomicsOr:
k = AtomicFetchOrOp;
break;
case InlinableNative::AtomicsXor:
k = AtomicFetchXorOp;
break;
default:
MOZ_CRASH("Bad atomic operation");
}
MAtomicTypedArrayElementBinop* binop = MAtomicTypedArrayElementBinop::New(
alloc(), k, elements, index, arrayType, value);
binop->setResultType(getInlineReturnType());
current->add(binop);
current->push(binop);
MOZ_TRY(resumeAfter(binop));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineAtomicsIsLockFree(
CallInfo& callInfo) {
if (callInfo.argc() != 1 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MAtomicIsLockFree* ilf = MAtomicIsLockFree::New(alloc(), callInfo.getArg(0));
current->add(ilf);
current->push(ilf);
return InliningStatus_Inlined;
}
bool IonBuilder::atomicsMeetsPreconditions(CallInfo& callInfo,
Scalar::Type* arrayType,
bool* requiresTagCheck,
AtomicCheckResult checkResult) {
if (!JitSupportsAtomics()) {
return false;
}
if (callInfo.getArg(0)->type() != MIRType::Object) {
return false;
}
if (callInfo.getArg(1)->type() != MIRType::Int32) {
return false;
}
TemporaryTypeSet* arg0Types = callInfo.getArg(0)->resultTypeSet();
if (!arg0Types) {
return false;
}
TemporaryTypeSet::TypedArraySharedness sharedness =
TemporaryTypeSet::UnknownSharedness;
*arrayType = arg0Types->getTypedArrayType(constraints(), &sharedness);
*requiresTagCheck = sharedness != TemporaryTypeSet::KnownShared;
switch (*arrayType) {
case Scalar::Int8:
case Scalar::Uint8:
case Scalar::Int16:
case Scalar::Uint16:
case Scalar::Int32:
return checkResult == DontCheckAtomicResult ||
getInlineReturnType() == MIRType::Int32;
case Scalar::Uint32:
return checkResult == DontCheckAtomicResult ||
getInlineReturnType() == MIRType::Double;
default:
return false;
}
}
void IonBuilder::atomicsCheckBounds(CallInfo& callInfo, MInstruction** elements,
MDefinition** index) {
MDefinition* obj = callInfo.getArg(0);
MInstruction* length = nullptr;
*index = callInfo.getArg(1);
*elements = nullptr;
addTypedArrayLengthAndData(obj, DoBoundsCheck, index, &length, elements);
}
IonBuilder::InliningResult IonBuilder::inlineIsConstructing(
CallInfo& callInfo) {
MOZ_ASSERT(!callInfo.constructing());
MOZ_ASSERT(callInfo.argc() == 0);
MOZ_ASSERT(script()->functionNonDelazifying(),
"isConstructing() should only be called in function scripts");
if (getInlineReturnType() != MIRType::Boolean) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
if (inliningDepth_ == 0) {
MInstruction* ins = MIsConstructing::New(alloc());
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
bool constructing = inlineCallInfo_->constructing();
pushConstant(BooleanValue(constructing));
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineConstructTypedObject(
CallInfo& callInfo, TypeDescr* descr) {
if (callInfo.argc() != 0) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
if (!InlineTypedObject::canAccommodateType(descr)) {
return InliningStatus_NotInlined;
}
JSObject* obj =
inspector->getTemplateObjectForClassHook(pc, descr->getClass());
if (!obj || !obj->is<InlineTypedObject>()) {
return InliningStatus_NotInlined;
}
InlineTypedObject* templateObject = &obj->as<InlineTypedObject>();
if (&templateObject->typeDescr() != descr) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MNewTypedObject* ins =
MNewTypedObject::New(alloc(), constraints(), templateObject,
templateObject->group()->initialHeap(constraints()));
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
IonBuilder::InliningResult IonBuilder::inlineWasmCall(CallInfo& callInfo,
JSFunction* target) {
MOZ_ASSERT(target->isWasmOptimized());
if (callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
if (target->realm() != script()->realm()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineCrossRealm);
return InliningStatus_NotInlined;
}
wasm::Instance& inst = wasm::ExportedFunctionToInstance(target);
uint32_t funcIndex = inst.code().getFuncIndex(target);
auto bestTier = inst.code().bestTier();
const wasm::FuncExport& funcExport =
inst.metadata(bestTier).lookupFuncExport(funcIndex);
const wasm::FuncType& sig = funcExport.funcType();
if (sig.hasI64ArgOrRet() || sig.temporarilyUnsupportedAnyRef()
#ifdef WASM_CODEGEN_DEBUG
|| !JitOptions.enableWasmIonFastCalls
#endif
) {
return InliningStatus_NotInlined;
}
static constexpr size_t MaxNumInlinedArgs = 8;
static_assert(MaxNumInlinedArgs <= MaxNumLInstructionOperands,
"inlined arguments can all be LIR operands");
if (sig.args().length() > MaxNumInlinedArgs) {
return InliningStatus_NotInlined;
}
auto* call = MIonToWasmCall::New(alloc(), inst.object(), funcExport);
if (!call) {
return abort(AbortReason::Alloc);
}
Maybe<MDefinition*> undefined;
for (size_t i = 0; i < sig.args().length(); i++) {
if (!alloc().ensureBallast()) {
return abort(AbortReason::Alloc);
}
if (i >= callInfo.argc() && !undefined) {
undefined.emplace(constant(UndefinedValue()));
}
MDefinition* arg = i >= callInfo.argc() ? *undefined : callInfo.getArg(i);
MInstruction* conversion = nullptr;
switch (sig.args()[i].code()) {
case wasm::ValType::I32:
conversion = MTruncateToInt32::New(alloc(), arg);
break;
case wasm::ValType::F32:
conversion = MToFloat32::New(alloc(), arg);
break;
case wasm::ValType::F64:
conversion = MToDouble::New(alloc(), arg);
break;
case wasm::ValType::I64:
case wasm::ValType::AnyRef:
case wasm::ValType::Ref:
MOZ_CRASH("impossible per above check");
case wasm::ValType::NullRef:
MOZ_CRASH("NullRef not expressible");
}
current->add(conversion);
call->initArg(i, conversion);
}
current->push(call);
current->add(call);
MOZ_TRY(resumeAfter(call));
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
#define ADD_NATIVE(native) \
const JSJitInfo JitInfo_##native{{nullptr}, \
{uint16_t(InlinableNative::native)}, \
{0}, \
JSJitInfo::InlinableNative};
INLINABLE_NATIVE_LIST(ADD_NATIVE)
#undef ADD_NATIVE
} }