Bug 1657285 - Part 3: Optimise IsTypedArrayConstructor in CacheIR and Warp. r=jandem

Add `IsTypedArrayConstructorResult` to CacheIR to test if a constructor is
definitely a TypedArray constructor. The `IsTypedArrayConstructor` intrinsic
returns `true` when the input is TypedArray constructor function. It allows the
constructor to be from a different realm, but it doesn't unwraps wrappers. (So
false-negatives are possible. This is okay because the function is only used to
guard a fast-path.)


Drive-by change:
- Change `IsTypedArrayConstructor` to use `Scalar::Type` for its parameter. This
  function is only called from asm.js code which already passes a `Scalar::Type`.
- Also change it to use the `JS_FOR_EACH_TYPED_ARRAY` macro used elsewhere in
  TypedArrayObject.cpp.

Differential Revision: https://phabricator.services.mozilla.com/D85970
This commit is contained in:
André Bargull 2020-08-06 14:22:23 +00:00
Родитель 67568a0e4c
Коммит 2c1cb8b174
13 изменённых файлов: 152 добавлений и 27 удалений

Просмотреть файл

@ -6923,6 +6923,29 @@ AttachDecision CallIRGenerator::tryAttachIsTypedArray(HandleFunction callee,
return AttachDecision::Attach;
}
AttachDecision CallIRGenerator::tryAttachIsTypedArrayConstructor(
HandleFunction callee) {
// Self-hosted code calls this with a single object argument.
MOZ_ASSERT(argc_ == 1);
MOZ_ASSERT(args_[0].isObject());
// Initialize the input operand.
Int32OperandId argcId(writer.setInputOperandId(0));
// Note: we don't need to call emitNativeCalleeGuard for intrinsics.
ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
ObjOperandId objArgId = writer.guardToObject(argId);
writer.isTypedArrayConstructorResult(objArgId);
// This stub does not need to be monitored because it always returns a bool.
writer.returnFromIC();
cacheIRStubKind_ = BaselineCacheIRStubKind::Regular;
trackAttached("IsTypedArrayConstructor");
return AttachDecision::Attach;
}
AttachDecision CallIRGenerator::tryAttachTypedArrayByteOffset(
HandleFunction callee) {
// Self-hosted code calls this with a single TypedArrayObject argument.
@ -7866,6 +7889,8 @@ AttachDecision CallIRGenerator::tryAttachInlinableNative(
return tryAttachIsTypedArray(callee, /* isPossiblyWrapped = */ false);
case InlinableNative::IntrinsicIsPossiblyWrappedTypedArray:
return tryAttachIsTypedArray(callee, /* isPossiblyWrapped = */ true);
case InlinableNative::IntrinsicIsTypedArrayConstructor:
return tryAttachIsTypedArrayConstructor(callee);
case InlinableNative::IntrinsicTypedArrayByteOffset:
return tryAttachTypedArrayByteOffset(callee);
case InlinableNative::IntrinsicTypedArrayElementShift:

Просмотреть файл

@ -1668,6 +1668,7 @@ class MOZ_RAII CallIRGenerator : public IRGenerator {
AttachDecision tryAttachMathMinMax(HandleFunction callee, bool isMax);
AttachDecision tryAttachIsTypedArray(HandleFunction callee,
bool isPossiblyWrapped);
AttachDecision tryAttachIsTypedArrayConstructor(HandleFunction callee);
AttachDecision tryAttachTypedArrayByteOffset(HandleFunction callee);
AttachDecision tryAttachTypedArrayElementShift(HandleFunction callee);
AttachDecision tryAttachTypedArrayLength(HandleFunction callee,

Просмотреть файл

@ -4038,6 +4038,18 @@ bool CacheIRCompiler::emitTypedArrayElementShiftResult(ObjOperandId objId) {
return true;
}
bool CacheIRCompiler::emitIsTypedArrayConstructorResult(ObjOperandId objId) {
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
AutoOutputRegister output(*this);
AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
Register obj = allocator.useRegister(masm, objId);
masm.setIsDefinitelyTypedArrayConstructor(obj, scratch);
masm.tagValue(JSVAL_TYPE_BOOLEAN, scratch, output.valueReg());
return true;
}
bool CacheIRCompiler::emitGetNextMapSetEntryForIteratorResult(
ObjOperandId iterId, ObjOperandId resultArrId, bool isMap) {
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);

Просмотреть файл

@ -962,6 +962,13 @@
obj: ObjId
isPossiblyWrapped: BoolImm
- name: IsTypedArrayConstructorResult
shared: true
transpile: true
cost_estimate: 2
args:
obj: ObjId
- name: TypedArrayByteOffsetResult
shared: true
transpile: true

Просмотреть файл

@ -14511,6 +14511,14 @@ void CodeGenerator::visitInitHomeObject(LInitHomeObject* lir) {
masm.storeValue(homeObject, addr);
}
void CodeGenerator::visitIsTypedArrayConstructor(
LIsTypedArrayConstructor* lir) {
Register object = ToRegister(lir->object());
Register output = ToRegister(lir->output());
masm.setIsDefinitelyTypedArrayConstructor(object, output);
}
template <size_t NumDefs>
void CodeGenerator::emitIonToWasmCallBase(LIonToWasmCallBase<NumDefs>* lir) {
wasm::JitCallStackArgVector stackArgs;

Просмотреть файл

@ -5227,6 +5227,14 @@ void LIRGenerator::visitInitHomeObject(MInitHomeObject* ins) {
add(lir, ins);
}
void LIRGenerator::visitIsTypedArrayConstructor(MIsTypedArrayConstructor* ins) {
MDefinition* object = ins->object();
MOZ_ASSERT(object->type() == MIRType::Object);
auto* lir = new (alloc()) LIsTypedArrayConstructor(useRegister(object));
define(lir, ins);
}
void LIRGenerator::visitConstant(MConstant* ins) {
if (!IsFloatingPointType(ins->type()) && ins->canEmitAtUses()) {
emitAtUses(ins);

Просмотреть файл

@ -11654,6 +11654,24 @@ class MInitHomeObject : public MBinaryInstruction,
}
};
// Return true if the object is definitely a TypedArray constructor, but not
// necessarily from the currently active realm. Return false if the object is
// not a TypedArray constructor or if it's a wrapper.
class MIsTypedArrayConstructor : public MUnaryInstruction,
public SingleObjectPolicy::Data {
explicit MIsTypedArrayConstructor(MDefinition* object)
: MUnaryInstruction(classOpcode, object) {
setResultType(MIRType::Boolean);
}
public:
INSTRUCTION_HEADER(IsTypedArrayConstructor)
TRIVIAL_NEW_WRAPPERS
NAMED_OPERANDS((0, object))
AliasSet getAliasSet() const override { return AliasSet::None(); }
};
// Flips the input's sign bit, independently of the rest of the number's
// payload. Note this is different from multiplying by minus-one, which has
// side-effects for e.g. NaNs.

Просмотреть файл

@ -34,6 +34,7 @@
#include "vm/ArrayBufferViewObject.h"
#include "vm/FunctionFlags.h" // js::FunctionFlags
#include "vm/TraceLogging.h"
#include "vm/TypedArrayObject.h"
#include "gc/Nursery-inl.h"
#include "jit/shared/Lowering-shared-inl.h"
@ -1836,6 +1837,39 @@ void MacroAssembler::setIsCrossRealmArrayConstructor(Register obj,
bind(&done);
}
void MacroAssembler::setIsDefinitelyTypedArrayConstructor(Register obj,
Register output) {
Label isFalse, isTrue, done;
// The object must be a function. (Wrappers are not supported.)
branchTestObjClass(Assembler::NotEqual, obj, &JSFunction::class_, output, obj,
&isFalse);
// Load the native into |output|.
loadPtr(Address(obj, JSFunction::offsetOfNativeOrEnv()), output);
auto branchIsTypedArrayCtor = [&](Scalar::Type type) {
// The function must be a TypedArrayConstructor native (from any realm).
JSNative constructor = TypedArrayConstructorNative(type);
branchPtr(Assembler::Equal, output, ImmPtr(constructor), &isTrue);
};
#define TYPED_ARRAY_CONSTRUCTOR_NATIVE(T, N) branchIsTypedArrayCtor(Scalar::N);
JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_CONSTRUCTOR_NATIVE)
#undef TYPED_ARRAY_CONSTRUCTOR_NATIVE
// Falls through to the false case.
bind(&isFalse);
move32(Imm32(0), output);
jump(&done);
bind(&isTrue);
move32(Imm32(1), output);
bind(&done);
}
void MacroAssembler::guardGroupHasUnanalyzedNewScript(Register group,
Register scratch,
Label* fail) {

Просмотреть файл

@ -3700,6 +3700,8 @@ class MacroAssembler : public MacroAssemblerSpecific {
void setIsCrossRealmArrayConstructor(Register obj, Register output);
void setIsDefinitelyTypedArrayConstructor(Register obj, Register output);
private:
void isCallableOrConstructor(bool isCallable, Register obj, Register output,
Label* isProxy);

Просмотреть файл

@ -2011,6 +2011,17 @@ bool WarpCacheIRTranspiler::emitTypedArrayElementShiftResult(
return true;
}
bool WarpCacheIRTranspiler::emitIsTypedArrayConstructorResult(
ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* ins = MIsTypedArrayConstructor::New(alloc(), obj);
add(ins);
pushResult(ins);
return true;
}
bool WarpCacheIRTranspiler::emitGetNextMapSetEntryForIteratorResult(
ObjOperandId iterId, ObjOperandId resultArrId, bool isMap) {
MDefinition* iter = getOperand(iterId);

Просмотреть файл

@ -7754,6 +7754,18 @@ class LInitHomeObject : public LInstructionHelper<0, 1 + BOX_PIECES, 0> {
const LAllocation* function() { return getOperand(0); }
};
class LIsTypedArrayConstructor : public LInstructionHelper<1, 1, 0> {
public:
LIR_HEADER(IsTypedArrayConstructor)
explicit LIsTypedArrayConstructor(const LAllocation& object)
: LInstructionHelper(classOpcode) {
setOperand(0, object);
}
const LAllocation* object() { return getOperand(0); }
};
template <size_t NumDefs>
class LIonToWasmCallBase : public LVariadicInstruction<NumDefs, 2> {
using Base = LVariadicInstruction<NumDefs, 2>;

Просмотреть файл

@ -2254,33 +2254,18 @@ bool js::IsTypedArrayConstructor(const JSObject* obj) {
return false;
}
bool js::IsTypedArrayConstructor(HandleValue v, uint32_t type) {
switch (type) {
case Scalar::Int8:
return IsNativeFunction(v, Int8Array::class_constructor);
case Scalar::Uint8:
return IsNativeFunction(v, Uint8Array::class_constructor);
case Scalar::Int16:
return IsNativeFunction(v, Int16Array::class_constructor);
case Scalar::Uint16:
return IsNativeFunction(v, Uint16Array::class_constructor);
case Scalar::Int32:
return IsNativeFunction(v, Int32Array::class_constructor);
case Scalar::Uint32:
return IsNativeFunction(v, Uint32Array::class_constructor);
case Scalar::BigInt64:
return IsNativeFunction(v, BigInt64Array::class_constructor);
case Scalar::BigUint64:
return IsNativeFunction(v, BigUint64Array::class_constructor);
case Scalar::Float32:
return IsNativeFunction(v, Float32Array::class_constructor);
case Scalar::Float64:
return IsNativeFunction(v, Float64Array::class_constructor);
case Scalar::Uint8Clamped:
return IsNativeFunction(v, Uint8ClampedArray::class_constructor);
case Scalar::MaxTypedArrayViewType:
break;
bool js::IsTypedArrayConstructor(HandleValue v, Scalar::Type type) {
return IsNativeFunction(v, TypedArrayConstructorNative(type));
}
JSNative js::TypedArrayConstructorNative(Scalar::Type type) {
#define TYPED_ARRAY_CONSTRUCTOR_NATIVE(T, N) \
if (type == Scalar::N) { \
return N##Array::class_constructor; \
}
JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_CONSTRUCTOR_NATIVE)
#undef TYPED_ARRAY_CONSTRUCTOR_NATIVE
MOZ_CRASH("unexpected typed array type");
}

Просмотреть файл

@ -222,7 +222,9 @@ inline Scalar::Type GetTypedArrayClassType(const JSClass* clasp) {
bool IsTypedArrayConstructor(const JSObject* obj);
bool IsTypedArrayConstructor(HandleValue v, uint32_t type);
bool IsTypedArrayConstructor(HandleValue v, Scalar::Type type);
JSNative TypedArrayConstructorNative(Scalar::Type type);
// In WebIDL terminology, a BufferSource is either an ArrayBuffer or a typed
// array view. In either case, extract the dataPointer/byteLength.