[compiler-rt] [compiler-rt][ARM] Make ARMv4T assembly builtins interwork (PR #221725)

Ties Stuij via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 7 06:21:13 PDT 2026


https://github.com/stuij created https://github.com/llvm/llvm-project/pull/221725

ARMv4T can't switch instruction state when a saved return address is loaded directly into the pc. Several builtins did exactly that, so a call from Thumb could call into Arm state and return without switching. Or vice versa.

To mitigate that, this patch implements aditional epilogue forms, that expand to sensible code depending on what arch they are compiled for:

- POP_PC still pops only the saved return address.
- POP_PC_WITH_REGS restores saved registers and may clobber low registers.
- POP_PC_WITH_REGS_NO_CLOBBER restores saved registers without clobbering low register.

ARMv5 and later keep direct pop-to-pc forms. Pre-v5 ARM returns through ip and bx. Thumb-1 returns through r3; the no-clobber form does a register dance to save the value of a low register so it can pop the return address into it, move it into a high register and put back the original value of that low register (r3) before bx-ing from the high register (lr).

>From 1bf76a6e86a65893be1c538fe51e76dc2f74ad36 Mon Sep 17 00:00:00 2001
From: Ties Stuij <ties.stuij at arm.com>
Date: Mon, 3 Aug 2026 21:08:52 +0100
Subject: [PATCH] [compiler-rt][ARM] Make ARMv4T assembly builtins interwork

ARMv4T can't switch instruction state when a saved return address is loaded
directly into the pc. Several builtins did exactly that, so a call from Thumb
could call into Arm state and return without switching. Or vice versa.

To mitigate that, this patch implements aditional epilogue forms, that expand to
sensible code depending on what arch they are compiled for:

- POP_PC still pops only the saved return address.
- POP_PC_WITH_REGS restores saved registers and may clobber low registers.
- POP_PC_WITH_REGS_NO_CLOBBER restores saved registers without clobbering
  low register.

ARMv5 and later keep direct pop-to-pc forms. Pre-v5 ARM returns through ip and
bx. Thumb-1 returns through r3; the no-clobber form does a register dance to
save the value of a low register so it can pop the return address into it, move
it into a high register and put back the original value of that low
register (r3) before bx-ing from the high register (lr).
---
 compiler-rt/lib/builtins/arm/aeabi_cdcmp.S    |  12 +--
 compiler-rt/lib/builtins/arm/aeabi_cfcmp.S    |  12 +--
 compiler-rt/lib/builtins/arm/aeabi_dcmp.S     |   4 +-
 compiler-rt/lib/builtins/arm/aeabi_fcmp.S     |   4 +-
 compiler-rt/lib/builtins/arm/aeabi_idivmod.S  |   2 +-
 compiler-rt/lib/builtins/arm/aeabi_ldivmod.S  |   2 +-
 compiler-rt/lib/builtins/arm/aeabi_memcmp.S   |   2 +-
 compiler-rt/lib/builtins/arm/aeabi_memcpy.S   |   2 +-
 compiler-rt/lib/builtins/arm/aeabi_memmove.S  |   2 +-
 compiler-rt/lib/builtins/arm/aeabi_memset.S   |   4 +-
 compiler-rt/lib/builtins/arm/aeabi_uidivmod.S |   2 +-
 compiler-rt/lib/builtins/arm/aeabi_uldivmod.S |   2 +-
 compiler-rt/lib/builtins/arm/comparesf2.S     |   2 +-
 compiler-rt/lib/builtins/arm/divmodsi4.S      |   2 +-
 compiler-rt/lib/builtins/arm/divsi3.S         |   2 +-
 compiler-rt/lib/builtins/arm/modsi3.S         |   2 +-
 compiler-rt/lib/builtins/arm/udivsi3.S        |   2 +-
 compiler-rt/lib/builtins/assembly.h           |  24 +++++
 .../Unit/arm/aeabi_cmpflags_interwork_test.c  |  71 ++++++++++++
 .../test/builtins/Unit/arm/aeabi_fcmp_test.c  |  40 +++++++
 .../builtins/Unit/arm/aeabi_idivmod_test.c    |   5 +-
 .../builtins/Unit/arm/aeabi_ldivmod_test.c    |  56 ++++++++++
 .../test/builtins/Unit/arm/aeabi_mem_test.c   | 102 ++++++++++++++++++
 .../builtins/Unit/arm/aeabi_uidivmod_test.c   |   5 +-
 .../builtins/Unit/arm/aeabi_uldivmod_test.c   |   5 +-
 .../test/builtins/Unit/divmodsi4_test.c       |   3 +
 compiler-rt/test/builtins/Unit/divsi3_test.c  |   3 +
 compiler-rt/test/builtins/Unit/modsi3_test.c  |   3 +
 compiler-rt/test/builtins/Unit/udivsi3_test.c |   3 +
 29 files changed, 347 insertions(+), 33 deletions(-)
 create mode 100644 compiler-rt/test/builtins/Unit/arm/aeabi_cmpflags_interwork_test.c
 create mode 100644 compiler-rt/test/builtins/Unit/arm/aeabi_fcmp_test.c
 create mode 100644 compiler-rt/test/builtins/Unit/arm/aeabi_ldivmod_test.c
 create mode 100644 compiler-rt/test/builtins/Unit/arm/aeabi_mem_test.c

diff --git a/compiler-rt/lib/builtins/arm/aeabi_cdcmp.S b/compiler-rt/lib/builtins/arm/aeabi_cdcmp.S
index b50c49ee33d11..900e59e0e2ea8 100644
--- a/compiler-rt/lib/builtins/arm/aeabi_cdcmp.S
+++ b/compiler-rt/lib/builtins/arm/aeabi_cdcmp.S
@@ -36,12 +36,12 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cdcmpeq)
         mov r0, sp
         ldm r0, {r0-r3}
         bl __aeabi_cdcmple
-        pop {r0-r3, pc}
+        POP_PC_WITH_REGS_NO_CLOBBER(r0-r3)
 1:
         // Z = 0, C = 1
         movs r0, #0xF
         lsls r0, r0, #31
-        pop {r0-r3, pc}
+        POP_PC_WITH_REGS_NO_CLOBBER(r0-r3)
 #else
 #if defined(__ARM_FEATURE_PAC_DEFAULT)
         pop {r0-r3, r12, lr}
@@ -96,7 +96,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cdcmple)
         // Z = 0, C = 0
         movs r0, #1
         lsls r0, r0, #1
-        pop {r0-r3, pc}
+        POP_PC_WITH_REGS_NO_CLOBBER(r0-r3)
 1:
         mov r0, sp
         ldm r0, {r0-r3}
@@ -106,12 +106,12 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cdcmple)
         // Z = 1, C = 1
         movs r0, #2
         lsls r0, r0, #31
-        pop {r0-r3, pc}
+        POP_PC_WITH_REGS_NO_CLOBBER(r0-r3)
 2:
         // Z = 0, C = 1
         movs r0, #0xF
         lsls r0, r0, #31
-        pop {r0-r3, pc}
+        POP_PC_WITH_REGS_NO_CLOBBER(r0-r3)
 #else
         ITT(eq)
         moveq ip, #0
@@ -159,7 +159,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cdrcmple)
 #if defined(USE_THUMB_1)
         push {r0, lr}
         bl __aeabi_cdcmple
-        pop {r0, pc}
+        POP_PC_WITH_REGS_NO_CLOBBER(r0)
 #else
         b __aeabi_cdcmple
 #endif
diff --git a/compiler-rt/lib/builtins/arm/aeabi_cfcmp.S b/compiler-rt/lib/builtins/arm/aeabi_cfcmp.S
index 8e5f760aaf5a4..d10c54e97d48b 100644
--- a/compiler-rt/lib/builtins/arm/aeabi_cfcmp.S
+++ b/compiler-rt/lib/builtins/arm/aeabi_cfcmp.S
@@ -36,12 +36,12 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cfcmpeq)
         mov r0, sp
         ldm r0, {r0-r3}
         bl __aeabi_cfcmple
-        pop {r0-r3, pc}
+        POP_PC_WITH_REGS_NO_CLOBBER(r0-r3)
 1:
         // Z = 0, C = 1
         movs r0, #0xF
         lsls r0, r0, #31
-        pop {r0-r3, pc}
+        POP_PC_WITH_REGS_NO_CLOBBER(r0-r3)
 #else
 #if defined(__ARM_FEATURE_PAC_DEFAULT)
         pop {r0-r3, r12, lr}
@@ -96,7 +96,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cfcmple)
         // Z = 0, C = 0
         movs r0, #1
         lsls r0, r0, #1
-        pop {r0-r3, pc}
+        POP_PC_WITH_REGS_NO_CLOBBER(r0-r3)
 1:
         mov r0, sp
         ldm r0, {r0-r3}
@@ -106,12 +106,12 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cfcmple)
         // Z = 1, C = 1
         movs r0, #2
         lsls r0, r0, #31
-        pop {r0-r3, pc}
+        POP_PC_WITH_REGS_NO_CLOBBER(r0-r3)
 2:
         // Z = 0, C = 1
         movs r0, #0xF
         lsls r0, r0, #31
-        pop {r0-r3, pc}
+        POP_PC_WITH_REGS_NO_CLOBBER(r0-r3)
 #else
         ITT(eq)
         moveq ip, #0
@@ -154,7 +154,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cfrcmple)
 #if defined(USE_THUMB_1)
         push {r0, lr}
         bl __aeabi_cfcmple
-        pop {r0, pc}
+        POP_PC_WITH_REGS_NO_CLOBBER(r0)
 #else
         b __aeabi_cfcmple
 #endif
diff --git a/compiler-rt/lib/builtins/arm/aeabi_dcmp.S b/compiler-rt/lib/builtins/arm/aeabi_dcmp.S
index 2ed13789d2f38..c05489971c2ab 100644
--- a/compiler-rt/lib/builtins/arm/aeabi_dcmp.S
+++ b/compiler-rt/lib/builtins/arm/aeabi_dcmp.S
@@ -33,10 +33,10 @@
 #elif defined(__ARM_FEATURE_BTI_DEFAULT)
 #  define PROLOGUE PACBTI_LANDING        SEPARATOR \
                    push      { r4, lr }
-#  define EPILOGUE pop       { r4, pc }
+#  define EPILOGUE POP_PC_WITH_REGS(r4)
 #else
 #  define PROLOGUE push      { r4, lr }
