Bug 851792: IonMonkey: Add stub for TypedArray with JSAtom index in IonCache, r=jandem

This commit is contained in:
Hannes Verschore 2013-03-28 15:25:01 +01:00
parent 6425f8158a
commit a4522f2a9b
4 changed files with 213 additions and 18 deletions

View File

@ -1737,7 +1737,6 @@ GetElementIC::attachTypedArrayElement(JSContext *cx, IonScript *ion, JSObject *o
const Value &idval)
{
JS_ASSERT(obj->isTypedArray());
JS_ASSERT(idval.isInt32());
Label failures;
MacroAssembler masm(cx);
@ -1759,19 +1758,55 @@ GetElementIC::attachTypedArrayElement(JSContext *cx, IonScript *ion, JSObject *o
// because load size differ in function of the typed array data width.
masm.branchTestObjClass(Assembler::NotEqual, object(), tmpReg, obj->getClass(), &failures);
// Ensure the index is an int32 value.
// Decide to what type index the stub should be optimized
Register indexReg = tmpReg;
JS_ASSERT(!index().constant());
if (index().reg().hasValue()) {
ValueOperand val = index().reg().valueReg();
masm.branchTestInt32(Assembler::NotEqual, val, &failures);
if (idval.isString()) {
JS_ASSERT(GetIndexFromString(idval.toString()) != UINT32_MAX);
// Part 1: Get the string into a register
Register str;
if (index().reg().hasValue()) {
ValueOperand val = index().reg().valueReg();
masm.branchTestString(Assembler::NotEqual, val, &failures);
str = masm.extractString(val, indexReg);
} else {
JS_ASSERT(!index().reg().typedReg().isFloat());
str = index().reg().typedReg().gpr();
}
// Part 2: Call to translate the str into index
RegisterSet regs = RegisterSet::Volatile();
masm.PushRegsInMask(regs);
regs.maybeTake(str);
Register temp = regs.takeGeneral();
masm.setupUnalignedABICall(1, temp);
masm.passABIArg(str);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, GetIndexFromString));
masm.mov(ReturnReg, indexReg);
RegisterSet ignore = RegisterSet();
ignore.add(indexReg);
masm.PopRegsInMaskIgnore(RegisterSet::Volatile(), ignore);
masm.branch32(Assembler::Equal, indexReg, Imm32(UINT32_MAX), &failures);
// Unbox the index.
masm.unboxInt32(val, indexReg);
} else {
JS_ASSERT(!index().reg().typedReg().isFloat());
indexReg = index().reg().typedReg().gpr();
JS_ASSERT(idval.isInt32());
if (index().reg().hasValue()) {
ValueOperand val = index().reg().valueReg();
masm.branchTestInt32(Assembler::NotEqual, val, &failures);
// Unbox the index.
masm.unboxInt32(val, indexReg);
} else {
JS_ASSERT(!index().reg().typedReg().isFloat());
indexReg = index().reg().typedReg().gpr();
}
}
// Guard on the initialized length.
@ -1850,14 +1885,18 @@ GetElementIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
if (!cache.attachDenseElement(cx, ion, obj, idval))
return false;
attachedStub = true;
} else if (obj->isTypedArray() && idval.isInt32()) {
int arrayType = TypedArray::type(obj);
bool floatOutput = arrayType == TypedArray::TYPE_FLOAT32 ||
arrayType == TypedArray::TYPE_FLOAT64;
if (!floatOutput || cache.output().hasValue()) {
if (!cache.attachTypedArrayElement(cx, ion, obj, idval))
return false;
attachedStub = true;
} else if (obj->isTypedArray()) {
if ((idval.isInt32()) ||
(idval.isString() && GetIndexFromString(idval.toString()) != UINT32_MAX))
{
int arrayType = TypedArray::type(obj);
bool floatOutput = arrayType == TypedArray::TYPE_FLOAT32 ||
arrayType == TypedArray::TYPE_FLOAT64;
if (!floatOutput || cache.output().hasValue()) {
if (!cache.attachTypedArrayElement(cx, ion, obj, idval))
return false;
attachedStub = true;
}
}
}
}

View File

@ -567,5 +567,24 @@ FilterArguments(JSContext *cx, JSString *str)
return !StringHasPattern(chars, str->length(), arguments, mozilla::ArrayLength(arguments));
}
uint32_t
GetIndexFromString(JSString *str)
{
// Masks the return value UINT32_MAX as failure to get the index.
// I.e. it is impossible to distinguish between failing to get the index
// or the actual index UINT32_MAX.
if (!str->isAtom())
return UINT32_MAX;
uint32_t index;
JSAtom *atom = &str->asAtom();
if (!atom->isIndex(&index))
return UINT32_MAX;
return index;
}
} // namespace ion
} // namespace js

