[PATCH v2] msvcrt: Avoid disallowed unaligned writes in memset on ARM
Martin Storsjö
martin at martin.st
Thu Sep 16 04:25:40 CDT 2021
This fixes a regression in memset on ARM since
7b17d7081512db52ef852705445762ac4016c29f.
ARM can do 64 bit writes with the STRD instruction, but that
instruction requires a 32 bit aligned address - while these stores
are unaligned.
Two consecutive stores to uint32_t* pointers can also be fused
into one single STRD, as a uint32_t* is supposed to be properly
aligned - therefore, do these stores as stores to volatile uint32_t*
to avoid fusing them.
Signed-off-by: Martin Storsjö <martin at martin.st>
---
v2: Using a macro to hide the difference.
---
dlls/msvcrt/string.c | 32 ++++++++++++++++++++++----------
1 file changed, 22 insertions(+), 10 deletions(-)
diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c
index f2b1b4a5b11..6fade4d80fc 100644
--- a/dlls/msvcrt/string.c
+++ b/dlls/msvcrt/string.c
@@ -2876,17 +2876,28 @@ void *__cdecl memset(void *dst, int c, size_t n)
unsigned char *d = (unsigned char *)dst;
size_t a = 0x20 - ((uintptr_t)d & 0x1f);
+#ifdef __arm__
+#define MEMSET_UNALIGNED_64(dst, off, val) do { \
+ *(volatile uint32_t *)((dst) + (off) + 0) = val; \
+ *(volatile uint32_t *)((dst) + (off) + 4) = val; \
+ } while (0)
+#else
+#define MEMSET_UNALIGNED_64(dst, off, val) do { \
+ *(uint64_t *)((dst) + (off)) = val; \
+ } while (0)
+#endif
+
if (n >= 16)
{
- *(uint64_t *)(d + 0) = v;
- *(uint64_t *)(d + 8) = v;
- *(uint64_t *)(d + n - 16) = v;
- *(uint64_t *)(d + n - 8) = v;
+ MEMSET_UNALIGNED_64(d, 0, v);
+ MEMSET_UNALIGNED_64(d, 8, v);
+ MEMSET_UNALIGNED_64(d, n - 16, v);
+ MEMSET_UNALIGNED_64(d, n - 8, v);
if (n <= 32) return dst;
- *(uint64_t *)(d + 16) = v;
- *(uint64_t *)(d + 24) = v;
- *(uint64_t *)(d + n - 32) = v;
- *(uint64_t *)(d + n - 24) = v;
+ MEMSET_UNALIGNED_64(d, 16, v);
+ MEMSET_UNALIGNED_64(d, 24, v);
+ MEMSET_UNALIGNED_64(d, n - 32, v);
+ MEMSET_UNALIGNED_64(d, n - 24, v);
if (n <= 64) return dst;
n = (n - a) & ~0x1f;
@@ -2895,8 +2906,8 @@ void *__cdecl memset(void *dst, int c, size_t n)
}
if (n >= 8)
{
- *(uint64_t *)d = v;
- *(uint64_t *)(d + n - 8) = v;
+ MEMSET_UNALIGNED_64(d, 0, v);
+ MEMSET_UNALIGNED_64(d, n - 8, v);
return dst;
}
if (n >= 4)
@@ -2916,6 +2927,7 @@ void *__cdecl memset(void *dst, int c, size_t n)
*(uint8_t *)d = v;
return dst;
}
+#undef MEMSET_UNALIGNED_64
return dst;
}
--
2.25.1
More information about the wine-devel
mailing list