Commit 5b3700a4 authored by binji's avatar binji Committed by Commit bot

Atomic operations on Uint8ClampedArray

BUG=chromium:497295
R=jarin@chromium.org
LOG=n

Review URL: https://codereview.chromium.org/1201543002

Cr-Commit-Position: refs/heads/master@{#29199}
parent 4292fa84
......@@ -424,6 +424,79 @@ inline Object* DoExchange(Isolate* isolate, void* buffer, size_t index,
return ToObject<T>(isolate, FromAtomic<T>(result));
}
// Uint8Clamped functions
uint8_t ClampToUint8(int32_t value) {
if (value < 0) return 0;
if (value > 255) return 255;
return value;
}
inline Object* DoCompareExchangeUint8Clamped(Isolate* isolate, void* buffer,
size_t index,
Handle<Object> oldobj,
Handle<Object> newobj) {
typedef int32_t convert_type;
typedef uint8_t atomic_type;
atomic_type oldval = ClampToUint8(FromObject<convert_type>(oldobj));
atomic_type newval = ClampToUint8(FromObject<convert_type>(newobj));
atomic_type result = CompareExchangeSeqCst(
static_cast<atomic_type*>(buffer) + index, oldval, newval);
return ToObject<uint8_t>(isolate, FromAtomic<uint8_t>(result));
}
inline Object* DoStoreUint8Clamped(Isolate* isolate, void* buffer, size_t index,
Handle<Object> obj) {
typedef int32_t convert_type;
typedef uint8_t atomic_type;
atomic_type value = ClampToUint8(FromObject<convert_type>(obj));
StoreSeqCst(static_cast<atomic_type*>(buffer) + index, value);
return *obj;
}
#define DO_UINT8_CLAMPED_OP(name, op) \
inline Object* Do##name##Uint8Clamped(Isolate* isolate, void* buffer, \
size_t index, Handle<Object> obj) { \
typedef int32_t convert_type; \
typedef uint8_t atomic_type; \
atomic_type* p = static_cast<atomic_type*>(buffer) + index; \
convert_type operand = FromObject<convert_type>(obj); \
atomic_type expected; \
atomic_type result; \
do { \
expected = *p; \
result = ClampToUint8(static_cast<convert_type>(expected) op operand); \
} while (CompareExchangeSeqCst(p, expected, result) != expected); \
return ToObject<uint8_t>(isolate, expected); \
}
DO_UINT8_CLAMPED_OP(Add, +)
DO_UINT8_CLAMPED_OP(Sub, -)
DO_UINT8_CLAMPED_OP(And, &)
DO_UINT8_CLAMPED_OP(Or, | )
DO_UINT8_CLAMPED_OP(Xor, ^)
#undef DO_UINT8_CLAMPED_OP
inline Object* DoExchangeUint8Clamped(Isolate* isolate, void* buffer,
size_t index, Handle<Object> obj) {
typedef int32_t convert_type;
typedef uint8_t atomic_type;
atomic_type* p = static_cast<atomic_type*>(buffer) + index;
atomic_type result = ClampToUint8(FromObject<convert_type>(obj));
atomic_type expected;
do {
expected = *p;
} while (CompareExchangeSeqCst(p, expected, result) != expected);
return ToObject<uint8_t>(isolate, expected);
}
} // anonymous namespace
// Duplicated from objects.h
......@@ -434,8 +507,7 @@ inline Object* DoExchange(Isolate* isolate, void* buffer, size_t index,
V(Uint16, uint16, UINT16, uint16_t, 2) \
V(Int16, int16, INT16, int16_t, 2) \
V(Uint32, uint32, UINT32, uint32_t, 4) \
V(Int32, int32, INT32, int32_t, 4) \
V(Uint8Clamped, uint8_clamped, UINT8_CLAMPED, uint8_t, 1)
V(Int32, int32, INT32, int32_t, 4)
RUNTIME_FUNCTION(Runtime_AtomicsCompareExchange) {
......@@ -455,9 +527,19 @@ RUNTIME_FUNCTION(Runtime_AtomicsCompareExchange) {
case kExternal##Type##Array: \
return DoCompareExchange<ctype>(isolate, buffer, index, oldobj, newobj);
TYPED_ARRAYS(TYPED_ARRAY_CASE)
INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
case kExternalFloat32Array:
return DoCompareExchange<float>(isolate, buffer, index, oldobj, newobj);
case kExternalFloat64Array:
return DoCompareExchange<double>(isolate, buffer, index, oldobj, newobj);
case kExternalUint8ClampedArray:
return DoCompareExchangeUint8Clamped(isolate, buffer, index, oldobj,
newobj);
default:
break;
}
......@@ -510,9 +592,18 @@ RUNTIME_FUNCTION(Runtime_AtomicsStore) {
case kExternal##Type##Array: \
return DoStore<ctype>(isolate, buffer, index, value);
TYPED_ARRAYS(TYPED_ARRAY_CASE)
INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
case kExternalFloat32Array:
return DoStore<float>(isolate, buffer, index, value);
case kExternalFloat64Array:
return DoStore<double>(isolate, buffer, index, value);
case kExternalUint8ClampedArray:
return DoStoreUint8Clamped(isolate, buffer, index, value);
default:
break;
}
......@@ -541,6 +632,9 @@ RUNTIME_FUNCTION(Runtime_AtomicsAdd) {
INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
case kExternalUint8ClampedArray:
return DoAddUint8Clamped(isolate, buffer, index, value);
case kExternalFloat32Array:
case kExternalFloat64Array:
default:
......@@ -571,6 +665,9 @@ RUNTIME_FUNCTION(Runtime_AtomicsSub) {
INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
case kExternalUint8ClampedArray:
return DoSubUint8Clamped(isolate, buffer, index, value);
case kExternalFloat32Array:
case kExternalFloat64Array:
default:
......@@ -601,6 +698,9 @@ RUNTIME_FUNCTION(Runtime_AtomicsAnd) {
INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
case kExternalUint8ClampedArray:
return DoAndUint8Clamped(isolate, buffer, index, value);
case kExternalFloat32Array:
case kExternalFloat64Array:
default:
......@@ -631,6 +731,9 @@ RUNTIME_FUNCTION(Runtime_AtomicsOr) {
INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
case kExternalUint8ClampedArray:
return DoOrUint8Clamped(isolate, buffer, index, value);
case kExternalFloat32Array:
case kExternalFloat64Array:
default:
......@@ -661,6 +764,9 @@ RUNTIME_FUNCTION(Runtime_AtomicsXor) {
INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
case kExternalUint8ClampedArray:
return DoXorUint8Clamped(isolate, buffer, index, value);
case kExternalFloat32Array:
case kExternalFloat64Array:
default:
......@@ -691,6 +797,9 @@ RUNTIME_FUNCTION(Runtime_AtomicsExchange) {
INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
case kExternalUint8ClampedArray:
return DoExchangeUint8Clamped(isolate, buffer, index, value);
case kExternalFloat32Array:
case kExternalFloat64Array:
default:
......
......@@ -5,15 +5,37 @@
// Flags: --harmony-atomics --harmony-sharedarraybuffer
//
function toRangeWrapped(value) {
var range = this.max - this.min + 1;
while (value < this.min) {
value += range;
}
while (value > this.max) {
value -= range;
}
return value;
}
function toRangeClamped(value) {
if (value < this.min) return this.min;
if (value > this.max) return this.max;
return value;
}
function makeConstructorObject(constr, min, max, toRange) {
var o = {constr: constr, min: min, max: max};
o.toRange = toRange.bind(o);
return o;
}
var IntegerTypedArrayConstructors = [
{constr: Int8Array, min: -128, max: 127},
{constr: Int16Array, min: -32768, max: 32767},
{constr: Int32Array, min: -0x80000000, max: 0x7fffffff},
{constr: Uint8Array, min: 0, max: 255},
// TODO(binji): support?
// {constr: Uint8ClampedArray, min: 0, max: 255},
{constr: Uint16Array, min: 0, max: 65535},
{constr: Uint32Array, min: 0, max: 0xffffffff},
makeConstructorObject(Int8Array, -128, 127, toRangeWrapped),
makeConstructorObject(Int16Array, -32768, 32767, toRangeWrapped),
makeConstructorObject(Int32Array, -0x80000000, 0x7fffffff, toRangeWrapped),
makeConstructorObject(Uint8Array, 0, 255, toRangeWrapped),
makeConstructorObject(Uint8ClampedArray, 0, 255, toRangeClamped),
makeConstructorObject(Uint16Array, 0, 65535, toRangeWrapped),
makeConstructorObject(Uint32Array, 0, 0xffffffff, toRangeWrapped),
];
var TypedArrayConstructors = IntegerTypedArrayConstructors.concat([
......@@ -141,33 +163,6 @@ function testAtomicOp(op, ia, index, expectedIndex, name) {
}
});
IntegerTypedArrayConstructors.forEach(function(t) {
var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
var sta = new t.constr(sab);
var name = Object.prototype.toString.call(sta);
var range = t.max - t.min + 1;
var add;
var oldVal, oldValWrapped;
var newVal, newValWrapped;
for (add = -range; add <= range; add += range) {
sta[0] = oldVal = 0;
newVal = t.max + add + 1;
newValWrapped = t.min;
assertEquals(oldVal,
Atomics.compareExchange(sta, 0, oldVal, newVal), name);
assertEquals(newValWrapped, sta[0], name);
oldVal = newVal;
oldValWrapped = newValWrapped;
newVal = t.min + add - 1;
newValWrapped = t.max;
assertEquals(oldValWrapped,
Atomics.compareExchange(sta, 0, oldVal, newVal), name);
assertEquals(newValWrapped, sta[0], name);
}
});
// * Exact float values should be OK
// * Infinity, -Infinity should be OK (has exact representation)
// * NaN is not OK, it has many representations, cannot ensure successful CAS
......@@ -219,28 +214,6 @@ function testAtomicOp(op, ia, index, expectedIndex, name) {
}
});
IntegerTypedArrayConstructors.forEach(function(t) {
var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
var sta = new t.constr(sab);
var name = Object.prototype.toString.call(sta);
var range = t.max - t.min + 1;
var add;
var val, valWrapped;
for (add = -range; add <= range; add += range) {
sta[0] = 0;
val = t.max + add + 1;
valWrapped = t.min;
assertEquals(val, Atomics.store(sta, 0, val), name);
assertEquals(valWrapped, sta[0], name);
val = t.min + add - 1;
valWrapped = t.max;
assertEquals(val, Atomics.store(sta, 0, val), name);
assertEquals(valWrapped, sta[0], name);
}
});
[1.5, 4.25, -1e8, -Infinity, Infinity, NaN].forEach(function(v) {
var sab = new SharedArrayBuffer(10 * Float32Array.BYTES_PER_ELEMENT);
var sf32a = new Float32Array(sab);
......@@ -269,24 +242,6 @@ function testAtomicOp(op, ia, index, expectedIndex, name) {
assertEquals(120, sta[i], name);
}
});
IntegerTypedArrayConstructors.forEach(function(t) {
var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
var sta = new t.constr(sab);
var name = Object.prototype.toString.call(sta);
var range = t.max - t.min + 1;
var add;
for (add = -range; add <= range; add += range) {
sta[0] = t.max;
valWrapped = t.min;
assertEquals(t.max, Atomics.add(sta, 0, add + 1), name);
assertEquals(t.min, sta[0], name);
assertEquals(t.min, Atomics.add(sta, 0, add - 1), name);
assertEquals(t.max, sta[0], name);
}
});
})();
(function TestSub() {
......@@ -303,24 +258,6 @@ function testAtomicOp(op, ia, index, expectedIndex, name) {
assertEquals(0, sta[i], name);
}
});
IntegerTypedArrayConstructors.forEach(function(t) {
var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
var sta = new t.constr(sab);
var name = Object.prototype.toString.call(sta);
var range = t.max - t.min + 1;
var add;
for (add = -range; add <= range; add += range) {
sta[0] = t.max;
valWrapped = t.min;
assertEquals(t.max, Atomics.sub(sta, 0, add - 1), name);
assertEquals(t.min, sta[0], name);
assertEquals(t.min, Atomics.sub(sta, 0, add + 1), name);
assertEquals(t.max, sta[0], name);
}
});
})();
(function TestAnd() {
......@@ -337,22 +274,6 @@ function testAtomicOp(op, ia, index, expectedIndex, name) {
assertEquals(0x20, sta[i], name);
}
});
IntegerTypedArrayConstructors.forEach(function(t) {
var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
var sta = new t.constr(sab);
var name = Object.prototype.toString.call(sta);
var range = t.max - t.min + 1;
var add;
// There's no way to wrap results with logical operators, just test that
// using an out-of-range value is properly masked.
for (add = -range; add <= range; add += range) {
sta[0] = 0xf;
assertEquals(0xf, Atomics.and(sta, 0, 0x3 + add), name);
assertEquals(0x3, sta[0], name);
}
});
})();
(function TestOr() {
......@@ -369,22 +290,6 @@ function testAtomicOp(op, ia, index, expectedIndex, name) {
assertEquals(0x3d, sta[i], name);
}
});
IntegerTypedArrayConstructors.forEach(function(t) {
var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
var sta = new t.constr(sab);
var name = Object.prototype.toString.call(sta);
var range = t.max - t.min + 1;
var add;
// There's no way to wrap results with logical operators, just test that
// using an out-of-range value is properly masked.
for (add = -range; add <= range; add += range) {
sta[0] = 0x12;
assertEquals(0x12, Atomics.or(sta, 0, 0x22 + add), name);
assertEquals(0x32, sta[0], name);
}
});
})();
(function TestXor() {
......@@ -401,22 +306,6 @@ function testAtomicOp(op, ia, index, expectedIndex, name) {
assertEquals(0x25, sta[i], name);
}
});
IntegerTypedArrayConstructors.forEach(function(t) {
var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
var sta = new t.constr(sab);
var name = Object.prototype.toString.call(sta);
var range = t.max - t.min + 1;
var add;
// There's no way to wrap results with logical operators, just test that
// using an out-of-range value is properly masked.
for (add = -range; add <= range; add += range) {
sta[0] = 0x12;
assertEquals(0x12, Atomics.xor(sta, 0, 0x22 + add), name);
assertEquals(0x30, sta[0], name);
}
});
})();
(function TestExchange() {
......@@ -433,22 +322,6 @@ function testAtomicOp(op, ia, index, expectedIndex, name) {
assertEquals(0x09, sta[i], name);
}
});
IntegerTypedArrayConstructors.forEach(function(t) {
var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
var sta = new t.constr(sab);
var name = Object.prototype.toString.call(sta);
var range = t.max - t.min + 1;
var add;
// There's no way to wrap results with logical operators, just test that
// using an out-of-range value is properly masked.
for (add = -range; add <= range; add += range) {
sta[0] = 0x12;
assertEquals(0x12, Atomics.exchange(sta, 0, 0x22 + add), name);
assertEquals(0x22, sta[0], name);
}
});
})();
(function TestIsLockFree() {
......@@ -470,3 +343,102 @@ function testAtomicOp(op, ia, index, expectedIndex, name) {
}
}
})();
(function TestWrapping() {
IntegerTypedArrayConstructors.forEach(function(t) {
var sab = new SharedArrayBuffer(10 * t.constr.BYTES_PER_ELEMENT);
var sta = new t.constr(sab);
var name = Object.prototype.toString.call(sta);
var range = t.max - t.min + 1;
var offset;
var operand;
var val, newVal;
var valWrapped, newValWrapped;
for (offset = -range; offset <= range; offset += range) {
// CompareExchange
sta[0] = val = 0;
newVal = val + offset + 1;
newValWrapped = t.toRange(newVal);
assertEquals(val, Atomics.compareExchange(sta, 0, val, newVal), name);
assertEquals(newValWrapped, sta[0], name);
sta[0] = val = t.min;
newVal = val + offset - 1;
newValWrapped = t.toRange(newVal);
assertEquals(val, Atomics.compareExchange(sta, 0, val, newVal), name);
assertEquals(newValWrapped, sta[0], name);
// Store
sta[0] = 0;
val = t.max + offset + 1;
valWrapped = t.toRange(val);
assertEquals(val, Atomics.store(sta, 0, val), name);
assertEquals(valWrapped, sta[0], name);
sta[0] = val = t.min + offset - 1;
valWrapped = t.toRange(val);
assertEquals(val, Atomics.store(sta, 0, val), name);
assertEquals(valWrapped, sta[0], name);
// Add
sta[0] = val = t.max;
operand = offset + 1;
valWrapped = t.toRange(val + operand);
assertEquals(val, Atomics.add(sta, 0, operand), name);
assertEquals(valWrapped, sta[0], name);
sta[0] = val = t.min;
operand = offset - 1;
valWrapped = t.toRange(val + operand);
assertEquals(val, Atomics.add(sta, 0, operand), name);
assertEquals(valWrapped, sta[0], name);
// Sub
sta[0] = val = t.max;
operand = offset - 1;
valWrapped = t.toRange(val - operand);
assertEquals(val, Atomics.sub(sta, 0, operand), name);
assertEquals(valWrapped, sta[0], name);
sta[0] = val = t.min;
operand = offset + 1;
valWrapped = t.toRange(val - operand);
assertEquals(val, Atomics.sub(sta, 0, operand), name);
assertEquals(valWrapped, sta[0], name);
// There's no way to wrap results with logical operators, just test that
// using an out-of-range value is properly wrapped/clamped when written
// to memory.
// And
sta[0] = val = 0xf;
operand = 0x3 + offset;
valWrapped = t.toRange(val & operand);
assertEquals(val, Atomics.and(sta, 0, operand), name);
assertEquals(valWrapped, sta[0], name);
// Or
sta[0] = val = 0x12;
operand = 0x22 + offset;
valWrapped = t.toRange(val | operand);
assertEquals(val, Atomics.or(sta, 0, operand), name);
assertEquals(valWrapped, sta[0], name);
// Xor
sta[0] = val = 0x12;
operand = 0x22 + offset;
valWrapped = t.toRange(val ^ operand);
assertEquals(val, Atomics.xor(sta, 0, operand), name);
assertEquals(valWrapped, sta[0], name);
// Exchange
sta[0] = val = 0x12;
operand = 0x22 + offset;
valWrapped = t.toRange(operand);
assertEquals(val, Atomics.exchange(sta, 0, operand), name);
assertEquals(valWrapped, sta[0], name);
}
});
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment