summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/atomics/memcpy-fidelity.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/atomics/memcpy-fidelity.js')
-rw-r--r--js/src/jit-test/tests/atomics/memcpy-fidelity.js181
1 files changed, 181 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/atomics/memcpy-fidelity.js b/js/src/jit-test/tests/atomics/memcpy-fidelity.js
new file mode 100644
index 0000000000..81eb63fba2
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/memcpy-fidelity.js
@@ -0,0 +1,181 @@
+// In order not to run afoul of C++ UB we have our own non-C++ definitions of
+// operations (they are actually jitted) that can operate racily on shared
+// memory, see jit/shared/AtomicOperations-shared-jit.cpp.
+//
+// Operations on fixed-width 1, 2, 4, and 8 byte data are adequately tested
+// elsewhere. Here we specifically test our safe-when-racy replacements of
+// memcpy and memmove.
+//
+// There are two primitives in the engine, memcpy_down and memcpy_up. These are
+// equivalent except when data overlap, in which case memcpy_down handles
+// overlapping copies that move from higher to lower addresses and memcpy_up
+// handles ditto from lower to higher. memcpy uses memcpy_down always while
+// memmove selects the one to use dynamically based on its arguments.
+
+// Basic memcpy algorithm to be tested:
+//
+// - if src and target have the same alignment
+// - byte copy up to word alignment
+// - block copy as much as possible
+// - word copy as much as possible
+// - byte copy any tail
+// - else if on a platform that can deal with unaligned access
+// (ie, x86, ARM64, and ARM if the proper flag is set)
+// - block copy as much as possible
+// - word copy as much as possible
+// - byte copy any tail
+// - else // on a platform that can't deal with unaligned access
+// (ie ARM without the flag or x86 DEBUG builds with the
+// JS_NO_UNALIGNED_MEMCPY env var)
+// - block copy with byte copies
+// - word copy with byte copies
+// - byte copy any tail
+
+var target_buf = new SharedArrayBuffer(1024);
+var src_buf = new SharedArrayBuffer(1024);
+
+///////////////////////////////////////////////////////////////////////////
+//
+// Different src and target buffer, this is memcpy "move down". The same
+// code is used in the engine for overlapping buffers when target addresses
+// are lower than source addresses.
+
+fill(src_buf);
+
+// Basic 1K perfectly aligned copy, copies blocks only.
+{
+ let target = new Uint8Array(target_buf);
+ let src = new Uint8Array(src_buf);
+ clear(target_buf);
+ target.set(src);
+ check(target_buf, 0, 1024, 0);
+}
+
+// Buffers are equally aligned but not on a word boundary and not ending on a
+// word boundary either, so this will copy first some bytes, then some blocks,
+// then some words, and then some bytes.
+{
+ let fill = 0x79;
+ clear(target_buf, fill);
+ let target = new Uint8Array(target_buf, 1, 1022);
+ let src = new Uint8Array(src_buf, 1, 1022);
+ target.set(src);
+ check_fill(target_buf, 0, 1, fill);
+ check(target_buf, 1, 1023, 1);
+ check_fill(target_buf, 1023, 1024, fill);
+}
+
+// Buffers are unequally aligned, we'll copy bytes only on some platforms and
+// unaligned blocks/words on others.
+{
+ clear(target_buf);
+ let target = new Uint8Array(target_buf, 0, 1023);
+ let src = new Uint8Array(src_buf, 1);
+ target.set(src);
+ check(target_buf, 0, 1023, 1);
+ check_zero(target_buf, 1023, 1024);
+}
+
+///////////////////////////////////////////////////////////////////////////
+//
+// Overlapping src and target buffer and the target addresses are always
+// higher than the source addresses, this is memcpy "move up"
+
+// Buffers are equally aligned but not on a word boundary and not ending on a
+// word boundary either, so this will copy first some bytes, then some blocks,
+// then some words, and then some bytes.
+{
+ fill(target_buf);
+ let target = new Uint8Array(target_buf, 9, 999);
+ let src = new Uint8Array(target_buf, 1, 999);
+ target.set(src);
+ check(target_buf, 9, 1008, 1);
+ check(target_buf, 1008, 1024, 1008 & 255);
+}
+
+// Buffers are unequally aligned, we'll copy bytes only on some platforms and
+// unaligned blocks/words on others.
+{
+ fill(target_buf);
+ let target = new Uint8Array(target_buf, 2, 1022);
+ let src = new Uint8Array(target_buf, 1, 1022);
+ target.set(src);
+ check(target_buf, 2, 1024, 1);
+}
+
+///////////////////////////////////////////////////////////////////////////
+//
+// Copy 0 to 127 bytes from and to a variety of addresses to check that we
+// handle limits properly in these edge cases.
+
+// Too slow in debug-noopt builds but we don't want to flag the test as slow,
+// since that means it'll never be run.
+
+if (this.getBuildConfiguration && !getBuildConfiguration().debug)
+{
+ let t = new Uint8Array(target_buf);
+ for (let my_src_buf of [src_buf, target_buf]) {
+ for (let size=0; size < 127; size++) {
+ for (let src_offs=0; src_offs < 8; src_offs++) {
+ for (let target_offs=0; target_offs < 8; target_offs++) {
+ clear(target_buf, Math.random()*255);
+ let target = new Uint8Array(target_buf, target_offs, size);
+
+ // Zero is boring
+ let bias = (Math.random() * 100 % 12) | 0;
+
+ // Note src may overlap target partially
+ let src = new Uint8Array(my_src_buf, src_offs, size);
+ for ( let i=0; i < size; i++ )
+ src[i] = i+bias;
+
+ // We expect these values to be unchanged by the copy
+ let below = target_offs > 0 ? t[target_offs - 1] : 0;
+ let above = t[target_offs + size];
+
+ // Copy
+ target.set(src);
+
+ // Verify
+ check(target_buf, target_offs, target_offs + size, bias);
+ if (target_offs > 0)
+ assertEq(t[target_offs-1], below);
+ assertEq(t[target_offs+size], above);
+ }
+ }
+ }
+ }
+}
+
+
+// Utilities
+
+function clear(buf, fill) {
+ let a = new Uint8Array(buf);
+ for ( let i=0; i < a.length; i++ )
+ a[i] = fill;
+}
+
+function fill(buf) {
+ let a = new Uint8Array(buf);
+ for ( let i=0; i < a.length; i++ )
+ a[i] = i & 255
+}
+
+function check(buf, from, to, startingWith) {
+ let a = new Uint8Array(buf);
+ for ( let i=from; i < to; i++ ) {
+ assertEq(a[i], startingWith);
+ startingWith = (startingWith + 1) & 255;
+ }
+}
+
+function check_zero(buf, from, to) {
+ check_fill(buf, from, to, 0);
+}
+
+function check_fill(buf, from, to, fill) {
+ let a = new Uint8Array(buf);
+ for ( let i=from; i < to; i++ )
+ assertEq(a[i], fill);
+}