-#  define EPILOGUE pop       { r4, pc }
+#  define EPILOGUE POP_PC_WITH_REGS(r4)
 #endif
 
 #define DEFINE_AEABI_DCMP(cond)                            \
diff --git a/compiler-rt/lib/builtins/arm/aeabi_fcmp.S b/compiler-rt/lib/builtins/arm/aeabi_fcmp.S
index 0e3d399a552b4..0ded220458e89 100644
--- a/compiler-rt/lib/builtins/arm/aeabi_fcmp.S
+++ b/compiler-rt/lib/builtins/arm/aeabi_fcmp.S
@@ -33,10 +33,10 @@
 #elif defined(__ARM_FEATURE_BTI_DEFAULT)
 #  define PROLOGUE PACBTI_LANDING        SEPARATOR \
                    push      { r4, lr }
-#  define EPILOGUE pop       { r4, pc }
+#  define EPILOGUE POP_PC_WITH_REGS(r4)
 #else
 #  define PROLOGUE push      { r4, lr }
-#  define EPILOGUE pop       { r4, pc }
+#  define EPILOGUE POP_PC_WITH_REGS(r4)
 #endif
 
 #define DEFINE_AEABI_FCMP(cond)                            \
diff --git a/compiler-rt/lib/builtins/arm/aeabi_idivmod.S b/compiler-rt/lib/builtins/arm/aeabi_idivmod.S
index d95b1db0b459a..d42a7029221bb 100644
--- a/compiler-rt/lib/builtins/arm/aeabi_idivmod.S
+++ b/compiler-rt/lib/builtins/arm/aeabi_idivmod.S
@@ -51,7 +51,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_idivmod)
         pop     { r12, lr }
         PAC_RETURN
 #else
-        pop     { pc }
+        POP_PC()
 #endif
 #endif //  defined(USE_THUMB_1)
 END_COMPILERRT_FUNCTION(__aeabi_idivmod)
diff --git a/compiler-rt/lib/builtins/arm/aeabi_ldivmod.S b/compiler-rt/lib/builtins/arm/aeabi_ldivmod.S
index 4fbfe3f1d2656..05720bcdd52e7 100644
--- a/compiler-rt/lib/builtins/arm/aeabi_ldivmod.S
+++ b/compiler-rt/lib/builtins/arm/aeabi_ldivmod.S
@@ -47,7 +47,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_ldivmod)
         pop     {r6, r12, lr}
         PAC_RETURN
 #else
-        pop     {r6, pc}
+        POP_PC_WITH_REGS_NO_CLOBBER(r6)
 #endif
 END_COMPILERRT_FUNCTION(__aeabi_ldivmod)
 
diff --git a/compiler-rt/lib/builtins/arm/aeabi_memcmp.S b/compiler-rt/lib/builtins/arm/aeabi_memcmp.S
index 41637289c1bcb..2a35aa94122a0 100644
--- a/compiler-rt/lib/builtins/arm/aeabi_memcmp.S
+++ b/compiler-rt/lib/builtins/arm/aeabi_memcmp.S
@@ -16,7 +16,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_memcmp)
 #ifdef USE_THUMB_1
         push    {r7, lr}
         bl      memcmp
-        pop     {r7, pc}
+        POP_PC_WITH_REGS(r7)
 #else
         b       memcmp
 #endif
diff --git a/compiler-rt/lib/builtins/arm/aeabi_memcpy.S b/compiler-rt/lib/builtins/arm/aeabi_memcpy.S
index fa6a58640179e..faed2528f5eaa 100644
--- a/compiler-rt/lib/builtins/arm/aeabi_memcpy.S
+++ b/compiler-rt/lib/builtins/arm/aeabi_memcpy.S
@@ -16,7 +16,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_memcpy)
 #ifdef USE_THUMB_1
         push    {r7, lr}
         bl      memcpy
-        pop     {r7, pc}
+        POP_PC_WITH_REGS(r7)
 #else
 #if defined(__ARM_FEATURE_BTI_DEFAULT)
         bti
diff --git a/compiler-rt/lib/builtins/arm/aeabi_memmove.S b/compiler-rt/lib/builtins/arm/aeabi_memmove.S
index a72d52a397b97..b33c31dc5b854 100644
--- a/compiler-rt/lib/builtins/arm/aeabi_memmove.S
+++ b/compiler-rt/lib/builtins/arm/aeabi_memmove.S
@@ -15,7 +15,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_memmove)
 #ifdef USE_THUMB_1
         push    {r7, lr}
         bl      memmove
-        pop     {r7, pc}
+        POP_PC_WITH_REGS(r7)
 #else
 #if defined(__ARM_FEATURE_BTI_DEFAULT)
         bti
diff --git a/compiler-rt/lib/builtins/arm/aeabi_memset.S b/compiler-rt/lib/builtins/arm/aeabi_memset.S
index 950be84c5307d..6d2175f9ad791 100644
--- a/compiler-rt/lib/builtins/arm/aeabi_memset.S
+++ b/compiler-rt/lib/builtins/arm/aeabi_memset.S
@@ -23,7 +23,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_memset)
 #ifdef USE_THUMB_1
         push    {r7, lr}
         bl      memset
-        pop     {r7, pc}
+        POP_PC_WITH_REGS(r7)
 #else
         b       memset
 #endif
@@ -42,7 +42,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_memclr)
 #ifdef USE_THUMB_1
         push    {r7, lr}
         bl      memset
