4
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
6
7
+ #include " mozilla/Assertions.h"
8
+ #include " mozilla/Attributes.h"
9
+ #include " mozilla/MathAlgorithms.h"
10
+
11
+ #include < atomic>
12
+ #include < stddef.h>
13
+ #include < stdint.h>
14
+ #include < stdlib.h>
15
+ #include < tuple>
16
+ #include < utility>
17
+
7
18
#include " jit/AtomicOperations.h"
19
+ #include " js/GCAPI.h"
8
20
9
21
#if defined(__arm__)
10
22
# include " jit/arm/Architecture-arm.h"
11
23
#endif
12
24
13
25
#ifdef JS_HAVE_GENERATED_ATOMIC_OPS
14
26
15
- # include < atomic>
16
-
17
- # include " js/GCAPI.h"
18
-
19
27
using namespace js ;
20
28
using namespace js ::jit;
21
29
@@ -70,6 +78,64 @@ void AtomicCompilerFence() {
70
78
}
71
79
# endif
72
80
81
+ /* *
82
+ * Return `true` if all pointers are aligned to `Alignment`.
83
+ */
84
+ template <size_t Alignment>
85
+ static inline bool CanCopyAligned (const uint8_t * dest, const uint8_t * src,
86
+ const uint8_t * lim) {
87
+ static_assert (mozilla::IsPowerOfTwo (Alignment));
88
+ return ((uintptr_t (dest) | uintptr_t (src) | uintptr_t (lim)) &
89
+ (Alignment - 1 )) == 0 ;
90
+ }
91
+
92
+ /* *
93
+ * Return `true` if both pointers have the same alignment and can be aligned to
94
+ * `Alignment`.
95
+ */
96
+ template <size_t Alignment>
97
+ static inline bool CanAlignTo (const uint8_t * dest, const uint8_t * src) {
98
+ static_assert (mozilla::IsPowerOfTwo (Alignment));
99
+ return ((uintptr_t (dest) ^ uintptr_t (src)) & (Alignment - 1 )) == 0 ;
100
+ }
101
+
102
+ /* *
103
+ * Copy a datum smaller than `WORDSIZE`. Prevents tearing when `dest` and `src`
104
+ * are both aligned.
105
+ *
106
+ * No tearing is a requirement for integer TypedArrays.
107
+ *
108
+ * https://tc39.es/ecma262/#sec-isnotearconfiguration
109
+ * https://tc39.es/ecma262/#sec-tear-free-aligned-reads
110
+ * https://tc39.es/ecma262/#sec-valid-executions
111
+ */
112
+ static MOZ_ALWAYS_INLINE auto AtomicCopyDownNoTearIfAlignedUnsynchronized (
113
+ uint8_t * dest, const uint8_t * src, const uint8_t * srcEnd) {
114
+ MOZ_ASSERT (src <= srcEnd);
115
+ MOZ_ASSERT (size_t (srcEnd - src) < WORDSIZE);
116
+
117
+ if (WORDSIZE > 4 && CanCopyAligned<4 >(dest, src, srcEnd)) {
118
+ static_assert (WORDSIZE <= 8 , " copies 32-bits at most once" );
119
+
120
+ if (src < srcEnd) {
121
+ AtomicCopy32Unsynchronized (dest, src);
122
+ dest += 4 ;
123
+ src += 4 ;
124
+ }
125
+ } else if (CanCopyAligned<2 >(dest, src, srcEnd)) {
126
+ while (src < srcEnd) {
127
+ AtomicCopy16Unsynchronized (dest, src);
128
+ dest += 2 ;
129
+ src += 2 ;
130
+ }
131
+ } else {
132
+ while (src < srcEnd) {
133
+ AtomicCopy8Unsynchronized (dest++, src++);
134
+ }
135
+ }
136
+ return std::pair{dest, src};
137
+ }
138
+
73
139
void AtomicMemcpyDownUnsynchronized (uint8_t * dest, const uint8_t * src,
74
140
size_t nbytes) {
75
141
JS::AutoSuppressGCAnalysis nogc;
@@ -85,12 +151,14 @@ void AtomicMemcpyDownUnsynchronized(uint8_t* dest, const uint8_t* src,
85
151
void (*copyBlock)(uint8_t * dest, const uint8_t * src);
86
152
void (*copyWord)(uint8_t * dest, const uint8_t * src);
87
153
88
- if ((( uintptr_t ( dest) ^ uintptr_t ( src)) & WORDMASK) == 0 ) {
154
+ if (CanAlignTo<WORDSIZE>( dest, src)) {
89
155
const uint8_t * cutoff = (const uint8_t *)RoundUp (uintptr_t (src), WORDSIZE);
90
156
MOZ_ASSERT (cutoff <= lim); // because nbytes >= WORDSIZE
91
- while (src < cutoff) {
92
- AtomicCopyByteUnsynchronized (dest++, src++);
93
- }
157
+
158
+ // Copy initial bytes to align to word size.
159
+ std::tie (dest, src) =
160
+ AtomicCopyDownNoTearIfAlignedUnsynchronized (dest, src, cutoff);
161
+
94
162
copyBlock = AtomicCopyBlockDownUnsynchronized;
95
163
copyWord = AtomicCopyWordUnsynchronized;
96
164
} else if (UnalignedAccessesAreOK ()) {
@@ -118,11 +186,46 @@ void AtomicMemcpyDownUnsynchronized(uint8_t* dest, const uint8_t* src,
118
186
}
119
187
}
120
188
121
- // Byte copy any remaining tail.
189
+ // Copy any remaining tail.
122
190
123
- while (src < lim) {
124
- AtomicCopyByteUnsynchronized (dest++, src++);
191
+ AtomicCopyDownNoTearIfAlignedUnsynchronized (dest, src, lim);
192
+ }
193
+
194
+ /* *
195
+ * Copy a datum smaller than `WORDSIZE`. Prevents tearing when `dest` and `src`
196
+ * are both aligned.
197
+ *
198
+ * No tearing is a requirement for integer TypedArrays.
199
+ *
200
+ * https://tc39.es/ecma262/#sec-isnotearconfiguration
201
+ * https://tc39.es/ecma262/#sec-tear-free-aligned-reads
202
+ * https://tc39.es/ecma262/#sec-valid-executions
203
+ */
204
+ static MOZ_ALWAYS_INLINE auto AtomicCopyUpNoTearIfAlignedUnsynchronized (
205
+ uint8_t * dest, const uint8_t * src, const uint8_t * srcBegin) {
206
+ MOZ_ASSERT (src >= srcBegin);
207
+ MOZ_ASSERT (size_t (src - srcBegin) < WORDSIZE);
208
+
209
+ if (WORDSIZE > 4 && CanCopyAligned<4 >(dest, src, srcBegin)) {
210
+ static_assert (WORDSIZE <= 8 , " copies 32-bits at most once" );
211
+
212
+ if (src > srcBegin) {
213
+ dest -= 4 ;
214
+ src -= 4 ;
215
+ AtomicCopy32Unsynchronized (dest, src);
216
+ }
217
+ } else if (CanCopyAligned<2 >(dest, src, srcBegin)) {
218
+ while (src > srcBegin) {
219
+ dest -= 2 ;
220
+ src -= 2 ;
221
+ AtomicCopy16Unsynchronized (dest, src);
222
+ }
223
+ } else {
224
+ while (src > srcBegin) {
225
+ AtomicCopy8Unsynchronized (--dest, --src);
226
+ }
125
227
}
228
+ return std::pair{dest, src};
126
229
}
127
230
128
231
void AtomicMemcpyUpUnsynchronized (uint8_t * dest, const uint8_t * src,
@@ -134,16 +237,23 @@ void AtomicMemcpyUpUnsynchronized(uint8_t* dest, const uint8_t* src,
134
237
src += nbytes;
135
238
dest += nbytes;
136
239
240
+ // Set up bulk copying. The cases are ordered the way they are on the
241
+ // assumption that if we can achieve aligned copies even with a little
242
+ // preprocessing then that is better than unaligned copying on a platform
243
+ // that supports it.
244
+
137
245
if (nbytes >= WORDSIZE) {
138
246
void (*copyBlock)(uint8_t * dest, const uint8_t * src);
139
247
void (*copyWord)(uint8_t * dest, const uint8_t * src);
140
248
141
- if ((( uintptr_t ( dest) ^ uintptr_t ( src)) & WORDMASK) == 0 ) {
249
+ if (CanAlignTo<WORDSIZE>( dest, src)) {
142
250
const uint8_t * cutoff = (const uint8_t *)(uintptr_t (src) & ~WORDMASK);
143
251
MOZ_ASSERT (cutoff >= lim); // Because nbytes >= WORDSIZE
144
- while (src > cutoff) {
145
- AtomicCopyByteUnsynchronized (--dest, --src);
146
- }
252
+
253
+ // Copy initial bytes to align to word size.
254
+ std::tie (dest, src) =
255
+ AtomicCopyUpNoTearIfAlignedUnsynchronized (dest, src, cutoff);
256
+
147
257
copyBlock = AtomicCopyBlockUpUnsynchronized;
148
258
copyWord = AtomicCopyWordUnsynchronized;
149
259
} else if (UnalignedAccessesAreOK ()) {
@@ -154,6 +264,8 @@ void AtomicMemcpyUpUnsynchronized(uint8_t* dest, const uint8_t* src,
154
264
copyWord = AtomicCopyUnalignedWordUpUnsynchronized;
155
265
}
156
266
267
+ // Bulk copy, first larger blocks and then individual words.
268
+
157
269
const uint8_t * blocklim = src - ((src - lim) & ~BLOCKMASK);
158
270
while (src > blocklim) {
159
271
dest -= BLOCKSIZE;
@@ -169,9 +281,9 @@ void AtomicMemcpyUpUnsynchronized(uint8_t* dest, const uint8_t* src,
169
281
}
170
282
}
171
283
172
- while (src > lim) {
173
- AtomicCopyByteUnsynchronized (--dest, --src);
174
- }
284
+ // Copy any remaining tail.
285
+
286
+ AtomicCopyUpNoTearIfAlignedUnsynchronized (dest, src, lim);
175
287
}
176
288
177
289
} // namespace jit
0 commit comments