memcpy-fidelity.js (6266B)
1 // In order not to run afoul of C++ UB we have our own non-C++ definitions of 2 // operations (they are actually jitted) that can operate racily on shared 3 // memory, see jit/shared/AtomicOperations-shared-jit.cpp. 4 // 5 // Operations on fixed-width 1, 2, 4, and 8 byte data are adequately tested 6 // elsewhere. Here we specifically test our safe-when-racy replacements of 7 // memcpy and memmove. 8 // 9 // There are two primitives in the engine, memcpy_down and memcpy_up. These are 10 // equivalent except when data overlap, in which case memcpy_down handles 11 // overlapping copies that move from higher to lower addresses and memcpy_up 12 // handles ditto from lower to higher. memcpy uses memcpy_down always while 13 // memmove selects the one to use dynamically based on its arguments. 14 15 // Basic memcpy algorithm to be tested: 16 // 17 // - if src and target have the same alignment 18 // - byte copy up to word alignment 19 // - block copy as much as possible 20 // - word copy as much as possible 21 // - byte copy any tail 22 // - else if on a platform that can deal with unaligned access 23 // (ie, x86, ARM64, and ARM if the proper flag is set) 24 // - block copy as much as possible 25 // - word copy as much as possible 26 // - byte copy any tail 27 // - else // on a platform that can't deal with unaligned access 28 // (ie ARM without the flag or x86 DEBUG builds with the 29 // JS_NO_UNALIGNED_MEMCPY env var) 30 // - block copy with byte copies 31 // - word copy with byte copies 32 // - byte copy any tail 33 34 var target_buf = new SharedArrayBuffer(1024); 35 var src_buf = new SharedArrayBuffer(1024); 36 37 /////////////////////////////////////////////////////////////////////////// 38 // 39 // Different src and target buffer, this is memcpy "move down". The same 40 // code is used in the engine for overlapping buffers when target addresses 41 // are lower than source addresses. 42 43 fill(src_buf); 44 45 // Basic 1K perfectly aligned copy, copies blocks only. 46 { 47 let target = new Uint8Array(target_buf); 48 let src = new Uint8Array(src_buf); 49 clear(target_buf); 50 target.set(src); 51 check(target_buf, 0, 1024, 0); 52 } 53 54 // Buffers are equally aligned but not on a word boundary and not ending on a 55 // word boundary either, so this will copy first some bytes, then some blocks, 56 // then some words, and then some bytes. 57 { 58 let fill = 0x79; 59 clear(target_buf, fill); 60 let target = new Uint8Array(target_buf, 1, 1022); 61 let src = new Uint8Array(src_buf, 1, 1022); 62 target.set(src); 63 check_fill(target_buf, 0, 1, fill); 64 check(target_buf, 1, 1023, 1); 65 check_fill(target_buf, 1023, 1024, fill); 66 } 67 68 // Buffers are unequally aligned, we'll copy bytes only on some platforms and 69 // unaligned blocks/words on others. 70 { 71 clear(target_buf); 72 let target = new Uint8Array(target_buf, 0, 1023); 73 let src = new Uint8Array(src_buf, 1); 74 target.set(src); 75 check(target_buf, 0, 1023, 1); 76 check_zero(target_buf, 1023, 1024); 77 } 78 79 /////////////////////////////////////////////////////////////////////////// 80 // 81 // Overlapping src and target buffer and the target addresses are always 82 // higher than the source addresses, this is memcpy "move up" 83 84 // Buffers are equally aligned but not on a word boundary and not ending on a 85 // word boundary either, so this will copy first some bytes, then some blocks, 86 // then some words, and then some bytes. 87 { 88 fill(target_buf); 89 let target = new Uint8Array(target_buf, 9, 999); 90 let src = new Uint8Array(target_buf, 1, 999); 91 target.set(src); 92 check(target_buf, 9, 1008, 1); 93 check(target_buf, 1008, 1024, 1008 & 255); 94 } 95 96 // Buffers are unequally aligned, we'll copy bytes only on some platforms and 97 // unaligned blocks/words on others. 98 { 99 fill(target_buf); 100 let target = new Uint8Array(target_buf, 2, 1022); 101 let src = new Uint8Array(target_buf, 1, 1022); 102 target.set(src); 103 check(target_buf, 2, 1024, 1); 104 } 105 106 /////////////////////////////////////////////////////////////////////////// 107 // 108 // Copy 0 to 127 bytes from and to a variety of addresses to check that we 109 // handle limits properly in these edge cases. 110 111 // Too slow in debug-noopt builds but we don't want to flag the test as slow, 112 // since that means it'll never be run. 113 114 if (this.getBuildConfiguration && !getBuildConfiguration("debug")) 115 { 116 let t = new Uint8Array(target_buf); 117 for (let my_src_buf of [src_buf, target_buf]) { 118 for (let size=0; size < 127; size++) { 119 for (let src_offs=0; src_offs < 8; src_offs++) { 120 for (let target_offs=0; target_offs < 8; target_offs++) { 121 clear(target_buf, Math.random()*255); 122 let target = new Uint8Array(target_buf, target_offs, size); 123 124 // Zero is boring 125 let bias = (Math.random() * 100 % 12) | 0; 126 127 // Note src may overlap target partially 128 let src = new Uint8Array(my_src_buf, src_offs, size); 129 for ( let i=0; i < size; i++ ) 130 src[i] = i+bias; 131 132 // We expect these values to be unchanged by the copy 133 let below = target_offs > 0 ? t[target_offs - 1] : 0; 134 let above = t[target_offs + size]; 135 136 // Copy 137 target.set(src); 138 139 // Verify 140 check(target_buf, target_offs, target_offs + size, bias); 141 if (target_offs > 0) 142 assertEq(t[target_offs-1], below); 143 assertEq(t[target_offs+size], above); 144 } 145 } 146 } 147 } 148 } 149 150 151 // Utilities 152 153 function clear(buf, fill) { 154 let a = new Uint8Array(buf); 155 for ( let i=0; i < a.length; i++ ) 156 a[i] = fill; 157 } 158 159 function fill(buf) { 160 let a = new Uint8Array(buf); 161 for ( let i=0; i < a.length; i++ ) 162 a[i] = i & 255 163 } 164 165 function check(buf, from, to, startingWith) { 166 let a = new Uint8Array(buf); 167 for ( let i=from; i < to; i++ ) { 168 assertEq(a[i], startingWith); 169 startingWith = (startingWith + 1) & 255; 170 } 171 } 172 173 function check_zero(buf, from, to) { 174 check_fill(buf, from, to, 0); 175 } 176 177 function check_fill(buf, from, to, fill) { 178 let a = new Uint8Array(buf); 179 for ( let i=from; i < to; i++ ) 180 assertEq(a[i], fill); 181 }