Bug 1248163 - Inline typed array constructors r=jandem r=Waldo

This commit is contained in:
Sander Mathijs van Veen 2016-07-03 08:24:00 +02:00
parent 677cf917fd
commit 8988e365a7
13 changed files with 308 additions and 35 deletions

View File

@ -0,0 +1,13 @@
function f () {
var x = new Uint8Array(4);
empty();
assertRecoveredOnBailout(x, true);
var res = inIon();
bailout();
return res;
}
function empty() {
}
while(!f());

View File

@ -32,6 +32,7 @@
#include "vm/Opcodes.h"
#include "vm/SelfHosting.h"
#include "vm/TypedArrayCommon.h"
#include "vm/TypedArrayObject.h"
#include "jsboolinlines.h"
#include "jsscriptinlines.h"
@ -5465,7 +5466,7 @@ GetTemplateObjectForSimd(JSContext* cx, JSFunction* target, MutableHandleObject
}
static bool
GetTemplateObjectForNative(JSContext* cx, JSFunction* target, const CallArgs& args,
GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs& args,
MutableHandleObject res, bool* skipAttach)
{
Native native = target->native();
@ -5501,6 +5502,12 @@ GetTemplateObjectForNative(JSContext* cx, JSFunction* target, const CallArgs& ar
}
}
if (args.length() == 1 && args[0].isInt32() && args[0].toInt32() >= 0) {
uint32_t len = args[0].toInt32();
if (TypedArrayObject::GetTemplateObjectForNative(cx, native, len, res))
return !!res;
}
if (native == js::array_slice) {
if (args.thisv().isObject()) {
JSObject* obj = &args.thisv().toObject();

View File

@ -5446,6 +5446,12 @@ typedef PlainObject* (*ObjectCreateWithTemplateFn)(JSContext*, HandlePlainObject
static const VMFunction ObjectCreateWithTemplateInfo =
FunctionInfo<ObjectCreateWithTemplateFn>(ObjectCreateWithTemplate, "ObjectCreateWithTemplate");
typedef TypedArrayObject* (*TypedArrayCreateWithTemplateFn)(JSContext*, HandleObject);
static const VMFunction TypedArrayCreateWithTemplateInfo =
FunctionInfo<TypedArrayCreateWithTemplateFn>(TypedArrayCreateWithTemplate,
"TypedArrayCreateWithTemplate");
void
CodeGenerator::visitNewObjectVMCall(LNewObject* lir)
{
@ -5456,11 +5462,17 @@ CodeGenerator::visitNewObjectVMCall(LNewObject* lir)
JSObject* templateObject = lir->mir()->templateObject();
MNewObject::Mode mode_ = lir->mir()->mode();
MOZ_ASSERT_IF(mode_ != MNewObject::TypedArray && templateObject,
!templateObject->is<TypedArrayObject>());
// If we're making a new object with a class prototype (that is, an object
// that derives its class from its prototype instead of being
// PlainObject::class_'d) from self-hosted code, we need a different init
// function.
if (lir->mir()->mode() == MNewObject::ObjectLiteral) {
switch (mode_) {
case MNewObject::ObjectLiteral:
if (templateObject) {
pushArg(ImmGCPtr(templateObject));
callVM(NewInitObjectWithTemplateInfo, lir);
@ -5470,10 +5482,15 @@ CodeGenerator::visitNewObjectVMCall(LNewObject* lir)
pushArg(ImmGCPtr(lir->mir()->block()->info().script()));
callVM(NewInitObjectInfo, lir);
}
} else {
MOZ_ASSERT(lir->mir()->mode() == MNewObject::ObjectCreate);
break;
case MNewObject::ObjectCreate:
pushArg(ImmGCPtr(templateObject));
callVM(ObjectCreateWithTemplateInfo, lir);
break;
case MNewObject::TypedArray:
pushArg(ImmGCPtr(templateObject));
callVM(TypedArrayCreateWithTemplateInfo, lir);
break;
}
if (ReturnReg != objReg)

View File

@ -126,6 +126,7 @@
_(IntrinsicArrayBufferByteLength) \
_(IntrinsicPossiblyWrappedArrayBufferByteLength) \
\
_(TypedArrayConstructor) \
_(IntrinsicIsTypedArray) \
_(IntrinsicIsPossiblyWrappedTypedArray) \
_(IntrinsicTypedArrayLength) \

View File

@ -890,6 +890,7 @@ class IonBuilder
// TypedArray intrinsics.
enum WrappingBehavior { AllowWrappedTypedArrays, RejectWrappedTypedArrays };
InliningStatus inlineTypedArray(CallInfo& callInfo, Native native);
InliningStatus inlineIsTypedArrayHelper(CallInfo& callInfo, WrappingBehavior wrappingBehavior);
InliningStatus inlineIsTypedArray(CallInfo& callInfo);
InliningStatus inlineIsPossiblyWrappedTypedArray(CallInfo& callInfo);

View File

@ -4,6 +4,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/Casting.h"
#include "jsmath.h"
#include "jsobj.h"
#include "jsstr.h"
@ -21,6 +23,7 @@
#include "vm/ArgumentsObject.h"
#include "vm/ProxyObject.h"
#include "vm/SelfHosting.h"
#include "vm/TypedArrayObject.h"
#include "jsscriptinlines.h"
@ -30,6 +33,7 @@
#include "vm/UnboxedObject-inl.h"
using mozilla::ArrayLength;
using mozilla::AssertedCast;
using JS::DoubleNaNValue;
using JS::TrackedOutcome;
@ -298,6 +302,8 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
return inlinePossiblyWrappedArrayBufferByteLength(callInfo);
// TypedArray intrinsics.
case InlinableNative::TypedArrayConstructor:
return inlineTypedArray(callInfo, target->native());
case InlinableNative::IntrinsicIsTypedArray:
return inlineIsTypedArray(callInfo);
case InlinableNative::IntrinsicIsPossiblyWrappedTypedArray:
@ -2275,6 +2281,78 @@ IonBuilder::inlinePossiblyWrappedArrayBufferByteLength(CallInfo& callInfo)
return InliningStatus_Inlined;
}
IonBuilder::InliningStatus
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() != 1)
return InliningStatus_NotInlined;
MDefinition* arg = callInfo.getArg(0);
if (arg->type() != MIRType::Int32)
return InliningStatus_NotInlined;
if (!arg->maybeConstantValue())
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>();
// Do not optimize when we see a template object with a singleton type,
// since it hits at most once.
if (templateObject->isSingleton())
return InliningStatus_NotInlined;
// Negative lengths must throw a RangeError. (We don't track that this
// might have previously thrown, when determining whether to inline, so we
// have to deal with this error case when inlining.)
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;
// Large typed arrays have a separate buffer object, while small arrays
// have their values stored inline.
bool createBuffer = len > TypedArrayObject::INLINE_BUFFER_LIMIT / obj->bytesPerElement();
// Buffers are not supported yet!
if (createBuffer)
return InliningStatus_NotInlined;
callInfo.setImplicitlyUsedUnchecked();
MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), obj);
current->add(templateConst);
MNewObject* ins = MNewObject::New(alloc(), constraints(), templateConst,
obj->group()->initialHeap(constraints()),
MNewObject::TypedArray);
current->add(ins);
current->push(ins);
if (!resumeAfter(ins))
return InliningStatus_Error;
return InliningStatus_Inlined;
}
IonBuilder::InliningStatus
IonBuilder::inlineIsTypedArrayHelper(CallInfo& callInfo, WrappingBehavior wrappingBehavior)
{

View File

@ -3279,7 +3279,7 @@ class MNewObject
public NoTypePolicy::Data
{
public:
enum Mode { ObjectLiteral, ObjectCreate };
enum Mode { ObjectLiteral, ObjectCreate, TypedArray };
private:
gc::InitialHeap initialHeap_;

View File

@ -1128,16 +1128,47 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject* templateObj,
} else {
// If the target type could be a TypedArray that maps shared memory
// then this would need to store emptyObjectElementsShared in that case.
// That cannot happen at present; TypedArray allocation is always
// a VM call.
MOZ_ASSERT(!ntemplate->isSharedMemory());
storePtr(ImmPtr(emptyObjectElements), Address(obj, NativeObject::offsetOfElements()));
initGCSlots(obj, temp, ntemplate, initContents);
if (ntemplate->hasPrivate()) {
uint32_t nfixed = ntemplate->numFixedSlots();
storePtr(ImmPtr(ntemplate->getPrivate()),
Address(obj, NativeObject::getPrivateDataOffset(nfixed)));
if (ntemplate->is<TypedArrayObject>()) {
TypedArrayObject* ttemplate = &ntemplate->as<TypedArrayObject>();
MOZ_ASSERT(ntemplate->hasPrivate());
MOZ_ASSERT(!ttemplate->hasBuffer());
size_t dataSlotOffset = TypedArrayObject::dataOffset();
size_t dataOffset = TypedArrayObject::dataOffset() + sizeof(HeapSlot);
static_assert(TypedArrayObject::FIXED_DATA_START == TypedArrayObject::DATA_SLOT + 1,
"fixed inline element data assumed to begin after the data slot");
computeEffectiveAddress(Address(obj, dataOffset), temp);
storePtr(temp, Address(obj, dataSlotOffset));
// Initialise inline data elements to zero.
size_t n = ttemplate->length() * ttemplate->bytesPerElement();
MOZ_ASSERT(dataOffset + n <= JSObject::MAX_BYTE_SIZE);
// Write enough zero pointers into fixed data to zero every
// element. (This zeroes past the end of a byte count that's
// not a multiple of pointer size. That's okay, because fixed
// data is a count of 8-byte HeapSlots (i.e. <= pointer size),
// and we won't inline unless the desired memory fits in that
// space.)
static_assert(sizeof(HeapSlot) == 8, "Assumed 8 bytes alignment");
size_t numZeroPointers = ((n + 7) & ~0x7) / sizeof(char *);
for (size_t i = 0; i < numZeroPointers; i++)
storePtr(ImmWord(0), Address(obj, dataOffset + i * sizeof(char *)));
} else {
if (ntemplate->hasPrivate()) {
uint32_t nfixed = ntemplate->numFixedSlots();
storePtr(ImmPtr(ntemplate->getPrivate()),
Address(obj, NativeObject::getPrivateDataOffset(nfixed)));
}
}
}
} else if (templateObj->is<InlineTypedObject>()) {

View File

@ -1202,11 +1202,16 @@ RNewObject::recover(JSContext* cx, SnapshotIterator& iter) const
JSObject* resultObject = nullptr;
// See CodeGenerator::visitNewObjectVMCall
if (mode_ == MNewObject::ObjectLiteral) {
switch (mode_) {
case MNewObject::ObjectLiteral:
resultObject = NewObjectOperationWithTemplate(cx, templateObject);
} else {
MOZ_ASSERT(mode_ == MNewObject::ObjectCreate);
break;
case MNewObject::ObjectCreate:
resultObject = ObjectCreateWithTemplate(cx, templateObject.as<PlainObject>());
break;
case MNewObject::TypedArray:
resultObject = TypedArrayCreateWithTemplate(cx, templateObject.as<TypedArrayObject>());
break;
}
if (!resultObject)

View File

@ -20,6 +20,7 @@ class DeclEnvObject;
class StaticWithScope;
class InlineTypedObject;
class GeneratorObject;
class TypedArrayObject;
namespace jit {
@ -281,6 +282,7 @@ template <> struct TypeToDataType<PlainObject*> { static const DataType result =
template <> struct TypeToDataType<InlineTypedObject*> { static const DataType result = Type_Object; };
template <> struct TypeToDataType<DeclEnvObject*> { static const DataType result = Type_Object; };
template <> struct TypeToDataType<ArrayObject*> { static const DataType result = Type_Object; };
template <> struct TypeToDataType<TypedArrayObject*> { static const DataType result = Type_Object; };
template <> struct TypeToDataType<JSString*> { static const DataType result = Type_Object; };
template <> struct TypeToDataType<JSFlatString*> { static const DataType result = Type_Object; };
template <> struct TypeToDataType<HandleObject> { static const DataType result = Type_Handle; };

View File

@ -205,18 +205,15 @@ ObjectGroup::useSingletonForAllocationSite(JSScript* script, jsbytecode* pc, JSP
/*
* Objects created outside loops in global and eval scripts should have
* singleton types. For now this is only done for plain objects and typed
* arrays, but not normal arrays.
* singleton types. For now this is only done for plain objects, but not
* typed arrays or normal arrays.
*/
if (script->functionNonDelazifying() && !script->treatAsRunOnce())
return GenericObject;
if (key != JSProto_Object &&
!(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray))
{
if (key != JSProto_Object)
return GenericObject;
}
// All loops in the script will have a try note indicating their boundary.

View File

@ -7,6 +7,7 @@
#include "vm/TypedArrayObject.h"
#include "mozilla/Alignment.h"
#include "mozilla/Casting.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/PodOperations.h"
@ -31,6 +32,7 @@
#include "builtin/TypedObjectConstants.h"
#include "gc/Barrier.h"
#include "gc/Marking.h"
#include "jit/InlinableNatives.h"
#include "js/Conversions.h"
#include "vm/ArrayBufferObject.h"
#include "vm/GlobalObject.h"
@ -49,6 +51,7 @@
using namespace js;
using namespace js::gc;
using mozilla::AssertedCast;
using JS::CanonicalizeNaN;
using JS::ToInt32;
using JS::ToUint32;
@ -175,6 +178,17 @@ template<typename ElementType>
static inline JSObject*
NewArray(JSContext* cx, uint32_t nelements);
#define JS_FOR_EACH_TYPED_ARRAY(macro) \
macro(int8_t, Int8) \
macro(uint8_t, Uint8) \
macro(int16_t, Int16) \
macro(uint16_t, Uint16) \
macro(int32_t, Int32) \
macro(uint32_t, Uint32) \
macro(float, Float32) \
macro(double, Float64) \
macro(uint8_clamped, Uint8Clamped)
namespace {
// We allow nullptr for newTarget for all the creation methods, to allow for
@ -225,11 +239,16 @@ class TypedArrayObjectTemplate : public TypedArrayObject
if (!ctorProto)
return nullptr;
return NewFunctionWithProto(cx, class_constructor, 3,
JSFunction::NATIVE_CTOR, nullptr,
ClassName(key, cx),
ctorProto, gc::AllocKind::FUNCTION,
SingletonObject);
JSFunction* fun = NewFunctionWithProto(cx, class_constructor, 3,
JSFunction::NATIVE_CTOR, nullptr,
ClassName(key, cx),
ctorProto, gc::AllocKind::FUNCTION,
SingletonObject);
if (fun)
fun->setJitInfo(&jit::JitInfo_TypedArrayConstructor);
return fun;
}
static bool
@ -438,6 +457,45 @@ class TypedArrayObjectTemplate : public TypedArrayObject
return makeInstance(cx, buffer, byteOffset, len, proto);
}
static TypedArrayObject*
makeTemplateObject(JSContext* cx, uint32_t len, NewObjectKind newKind)
{
MOZ_ASSERT(len <= TypedArrayObject::INLINE_BUFFER_LIMIT / sizeof(NativeType));
gc::AllocKind allocKind = AllocKindForLazyBuffer(len * sizeof(NativeType));
AutoSetNewObjectMetadata metadata(cx);
MOZ_ASSERT(len * sizeof(NativeType) < TypedArrayObject::SINGLETON_BYTE_LENGTH);
const Class* clasp = instanceClass();
jsbytecode* pc;
RootedScript script(cx, cx->currentScript(&pc));
if (script && ObjectGroup::useSingletonForAllocationSite(script, pc, clasp))
newKind = SingletonObject;
RootedObject tmp(cx, NewBuiltinClassInstance(cx, clasp, allocKind, newKind));
if (!tmp)
return nullptr;
if (script && !ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, tmp,
newKind == SingletonObject))
{
return nullptr;
}
TypedArrayObject* tarray = &tmp->as<TypedArrayObject>();
void* data = tarray->fixedData(FIXED_DATA_START);
tarray->initPrivate(data);
memset(data, 0, len * sizeof(NativeType));
tarray->setFixedSlot(TypedArrayObject::BUFFER_SLOT, NullValue());
tarray->setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(AssertedCast<int32_t>(len)));
tarray->setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(0));
// Verify that the private slot is at the expected place.
MOZ_ASSERT(tarray->numFixedSlots() == TypedArrayObject::DATA_SLOT);
return tarray;
}
/*
* new [Type]Array(length)
* new [Type]Array(otherTypedArray)
@ -728,18 +786,52 @@ class TypedArrayObjectTemplate : public TypedArrayObject
static Value getIndexValue(JSObject* tarray, uint32_t index);
};
typedef TypedArrayObjectTemplate<int8_t> Int8Array;
typedef TypedArrayObjectTemplate<uint8_t> Uint8Array;
typedef TypedArrayObjectTemplate<int16_t> Int16Array;
typedef TypedArrayObjectTemplate<uint16_t> Uint16Array;
typedef TypedArrayObjectTemplate<int32_t> Int32Array;
typedef TypedArrayObjectTemplate<uint32_t> Uint32Array;
typedef TypedArrayObjectTemplate<float> Float32Array;
typedef TypedArrayObjectTemplate<double> Float64Array;
typedef TypedArrayObjectTemplate<uint8_clamped> Uint8ClampedArray;
#define CREATE_TYPE_FOR_TYPED_ARRAY(T, N) \
typedef TypedArrayObjectTemplate<T> N##Array;
JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPE_FOR_TYPED_ARRAY)
#undef CREATE_TYPE_FOR_TYPED_ARRAY
} /* anonymous namespace */
static void
UpdateTypedArrayAfterMove(JSObject* obj, const JSObject* old)
{
MOZ_ASSERT(obj->is<TypedArrayObject>());
MOZ_ASSERT(old->is<TypedArrayObject>());
TypedArrayObject* newObj = &obj->as<TypedArrayObject>();
const TypedArrayObject* oldObj = &old->as<TypedArrayObject>();
// Typed arrays with a buffer or small typed arrays with a *shared* buffer object are not
// supported yet!
if (oldObj->hasBuffer())
return;
// Update the data slot pointer if it points to the old JSObject.
void* oldData = newObj->fixedData(TypedArrayObject::FIXED_DATA_START);
if (oldData == ((char *)oldObj) + oldObj->dataOffset()) {
void* newData = newObj->fixedData(TypedArrayObject::FIXED_DATA_START);
*(void **)((((char *)newObj) + newObj->dataOffset())) = newData;
}
}
TypedArrayObject*
js::TypedArrayCreateWithTemplate(JSContext* cx, HandleObject templateObj)
{
MOZ_ASSERT(templateObj->is<TypedArrayObject>());
TypedArrayObject* obj = &templateObj->as<TypedArrayObject>();
size_t len = obj->length();
switch (obj->type()) {
#define CREATE_TYPED_ARRAY(T, N) \
case Scalar::N: \
return TypedArrayObjectTemplate<T>::makeTemplateObject(cx, len, GenericObject);
JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPED_ARRAY)
#undef CREATE_TYPED_ARRAY
default:
MOZ_CRASH("Unsupported TypedArray type");
}
}
template<typename T>
struct TypedArrayObject::OfType
{
@ -1021,6 +1113,22 @@ TypedArrayConstructor(JSContext* cx, unsigned argc, Value* vp)
return false;
}
/* static */ bool
TypedArrayObject::GetTemplateObjectForNative(JSContext* cx, Native native, uint32_t len,
MutableHandleObject res)
{
#define CHECK_TYPED_ARRAY_CONSTRUCTOR(T, N) \
if (native == &TypedArrayObjectTemplate<T>::class_constructor && \
len <= TypedArrayObject::INLINE_BUFFER_LIMIT / sizeof(T)) \
{ \
res.set(TypedArrayObjectTemplate<T>::makeTemplateObject(cx, len, TenuredObject)); \
return true; \
}
JS_FOR_EACH_TYPED_ARRAY(CHECK_TYPED_ARRAY_CONSTRUCTOR)
#undef CHECK_TYPED_ARRAY_CONSTRUCTOR
return false;
}
/*
* These next 3 functions are brought to you by the buggy GCC we use to build
* B2G ICS. Older GCC versions have a bug in which they fail to compile
@ -2196,6 +2304,11 @@ static const ClassOps TypedArrayClassOps = {
TypedArrayObject::trace, /* trace */
};
static const ClassExtension TypedArrayClassExtension = {
nullptr,
UpdateTypedArrayAfterMove,
};
#define IMPL_TYPED_ARRAY_CLASS_SPEC(_type) \
{ \
_type##Array::createConstructor, \
@ -2228,7 +2341,8 @@ static const ClassSpec TypedArrayObjectClassSpecs[Scalar::MaxTypedArrayViewType]
JSCLASS_HAS_CACHED_PROTO(JSProto_##_type##Array) | \
JSCLASS_DELAY_METADATA_BUILDER, \
&TypedArrayClassOps, \
&TypedArrayObjectClassSpecs[Scalar::Type::_type] \
&TypedArrayObjectClassSpecs[Scalar::Type::_type], \
&TypedArrayClassExtension \
}
const Class TypedArrayObject::classes[Scalar::MaxTypedArrayViewType] = {

View File

@ -155,6 +155,10 @@ class TypedArrayObject : public NativeObject
void notifyBufferDetached(void* newData);
static bool
GetTemplateObjectForNative(JSContext* cx, Native native, uint32_t len,
MutableHandleObject res);
/*
* Byte length above which created typed arrays and data views will have
* singleton types regardless of the context in which they are created.
@ -267,6 +271,9 @@ class TypedArrayObject : public NativeObject
static bool set(JSContext* cx, unsigned argc, Value* vp);
};
extern TypedArrayObject*
TypedArrayCreateWithTemplate(JSContext* cx, HandleObject templateObj);
inline bool
IsTypedArrayClass(const Class* clasp)
{