Skip to content

Commit eaa91ed

Browse files
committed
Use memcpy for cache reads/writes
1 parent 1756ffd commit eaa91ed

File tree

1 file changed

+20
-74
lines changed

1 file changed

+20
-74
lines changed

Include/internal/pycore_code.h

Lines changed: 20 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -285,110 +285,56 @@ PyAPI_FUNC(PyObject*) _Py_GetSpecializationStats(void);
285285
#define EVAL_CALL_STAT_INC_IF_FUNCTION(name, callable) ((void)0)
286286
#endif // !Py_STATS
287287

288-
// Cache values are only valid in memory, so use native endianness.
289-
#ifdef WORDS_BIGENDIAN
288+
// NOTE: These cache reading/writing utilities use memcpy to avoid voilating C's
289+
// strict aliasing rules, while also avoiding the need to maintain big-endian
290+
// versions of the same code. Compilers are smart enough to understand what
291+
// we're really trying to do here (see https://blog.regehr.org/archives/959).
292+
293+
// When modifying these, great care must be taken to ensure that we don't break
294+
// or slow down our inline caching! All of these functions should compile to
295+
// simple "move" instructions on all supported compilers and platforms. You can
296+
// use the Compiler Explorer at https://godbolt.org to help verify this.
290297

291298
static inline void
292299
write_u32(uint16_t *p, uint32_t val)
293300
{
294-
p[0] = (uint16_t)(val >> 16);
295-
p[1] = (uint16_t)(val >> 0);
301+
memcpy(p, &val, sizeof(val));
296302
}
297303

298304
static inline void
299305
write_u64(uint16_t *p, uint64_t val)
300306
{
301-
p[0] = (uint16_t)(val >> 48);
302-
p[1] = (uint16_t)(val >> 32);
303-
p[2] = (uint16_t)(val >> 16);
304-
p[3] = (uint16_t)(val >> 0);
305-
}
306-
307-
static inline uint32_t
308-
read_u32(uint16_t *p)
309-
{
310-
uint32_t val = 0;
311-
val |= (uint32_t)p[0] << 16;
312-
val |= (uint32_t)p[1] << 0;
313-
return val;
314-
}
315-
316-
static inline uint64_t
317-
read_u64(uint16_t *p)
318-
{
319-
uint64_t val = 0;
320-
val |= (uint64_t)p[0] << 48;
321-
val |= (uint64_t)p[1] << 32;
322-
val |= (uint64_t)p[2] << 16;
323-
val |= (uint64_t)p[3] << 0;
324-
return val;
325-
}
326-
327-
#else
328-
329-
static inline void
330-
write_u32(uint16_t *p, uint32_t val)
331-
{
332-
p[0] = (uint16_t)(val >> 0);
333-
p[1] = (uint16_t)(val >> 16);
307+
memcpy(p, &val, sizeof(val));
334308
}
335309

336310
static inline void
337-
write_u64(uint16_t *p, uint64_t val)
311+
write_obj(uint16_t *p, PyObject *val)
338312
{
339-
p[0] = (uint16_t)(val >> 0);
340-
p[1] = (uint16_t)(val >> 16);
341-
p[2] = (uint16_t)(val >> 32);
342-
p[3] = (uint16_t)(val >> 48);
313+
memcpy(p, &val, sizeof(val));
343314
}
344315

345316
static inline uint32_t
346317
read_u32(uint16_t *p)
347318
{
348-
uint32_t val = 0;
349-
val |= (uint32_t)p[0] << 0;
350-
val |= (uint32_t)p[1] << 16;
319+
uint32_t val;
320+
memcpy(&val, p, sizeof(val));
351321
return val;
352322
}
353323

354324
static inline uint64_t
355325
read_u64(uint16_t *p)
356326
{
357-
uint64_t val = 0;
358-
val |= (uint64_t)p[0] << 0;
359-
val |= (uint64_t)p[1] << 16;
360-
val |= (uint64_t)p[2] << 32;
361-
val |= (uint64_t)p[3] << 48;
327+
uint64_t val;
328+
memcpy(&val, p, sizeof(val));
362329
return val;
363330
}
364331

365-
#endif
366-
367-
static inline void
368-
write_obj(uint16_t *p, PyObject *obj)
369-
{
370-
uintptr_t val = (uintptr_t)obj;
371-
#if SIZEOF_VOID_P == 8
372-
write_u64(p, val);
373-
#elif SIZEOF_VOID_P == 4
374-
write_u32(p, val);
375-
#else
376-
#error "SIZEOF_VOID_P must be 4 or 8"
377-
#endif
378-
}
379-
380332
static inline PyObject *
381333
read_obj(uint16_t *p)
382334
{
383-
uintptr_t val;
384-
#if SIZEOF_VOID_P == 8
385-
val = read_u64(p);
386-
#elif SIZEOF_VOID_P == 4
387-
val = read_u32(p);
388-
#else
389-
#error "SIZEOF_VOID_P must be 4 or 8"
390-
#endif
391-
return (PyObject *)val;
335+
PyObject *val;
336+
memcpy(&val, p, sizeof(val));
337+
return val;
392338
}
393339

394340
/* See Objects/exception_handling_notes.txt for details.

0 commit comments

Comments
 (0)