-        pop     {r7, pc}
+        POP_PC_WITH_REGS(r7)
 #else
         b       memset
 #endif
diff --git a/compiler-rt/lib/builtins/arm/aeabi_uidivmod.S b/compiler-rt/lib/builtins/arm/aeabi_uidivmod.S
index f77beab774115..585c5db9e4c21 100644
--- a/compiler-rt/lib/builtins/arm/aeabi_uidivmod.S
+++ b/compiler-rt/lib/builtins/arm/aeabi_uidivmod.S
@@ -58,7 +58,7 @@ LOCAL_LABEL(case_denom_larger):
         pop     { r12, lr }
         PAC_RETURN
 #else
-        pop     { pc }
+        POP_PC()
 #endif
 #endif
 END_COMPILERRT_FUNCTION(__aeabi_uidivmod)
diff --git a/compiler-rt/lib/builtins/arm/aeabi_uldivmod.S b/compiler-rt/lib/builtins/arm/aeabi_uldivmod.S
index e019ba721b75d..4e2067fadd32d 100644
--- a/compiler-rt/lib/builtins/arm/aeabi_uldivmod.S
+++ b/compiler-rt/lib/builtins/arm/aeabi_uldivmod.S
@@ -47,7 +47,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_uldivmod)
         pop     {r6, r12, lr}
         PAC_RETURN
 #else
-        pop     {r6, pc}
+        POP_PC_WITH_REGS_NO_CLOBBER(r6)
 #endif
 END_COMPILERRT_FUNCTION(__aeabi_uldivmod)
 
diff --git a/compiler-rt/lib/builtins/arm/comparesf2.S b/compiler-rt/lib/builtins/arm/comparesf2.S
index 24b85d2fee150..e177effaa4d50 100644
--- a/compiler-rt/lib/builtins/arm/comparesf2.S
+++ b/compiler-rt/lib/builtins/arm/comparesf2.S
@@ -159,7 +159,7 @@ LOCAL_LABEL(CHECK_NAN\@):
     bls     2f
     \handle_nan
 2:
-    pop     {r6, pc}
+    POP_PC_WITH_REGS(r6)
 #else
     cmp     r2,         #0xff000000
     ite ls
diff --git a/compiler-rt/lib/builtins/arm/divmodsi4.S b/compiler-rt/lib/builtins/arm/divmodsi4.S
index f94438dfd593e..a142064f6e3f8 100644
--- a/compiler-rt/lib/builtins/arm/divmodsi4.S
+++ b/compiler-rt/lib/builtins/arm/divmodsi4.S
@@ -18,7 +18,7 @@
     push   {r4-r7, lr}   ;\
     add     r7,     sp, #12
 #define CLEAR_FRAME_AND_RETURN \
-    pop    {r4-r7, pc}
+    POP_PC_WITH_REGS(r4-r7)
 
 	.syntax unified
 	.text
diff --git a/compiler-rt/lib/builtins/arm/divsi3.S b/compiler-rt/lib/builtins/arm/divsi3.S
index faf9af917ab67..f003faaa9ca83 100644
--- a/compiler-rt/lib/builtins/arm/divsi3.S
+++ b/compiler-rt/lib/builtins/arm/divsi3.S
@@ -17,7 +17,7 @@
     push   {r4, r7, lr}    ;\
     add     r7,     sp, #4
 #define CLEAR_FRAME_AND_RETURN \
-    pop    {r4, r7, pc}
+    POP_PC_WITH_REGS(r4, r7)
 
    .syntax unified
    .text
diff --git a/compiler-rt/lib/builtins/arm/modsi3.S b/compiler-rt/lib/builtins/arm/modsi3.S
index 5312f5b41c4d2..729b94f60a6b3 100644
--- a/compiler-rt/lib/builtins/arm/modsi3.S
+++ b/compiler-rt/lib/builtins/arm/modsi3.S
@@ -17,7 +17,7 @@
     push   {r4, r7, lr}    ;\
     add     r7,     sp, #4
 #define CLEAR_FRAME_AND_RETURN \
-    pop    {r4, r7, pc}
+    POP_PC_WITH_REGS(r4, r7)
 
 	.syntax unified
 	.text
diff --git a/compiler-rt/lib/builtins/arm/udivsi3.S b/compiler-rt/lib/builtins/arm/udivsi3.S
index 16528e8bbd829..0c0114f7edf49 100644
--- a/compiler-rt/lib/builtins/arm/udivsi3.S
+++ b/compiler-rt/lib/builtins/arm/udivsi3.S
@@ -189,7 +189,7 @@ LOCAL_LABEL(divby0):
 #      if defined(__ARM_EABI__)
 	push {r7, lr}
 	bl	__aeabi_idiv0 // due to relocation limit, can't use b.
-	pop  {r7, pc}
+	POP_PC_WITH_REGS(r7)
 #      else
 	JMP(lr)
 #      endif
diff --git a/compiler-rt/lib/builtins/assembly.h b/compiler-rt/lib/builtins/assembly.h
index 2eddbf468c149..3fc79073f37ab 100644
--- a/compiler-rt/lib/builtins/assembly.h
+++ b/compiler-rt/lib/builtins/assembly.h
@@ -206,10 +206,34 @@
 // pop {pc} can't switch Thumb mode on ARMv4T
 #if __ARM_ARCH >= 5
 #define POP_PC() pop {pc}
