Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions compiler-rt/lib/builtins/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,10 @@ set(arm_EABI_RT_SOURCES
arm/aeabi_ldivmod.S
arm/aeabi_uidivmod.S
arm/aeabi_uldivmod.S
arm/aeabi_uread4.c
arm/aeabi_uread8.c
arm/aeabi_uwrite4.c
arm/aeabi_uwrite8.c
)

set(arm_EABI_CLIB_SOURCES
Expand Down
21 changes: 21 additions & 0 deletions compiler-rt/lib/builtins/arm/aeabi_uread4.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- aeabi_uread4.c - ARM EABI Helper — Unaligned 4-Byte Memory Read --===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===-----------------------------------------------------------------------------===//
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably a good idea to link to the document defining these functions.
https://github.com/ARM-software/abi-aa/blob/main/rtabi32/rtabi32.rst#unaligned-memory-access

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


typedef struct {
char v[4];
} v4;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, to avoid breaking TBAA rules, this needs to be __attribute__((__may_alias__)). Not sure if it's likely to matter in practice.


int __aeabi_uread4(void *p) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to say that surely the read functions should take a const void *, but in RTABI, they don't! I suppose because nobody ever calls these functions from C, so the C prototype isn't really important, only the machine-level PCS that it translates into.

union {
v4 v;
int u;
} u;

u.v = *(v4 *)p;
return u.u;
}
21 changes: 21 additions & 0 deletions compiler-rt/lib/builtins/arm/aeabi_uread8.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- aeabi_uread8.c - ARM EABI Helper — Unaligned 8-Byte Memory Read --===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===-----------------------------------------------------------------------------===//

typedef struct {
char v[8];
} v8;

long long __aeabi_uread8(void *p) {
union {
v8 v;
long long u;
} u;

u.v = *(v8 *)p;
return u.u;
}
23 changes: 23 additions & 0 deletions compiler-rt/lib/builtins/arm/aeabi_uwrite4.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//===-- aeabi_uread8.c - ARM EABI Helper — Unaligned 4-Byte Memory Write --===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===-----------------------------------------------------------------------------===//

typedef struct {
char v[4];
} v4;

int __aeabi_uwrite4(int val, void *p) {
union {
v4 v;
int u;
} u;

u.u = val;
*(v4 *)p = u.v;

return val;
}
23 changes: 23 additions & 0 deletions compiler-rt/lib/builtins/arm/aeabi_uwrite8.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//===-- aeabi_uread8.c - ARM EABI Helper — Unaligned 8-Byte Memory Write --===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===-----------------------------------------------------------------------------===//

typedef struct {
char v[8];
} v8;

long long __aeabi_uwrite8(long long val, void *p) {
union {
v8 v;
long long u;
} u;

u.u = val;
*(v8 *)p = u.v;

return val;
}
88 changes: 88 additions & 0 deletions compiler-rt/test/builtins/Unit/arm/aeabi_unaligned_access_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// REQUIRES: arm-target-arch || armv6m-target-arch
// RUN: %clang_builtins %s %librt -o %t && %run %t

#include <stdint.h>
#include <stdio.h>
#include <string.h>

extern int __aeabi_uread4(void *);
extern int __aeabi_uwrite4(int, void *);
extern long long __aeabi_uread8(void *);
extern long long __aeabi_uwrite8(long long, void *);

#define lenof(x) (sizeof((x)) / sizeof(*(x)))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's 2025, so we can use _Countof for this! \o/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


int test_unaligned(void) {
long long target8;
int target4;
const char source[] = "abcdefghijklmno";
static char dest1[lenof(source)], dest2[lenof(source)];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why static?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using static is optional. It’s used here mainly to improve reproducibility — since it provides a stable address from which we can repeatably test unaligned writes.

int i, j;

for (i = 0; i < 7; i++) {
memcpy(&target8, source + i, 8);
if (__aeabi_uread8(source + i) != target8) {
printf("error in __aeabi_uread8 => output = %llx, expected %llx\n",
__aeabi_uread8(source + i), target8);
return 1;
}

memcpy(dest1, source, lenof(source));
memcpy(dest2, source, lenof(source));
target8 = 0x4142434445464748ULL;
if (__aeabi_uwrite8(target8, dest1 + i) != target8) {
printf("error in __aeabi_uwrite8 => output = %llx, expected %llx\n",
__aeabi_uwrite8(target8, dest1 + i), target8);
return 1;
}
memcpy(dest2 + i, &target8, 8);
if (memcmp(dest1, dest2, lenof(source)) != 0) {
int pos = -1;
printf("error in __aeabi_uwrite8: memcmp failed: buffers differ!\n");
for (int j = 0; j < 8; ++j) {
if (dest1[j] != dest2[j]) {
pos = j;
break;
}
}
printf("error: 8-byte write mismatch at offset %d\n", pos);
return 1;
}

memcpy(&target4, source + i, 4);
if (__aeabi_uread4(source + i) != target4) {
printf("error in __aeabi_uread4 => output = %x, expected %x\n",
__aeabi_uread4(source + i), target4);
return 1;
}

memcpy(dest1, source, lenof(source));
memcpy(dest2, source, lenof(source));
target4 = 0x414243444;
if (__aeabi_uwrite4(target4, dest1 + i) != target4) {
printf("error in __aeabi_uwrite4 => output = %x, expected %x\n",
__aeabi_uwrite4(target4, dest1 + i), target4);
return 1;
}
memcpy(dest2 + i, &target4, 4);
if (memcmp(dest1, dest2, lenof(source)) != 0) {
int pos = -1;
printf("error in __aeabi_uwrite4: memcmp failed: buffers differ!\n");
for (int j = 0; j < 4; ++j) {
if (dest1[j] != dest2[j]) {
pos = j;
break;
}
}
printf("error: 4-byte write mismatch at offset %d\n", pos);
return 1;
}
}
return 0;
}

int main() {
if (test_unaligned())
return 1;
return 0;
}
Loading