summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/atomics/memcpy-fidelity.js
blob: 81eb63fba28a3802e92e08b622ba2ebb3b2c0478 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
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);
}