+#define POP_PC_WITH_REGS(...) pop {__VA_ARGS__, pc}
+#define POP_PC_WITH_REGS_NO_CLOBBER(...) pop {__VA_ARGS__, pc}
+#elif defined(USE_THUMB_1)
+#define POP_PC()                                                               \
+  pop {r3};                                                                    \
+  JMP(r3)
+#define POP_PC_WITH_REGS(...)                                                  \
+  pop {__VA_ARGS__};                                                           \
+  pop {r3};                                                                    \
+  JMP(r3)
+// Some builtins have live values in every Thumb-1 low register. Use ip and lr
+// while transferring the stacked return address to a BX-capable high register.
+// NO_CLOBBER refers to low registers; ip and lr may be clobbered.
+#define POP_PC_WITH_REGS_NO_CLOBBER(...)                                       \
+  pop {__VA_ARGS__};                                                           \
+  mov ip, r3;                                                                  \
+  pop {r3};                                                                    \
+  mov lr, r3;                                                                  \
+  mov r3, ip;                                                                  \
+  JMP(lr)
 #else
 #define POP_PC()                                                               \
   pop {ip};                                                                    \
   JMP(ip)
+#define POP_PC_WITH_REGS(...)                                                  \
+  pop {__VA_ARGS__, ip};                                                       \
+  JMP(ip)
+#define POP_PC_WITH_REGS_NO_CLOBBER(...) POP_PC_WITH_REGS(__VA_ARGS__)
 #endif
 
 #if defined(USE_THUMB_2)
diff --git a/compiler-rt/test/builtins/Unit/arm/aeabi_cmpflags_interwork_test.c b/compiler-rt/test/builtins/Unit/arm/aeabi_cmpflags_interwork_test.c
new file mode 100644
index 0000000000000..50c2dafeae2a8
--- /dev/null
+++ b/compiler-rt/test/builtins/Unit/arm/aeabi_cmpflags_interwork_test.c
@@ -0,0 +1,71 @@
+// REQUIRES: arm-target-arch || armv4t-target-arch
+// RUN: %clang_builtins -mthumb %s %librt -o %t && %run %t
+
+#include <math.h>
+
+// ARMv4T Thumb code can not read status register. These helpers call
+// the EABI comparison functions and turn Z/C flags into numbers:
+// -1 for less, 0 for equal, and 1 for greater or unordered
+__asm__(".globl call_aeabi_cfcmple\n"
+        ".thumb_func\n"
+        "call_aeabi_cfcmple:\n"
+        "  push {r4, lr}\n"
+        "  bl __aeabi_cfcmple\n"
+        "  beq 1f\n"
+        "  bcs 2f\n"
+        "  movs r0, #1\n"
+        "  negs r0, r0\n"
+        "  b 3f\n"
+        "1:\n"
+        "  movs r0, #0\n"
+        "  b 3f\n"
+        "2:\n"
+        "  movs r0, #1\n"
+        "3:\n"
+        "  pop {r4}\n"
+        "  pop {r3}\n"
+        "  bx r3\n"
+
+        ".globl call_aeabi_cdcmple\n"
+        ".thumb_func\n"
+        "call_aeabi_cdcmple:\n"
+        "  push {r4, lr}\n"
+        "  bl __aeabi_cdcmple\n"
+        "  beq 1f\n"
+        "  bcs 2f\n"
+        "  movs r0, #1\n"
+        "  negs r0, r0\n"
+        "  b 3f\n"
+        "1:\n"
+        "  movs r0, #0\n"
+        "  b 3f\n"
+        "2:\n"
+        "  movs r0, #1\n"
+        "3:\n"
+        "  pop {r4}\n"
+        "  pop {r3}\n"
+        "  bx r3\n");
+
+extern int call_aeabi_cfcmple(float, float);
+extern int call_aeabi_cdcmple(double, double);
+
+int main(void) {
+  if (call_aeabi_cfcmple(1.0f, 2.0f) != -1)
+    return 1;
+  if (call_aeabi_cfcmple(1.0f, 1.0f) != 0)
+    return 1;
+  if (call_aeabi_cfcmple(2.0f, 1.0f) != 1)
+    return 1;
+  if (call_aeabi_cfcmple(NAN, 1.0f) != 1)
+    return 1;
+
+  if (call_aeabi_cdcmple(1.0, 2.0) != -1)
+    return 1;
+  if (call_aeabi_cdcmple(1.0, 1.0) != 0)
+    return 1;
+  if (call_aeabi_cdcmple(2.0, 1.0) != 1)
+    return 1;
+  if (call_aeabi_cdcmple(NAN, 1.0) != 1)
+    return 1;
+  return 0;
+}
diff --git a/compiler-rt/test/builtins/Unit/arm/aeabi_fcmp_test.c b/compiler-rt/test/builtins/Unit/arm/aeabi_fcmp_test.c
new file mode 100644
index 0000000000000..1a2d40c6613d0
--- /dev/null
+++ b/compiler-rt/test/builtins/Unit/arm/aeabi_fcmp_test.c
@@ -0,0 +1,40 @@
+// REQUIRES: arm-target-arch || armv4t-target-arch
+// RUN: %clang_builtins -mthumb %s %librt -o %t && %run %t
+
+#include <math.h>
+
+extern int __aeabi_fcmpeq(float, float);
+extern int __aeabi_fcmplt(float, float);
+extern int __aeabi_fcmple(float, float);
+extern int __aeabi_fcmpge(float, float);
+extern int __aeabi_fcmpgt(float, float);
+extern int __aeabi_dcmpeq(double, double);
+extern int __aeabi_dcmplt(double, double);
+extern int __aeabi_dcmple(double, double);
+extern int __aeabi_dcmpge(double, double);
+extern int __aeabi_dcmpgt(double, double);
+
+#define CHECK_COMPARE(name, true_a, true_b, false_a, false_b)                  \
+  do {                                                                         \
+    if (!name(true_a, true_b))                                                 \
+      return __LINE__;                                                         \
+    if (name(false_a, false_b))                                                \
+      return __LINE__;                                                         \
+    if (name(NAN, false_b))                                                    \
+      return __LINE__;                                                         \
+  } while (0)
+
+int main(void) {
+  CHECK_COMPARE(__aeabi_fcmpeq, 1.0f, 1.0f, 1.0f, 2.0f);
+  CHECK_COMPARE(__aeabi_fcmplt, 1.0f, 2.0f, 2.0f, 1.0f);
+  CHECK_COMPARE(__aeabi_fcmple, 1.0f, 1.0f, 2.0f, 1.0f);
+  CHECK_COMPARE(__aeabi_fcmpge, 2.0f, 1.0f, 1.0f, 2.0f);
+  CHECK_COMPARE(__aeabi_fcmpgt, 2.0f, 1.0f, 1.0f, 1.0f);
+
+  CHECK_COMPARE(__aeabi_dcmpeq, 1.0, 1.0, 1.0, 2.0);
+  CHECK_COMPARE(__aeabi_dcmplt, 1.0, 2.0, 2.0, 1.0);
+  CHECK_COMPARE(__aeabi_dcmple, 1.0, 1.0, 2.0, 1.0);
+  CHECK_COMPARE(__aeabi_dcmpge, 2.0, 1.0, 1.0, 2.0);
+  CHECK_COMPARE(__aeabi_dcmpgt, 2.0, 1.0, 1.0, 1.0);
+  return 0;
+}
diff --git a/compiler-rt/test/builtins/Unit/arm/aeabi_idivmod_test.c b/compiler-rt/test/builtins/Unit/arm/aeabi_idivmod_test.c
index e4953bf51b037..07d1a6ff55163 100644
--- a/compiler-rt/test/builtins/Unit/arm/aeabi_idivmod_test.c
+++ b/compiler-rt/test/builtins/Unit/arm/aeabi_idivmod_test.c
@@ -1,5 +1,8 @@
-// REQUIRES: arm-target-arch || armv6m-target-arch
+// REQUIRES: arm-target-arch || armv4t-target-arch || armv6m-target-arch
 // RUN: %clang_builtins %s %librt -o %t && %run %t