View File

@ -478,6 +478,8 @@ void GetDynamicName(JSContext *cx, JSObject *scopeChain, JSString *str, Value *v
JSBool FilterArguments(JSContext *cx, JSString *str);
uint32_t GetIndexFromString(JSString *str);
} // namespace ion
} // namespace js

View File

@ -0,0 +1,135 @@
// Based on tests/ion/typed-arrays-1.js, but with string indexes
function testInt8() {
var arr1 = new Int8Array(50);
var arr2 = new Uint8Array(50);
var arr3 = new Uint8ClampedArray(50);
for (var i=0; i<arr1.length; i++) {
arr1[i] = arr2[i] = arr3[i] = i * 8;
}
var res = 0;
for (var i=0; i<arr1.length; i++) {
res += arr1[i+""] + arr2[i+""] + arr3[i+""] + arr2["10".concat("")];
}
assertEq(res, 18334);
}
testInt8();
function testInt16() {
var arr1 = new Int16Array(70);
var arr2 = new Uint16Array(70);
for (var i=0; i<arr1.length; i++) {
arr1[i] = arr2[i] = i * 1000;
}
var res = 0;
for (var i=0; i<arr1.length; i++) {
res += arr1[i+""] + arr2[i+""] + arr2["1".concat("")] + arr1["3".concat("")];
}
assertEq(res, 2423024);
}
testInt16();
function testInt32() {
var arr = new Int32Array(60);
arr[0] = -50;
for (var i=1; i<arr.length; i++) {
arr[i] = arr[(i-1)+""] + arr["0".concat("")];
++arr[0];
}
assertEq(arr[(arr.length-1)+""], -1289);
}
testInt32();
function testUint32() {
function sum(arr) {
var res = 0;
for (var i=0; i<arr.length; i++) {
res += arr[i+""];
}
return res;
}
var arr = new Uint32Array(100);
for (var i=0; i<arr.length; i++) {
arr[i] = i;
}
// Compile sum() to read int32 values.
assertEq(sum(arr), 4950);
// Add a large uint32 so that the sum no longer fits in an
// int32. sum() should be recompiled to return a double.
arr[50] = 0xffffeeee;
assertEq(sum(arr), 4294967826);
}
testUint32();
function testFloat() {
var arr1 = new Float32Array(75);
var arr2 = new Float64Array(75);
arr1[0] = arr2[0] = Math.PI * 1234567.8;
for (var i=1; i<75; i++) {
arr1[i] = arr1[(i-1)+""] + arr1[0];
arr2[i] = arr2[(i-1)+""] + arr2[0];
}
assertEq(arr1["74".concat("")] > 290888255, true);
assertEq(arr1["74".concat("")] < 290888257, true);
assertEq(arr2["74".concat("")] > 290888184, true);
assertEq(arr2["74".concat("")] < 290888185, true);
}
testFloat();
function testCanonicalNaN() {
// NaN values have to be canonicalized. Otherwise, malicious scripts could
// construct arbitrary Value's (due to our NaN boxing Value representation).
var buf = new ArrayBuffer(16);
var uint32 = new Uint32Array(buf);
var f64 = new Float64Array(buf);
var f32 = new Float32Array(buf);
// Evil: write a JSVAL_TYPE_OBJECT type tag...
uint32[0] = 0xffffff87;
uint32[1] = 0xffffff87;
// Make sure this value is interpreted as a double.
for (var i=0; i<3; i++) {
assertEq(isNaN(f64["0".concat("")]), true);
assertEq(isNaN(f32["0".concat("")]), true);
}
}
testCanonicalNaN();
function testOutOfBounds() {
var buf = new ArrayBuffer(16);
var uint32 = new Uint32Array(buf);
uint32[0] = 0;
uint32[1] = 1;
for (var i=0; i<3; i++) {
assertEq(uint32["0".concat("")], 0);
assertEq(uint32["1".concat("")], 1);
assertEq(uint32["2".concat("")], 0);
assertEq(uint32["17".concat("")], undefined);
}
}
testOutOfBounds();
function testStrangeIndexes() {
var buf = new ArrayBuffer(16);
var uint32 = new Uint32Array(buf);
uint32[0] = 0;
uint32[1] = 1;
indexes = ["0", "1", "2", "3", "17", "3.5", "NaN", "undefined", "null"];
solutions = [0, 1, 0, 0, undefined, undefined, undefined, undefined, undefined];
for (var i=0; i<indexes.length; i++) {
assertEq(uint32[indexes[i]], solutions[i]);
}
}
testStrangeIndexes();