+// RUN: %if !armv6m-target-arch %{ \
+// RUN:   %clang_builtins -mthumb %s %librt -o %t.thumb && %run %t.thumb \
+// RUN: %}
 
 #include "int_lib.h"
 #include <stdio.h>
diff --git a/compiler-rt/test/builtins/Unit/arm/aeabi_ldivmod_test.c b/compiler-rt/test/builtins/Unit/arm/aeabi_ldivmod_test.c
new file mode 100644
index 0000000000000..20dc33aa71cb0
--- /dev/null
+++ b/compiler-rt/test/builtins/Unit/arm/aeabi_ldivmod_test.c
@@ -0,0 +1,56 @@
+// REQUIRES: arm-target-arch || armv4t-target-arch
+// RUN: %clang_builtins -mthumb %s %librt -o %t && %run %t
+
+#include "int_lib.h"
+
+COMPILER_RT_ABI void __aeabi_ldivmod(di_int a, di_int b);
+
+int test_aeabi_ldivmod(di_int a, di_int b, di_int expected_q,
+                       di_int expected_r) {
+  di_int q, r;
+  // __aeabi_ldivmod returns a struct { quotient; remainder; } using
+  // value_in_regs calling convention. Each field is a 64-bit integer, so the
+  // quotient resides in r0 and r1, while the remainder in r2 and r3. The
+  // byte order however depends on the endianness.
+  __asm__(
+#if _YUGA_BIG_ENDIAN
+      "movs r1, %Q[a] \n"
+      "movs r0, %R[a] \n"
+      "movs r3, %Q[b] \n"
+      "movs r2, %R[b] \n"
+#else
+      "movs r0, %Q[a] \n"
+      "movs r1, %R[a] \n"
+      "movs r2, %Q[b] \n"
+      "movs r3, %R[b] \n"
+#endif
+      "bl __aeabi_ldivmod \n"
+#if _YUGA_BIG_ENDIAN
+      "movs %Q[q], r1 \n"
+      "movs %R[q], r0 \n"
+      "movs %Q[r], r3 \n"
+      "movs %R[r], r2 \n"
+#else
+      "movs %Q[q], r0 \n"
+      "movs %R[q], r1 \n"
+      "movs %Q[r], r2 \n"
+      "movs %R[r], r3 \n"
+#endif
+      : [q] "=r"(q), [r] "=r"(r)
+      : [a] "r"(a), [b] "r"(b)
+      : "lr", "r0", "r1", "r2", "r3");
+  return q != expected_q || r != expected_r;
+}
+
+int main(void) {
+  if (test_aeabi_ldivmod(0, 1, 0, 0))
+    return 1;
+  if (test_aeabi_ldivmod(19, 5, 3, 4))
+    return 1;
+  if (test_aeabi_ldivmod(-19, 5, -3, -4))
+    return 1;
+  if (test_aeabi_ldivmod(0x123456789abcdefLL, -0x1234567LL, -0x100000079LL,
+                         0x40LL))
+    return 1;
+  return 0;
+}
diff --git a/compiler-rt/test/builtins/Unit/arm/aeabi_mem_test.c b/compiler-rt/test/builtins/Unit/arm/aeabi_mem_test.c
new file mode 100644
index 0000000000000..685580fe0ce9d
--- /dev/null
+++ b/compiler-rt/test/builtins/Unit/arm/aeabi_mem_test.c
@@ -0,0 +1,102 @@
+// REQUIRES: arm-target-arch || armv4t-target-arch || armv6m-target-arch
+// RUN: %clang_builtins %s %librt -o %t && %run %t
+// RUN: %if !armv6m-target-arch %{ \
+// RUN:   %clang_builtins -mthumb %s %librt -o %t.thumb && %run %t.thumb \
+// RUN: %}
+
+#include <stddef.h>
+
+extern int __aeabi_memcmp(const void *, const void *, size_t);
+extern int __aeabi_memcmp4(const void *, const void *, size_t);
+extern void __aeabi_memcpy(void *, const void *, size_t);
+extern void __aeabi_memcpy4(void *, const void *, size_t);
+extern void __aeabi_memmove(void *, const void *, size_t);
+extern void __aeabi_memmove4(void *, const void *, size_t);
+extern void __aeabi_memset(void *, size_t, int);
+extern void __aeabi_memset4(void *, size_t, int);
+extern void __aeabi_memclr(void *, size_t);
+extern void __aeabi_memclr4(void *, size_t);
+
+typedef unsigned char buffer[8] __attribute__((aligned(4)));
+
+int check(const unsigned char *p, const unsigned char *expected, size_t n) {
+  for (size_t i = 0; i != n; ++i)
+    if (p[i] != expected[i])
+      return 1;
+  return 0;
+}
+
+int main(void) {
+  unsigned char *source = (buffer){0, 1, 2, 3, 4, 5, 6, 7};
+  unsigned char *data =
+      (buffer){0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+  unsigned char *expected;
+
+  __aeabi_memcpy(data, source, 8);
+  if (check(data, source, 8))
+    return 1;
+
+  source = (buffer){0, 1, 2, 3, 4, 5, 6, 7};
+  data = (buffer){0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+  __aeabi_memcpy4(data, source, 8);
+  if (check(data, source, 8))
+    return 1;
+
+  data = (buffer){0, 1, 2, 3, 4, 5, 6, 7};
+  expected = (buffer){0, 0, 1, 2, 3, 4, 5, 6};
+  __aeabi_memmove(data + 1, data, 7);
+  if (check(data, expected, 8))
+    return 1;
+
+  data = (buffer){0, 1, 2, 3, 4, 5, 6, 7};
+  expected = (buffer){0, 1, 2, 3, 0, 1, 2, 3};
+  __aeabi_memmove4(data + 4, data, 4);
+  if (check(data, expected, 8))
+    return 1;
+
+  data = (buffer){0, 1, 2, 3, 4, 5, 6, 7};
+  expected = (buffer){0, 1, 0xff, 0xff, 0xff, 0xff, 6, 7};
+  __aeabi_memset(data + 2, 4, 0xff);
+  if (check(data, expected, 8))
+    return 1;
+
+  data = (buffer){0, 1, 2, 3, 4, 5, 6, 7};
+  expected = (buffer){0x5a, 0x5a, 0x5a, 0x5a, 4, 5, 6, 7};
+  __aeabi_memset4(data, 4, 0x5a);
+  if (check(data, expected, 8))
+    return 1;
+
+  data = (buffer){0, 1, 2, 3, 4, 5, 6, 7};
+  expected = (buffer){0, 1, 0, 0, 0, 0, 6, 7};
+  __aeabi_memclr(data + 2, 4);
+  if (check(data, expected, 8))
+    return 1;
+
+  data = (buffer){1, 2, 3, 4, 5, 6, 7, 8};
+  expected = (buffer){0};
+  __aeabi_memclr4(data, 8);
+  if (check(data, expected, 8))
+    return 1;
+
+  data = (buffer){0, 1, 2, 3, 4, 5, 6, 7};
+  expected = (buffer){0, 1, 2, 3, 4, 5, 6, 7};
+  if (__aeabi_memcmp(data, expected, 8) != 0)
+    return 1;
+  expected[7] = 8;
+  if (__aeabi_memcmp(data, expected, 8) >= 0)
+    return 1;
+  if (__aeabi_memcmp(expected, data, 8) <= 0)
+    return 1;
+
+  data = (buffer){0, 1, 2, 3, 4, 5, 6, 7};
+  expected = (buffer){0, 1, 2, 3, 4, 5, 6, 7};
+  if (__aeabi_memcmp4(data, expected, 8) != 0)
+    return 1;
+  expected[7] = 8;
+  if (__aeabi_memcmp4(data, expected, 8) >= 0)
+    return 1;
+  if (__aeabi_memcmp4(expected, data, 8) <= 0)
+    return 1;
+
+  return 0;
+}
diff --git a/compiler-rt/test/builtins/Unit/arm/aeabi_uidivmod_test.c b/compiler-rt/test/builtins/Unit/arm/aeabi_uidivmod_test.c
index a507267c44602..763257b228f59 100644
--- a/compiler-rt/test/builtins/Unit/arm/aeabi_uidivmod_test.c
+++ b/compiler-rt/test/builtins/Unit/arm/aeabi_uidivmod_test.c
@@ -1,5 +1,8 @@
-// REQUIRES: arm-target-arch || armv6m-target-arch
+// REQUIRES: arm-target-arch || armv4t-target-arch || armv6m-target-arch
 // RUN: %clang_builtins %s %librt -o %t && %run %t
+// RUN: %if !armv6m-target-arch %{ \
+// RUN:   %clang_builtins -mthumb %s %librt -o %t.thumb && %run %t.thumb \
+// RUN: %}
 
 #include "int_lib.h"
 #include <stdio.h>
diff --git a/compiler-rt/test/builtins/Unit/arm/aeabi_uldivmod_test.c b/compiler-rt/test/builtins/Unit/arm/aeabi_uldivmod_test.c
index 02cfa2f38713b..05618a3edf164 100644
--- a/compiler-rt/test/builtins/Unit/arm/aeabi_uldivmod_test.c
+++ b/compiler-rt/test/builtins/Unit/arm/aeabi_uldivmod_test.c
@@ -1,5 +1,8 @@
-// REQUIRES: arm-target-arch || armv6m-target-arch
+// REQUIRES: arm-target-arch || armv4t-target-arch || armv6m-target-arch
 // RUN: %clang_builtins %s %librt -o %t && %run %t
+// RUN: %if !armv6m-target-arch %{ \
+// RUN:   %clang_builtins -mthumb %s %librt -o %t.thumb && %run %t.thumb \
+// RUN: %}
 
 #include "int_lib.h"
 #include <stdio.h>
diff --git a/compiler-rt/test/builtins/Unit/divmodsi4_test.c b/compiler-rt/test/builtins/Unit/divmodsi4_test.c
index b6160d25391ca..6b4538b666409 100644
--- a/compiler-rt/test/builtins/Unit/divmodsi4_test.c
+++ b/compiler-rt/test/builtins/Unit/divmodsi4_test.c
@@ -1,4 +1,7 @@
 // RUN: %clang_builtins %s %librt -o %t && %run %t
+// RUN: %if arm-target-arch || armv4t-target-arch %{ \
+// RUN:   %clang_builtins -mthumb %s %librt -o %t.thumb && %run %t.thumb \
+// RUN: %}
 // REQUIRES: librt_has_divmodsi4
 
 #include "int_lib.h"
diff --git a/compiler-rt/test/builtins/Unit/divsi3_test.c b/compiler-rt/test/builtins/Unit/divsi3_test.c
index 5d97231363cda..e603f8150d123 100644
--- a/compiler-rt/test/builtins/Unit/divsi3_test.c
+++ b/compiler-rt/test/builtins/Unit/divsi3_test.c
@@ -1,4 +1,7 @@
 // RUN: %clang_builtins %s %librt -o %t && %run %t
+// RUN: %if arm-target-arch || armv4t-target-arch %{ \
+// RUN:   %clang_builtins -mthumb %s %librt -o %t.thumb && %run %t.thumb \
+// RUN: %}
 // REQUIRES: librt_has_divsi3
 
 #include "int_lib.h"
diff --git a/compiler-rt/test/builtins/Unit/modsi3_test.c b/compiler-rt/test/builtins/Unit/modsi3_test.c
index f0ecdd002c8f2..402b2d97777e7 100644
--- a/compiler-rt/test/builtins/Unit/modsi3_test.c
+++ b/compiler-rt/test/builtins/Unit/modsi3_test.c
@@ -1,4 +1,7 @@
 // RUN: %clang_builtins %s %librt -o %t && %run %t
+// RUN: %if arm-target-arch || armv4t-target-arch %{ \
+// RUN:   %clang_builtins -mthumb %s %librt -o %t.thumb && %run %t.thumb \
+// RUN: %}
 // REQUIRES: librt_has_modsi3
 
 #include "int_lib.h"
diff --git a/compiler-rt/test/builtins/Unit/udivsi3_test.c b/compiler-rt/test/builtins/Unit/udivsi3_test.c
index beb4c60b8ec1f..721004ca15269 100644
--- a/compiler-rt/test/builtins/Unit/udivsi3_test.c
+++ b/compiler-rt/test/builtins/Unit/udivsi3_test.c
@@ -1,4 +1,7 @@
 // RUN: %clang_builtins %s %librt -o %t && %run %t
+// RUN: %if arm-target-arch || armv4t-target-arch %{ \
+// RUN:   %clang_builtins -mthumb %s %librt -o %t.thumb && %run %t.thumb \
+// RUN: %}
 // REQUIRES: librt_has_udivsi3
 
 #include "int_lib.h"



More information about the llvm-commits mailing list