Implement __aeabi_c{d,f}{cmpeq,cmple,rcmple}.

Summary: Implement more missing ARM EABI runtime functions.

Reviewers: rengolin, compnerd

Subscribers: pirama, srhines, danalbert, aemerson, llvm-commits

Differential Revision: http://reviews.llvm.org/D12089

llvm-svn: 245648
This commit is contained in:
Josh Gao 2015-08-21 00:25:37 +00:00
parent 25fd09a756
commit 1108ae03b1
12 changed files with 638 additions and 0 deletions

View File

@ -189,6 +189,10 @@ set(i686_SOURCES
set(arm_SOURCES
arm/adddf3vfp.S
arm/addsf3vfp.S
arm/aeabi_cdcmp.S
arm/aeabi_cdcmpeq_check_nan.c
arm/aeabi_cfcmp.S
arm/aeabi_cfcmpeq_check_nan.c
arm/aeabi_dcmp.S
arm/aeabi_div0.c
arm/aeabi_drsub.c

View File

@ -0,0 +1,96 @@
//===-- aeabi_cdcmp.S - EABI cdcmp* implementation ------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "../assembly.h"
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
#error big endian support not implemented
#endif
#define APSR_Z (1 << 30)
#define APSR_C (1 << 29)
// void __aeabi_cdcmpeq(double a, double b) {
// if (isnan(a) || isnan(b)) {
// Z = 0; C = 1;
// } else {
// __aeabi_cdcmple(a, b);
// }
// }
.syntax unified
.p2align 2
DEFINE_COMPILERRT_FUNCTION(__aeabi_cdcmpeq)
push {r0-r3, lr}
bl __aeabi_cdcmpeq_check_nan
cmp r0, #1
pop {r0-r3, lr}
// NaN has been ruled out, so __aeabi_cdcmple can't trap
bne __aeabi_cdcmple
msr CPSR_f, #APSR_C
JMP(lr)
END_COMPILERRT_FUNCTION(__aeabi_cdcmpeq)
// void __aeabi_cdcmple(double a, double b) {
// if (__aeabi_dcmplt(a, b)) {
// Z = 0; C = 0;
// } else if (__aeabi_dcmpeq(a, b)) {
// Z = 1; C = 1;
// } else {
// Z = 0; C = 1;
// }
// }
.syntax unified
.p2align 2
DEFINE_COMPILERRT_FUNCTION(__aeabi_cdcmple)
// Per the RTABI, this function must preserve r0-r11.
// Save lr in the same instruction for compactness
push {r0-r3, lr}
bl __aeabi_dcmplt
cmp r0, #1
moveq ip, #0
beq 1f
ldm sp, {r0-r3}
bl __aeabi_dcmpeq
cmp r0, #1
moveq ip, #(APSR_C | APSR_Z)
movne ip, #(APSR_C)
1:
msr CPSR_f, ip
pop {r0-r3}
POP_PC()
END_COMPILERRT_FUNCTION(__aeabi_cdcmple)
// int __aeabi_cdrcmple(double a, double b) {
// return __aeabi_cdcmple(b, a);
// }
.syntax unified
.p2align 2
DEFINE_COMPILERRT_FUNCTION(__aeabi_cdrcmple)
// Swap r0 and r2
mov ip, r0
mov r0, r2
mov r2, ip
// Swap r1 and r3
mov ip, r1
mov r1, r3
mov r3, ip
b __aeabi_cdcmple
END_COMPILERRT_FUNCTION(__aeabi_cdrcmple)

View File

@ -0,0 +1,16 @@
//===-- lib/arm/aeabi_cdcmpeq_helper.c - Helper for cdcmpeq ---------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <stdint.h>
__attribute__((pcs("aapcs")))
__attribute__((visibility("hidden")))
int __aeabi_cdcmpeq_check_nan(double a, double b) {
return __builtin_isnan(a) || __builtin_isnan(b);
}

View File

@ -0,0 +1,91 @@
//===-- aeabi_cfcmp.S - EABI cfcmp* implementation ------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "../assembly.h"
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
#error big endian support not implemented
#endif
#define APSR_Z (1 << 30)
#define APSR_C (1 << 29)
// void __aeabi_cfcmpeq(float a, float b) {
// if (isnan(a) || isnan(b)) {
// Z = 0; C = 1;
// } else {
// __aeabi_cfcmple(a, b);
// }
// }
.syntax unified
.p2align 2
DEFINE_COMPILERRT_FUNCTION(__aeabi_cfcmpeq)
push {r0-r3, lr}
bl __aeabi_cfcmpeq_check_nan
cmp r0, #1
pop {r0-r3, lr}
// NaN has been ruled out, so __aeabi_cfcmple can't trap
bne __aeabi_cfcmple
msr CPSR_f, #APSR_C
JMP(lr)
END_COMPILERRT_FUNCTION(__aeabi_cfcmpeq)
// void __aeabi_cfcmple(float a, float b) {
// if (__aeabi_fcmplt(a, b)) {
// Z = 0; C = 0;
// } else if (__aeabi_fcmpeq(a, b)) {
// Z = 1; C = 1;
// } else {
// Z = 0; C = 1;
// }
// }
.syntax unified
.p2align 2
DEFINE_COMPILERRT_FUNCTION(__aeabi_cfcmple)
// Per the RTABI, this function must preserve r0-r11.
// Save lr in the same instruction for compactness
push {r0-r3, lr}
bl __aeabi_fcmplt
cmp r0, #1
moveq ip, #0
beq 1f
ldm sp, {r0-r3}
bl __aeabi_fcmpeq
cmp r0, #1
moveq ip, #(APSR_C | APSR_Z)
movne ip, #(APSR_C)
1:
msr CPSR_f, ip
pop {r0-r3}
POP_PC()
END_COMPILERRT_FUNCTION(__aeabi_cfcmple)
// int __aeabi_cfrcmple(float a, float b) {
// return __aeabi_cfcmple(b, a);
// }
.syntax unified
.p2align 2
DEFINE_COMPILERRT_FUNCTION(__aeabi_cfrcmple)
// Swap r0 and r1
mov ip, r0
mov r0, r1
mov r1, ip
b __aeabi_cfcmple
END_COMPILERRT_FUNCTION(__aeabi_cfrcmple)

View File

@ -0,0 +1,16 @@
//===-- lib/arm/aeabi_cfcmpeq_helper.c - Helper for cdcmpeq ---------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <stdint.h>
__attribute__((pcs("aapcs")))
__attribute__((visibility("hidden")))
int __aeabi_cfcmpeq_check_nan(float a, float b) {
return __builtin_isnan(a) || __builtin_isnan(b);
}

View File

@ -73,6 +73,15 @@
#define JMPc(r, c) mov##c pc, r
#endif
// pop {pc} can't switch Thumb mode on ARMv4T
#if __ARM_ARCH >= 5
#define POP_PC() pop {pc}
#else
#define POP_PC() \
pop {ip}; \
JMP(ip)
#endif
#if __ARM_ARCH_ISA_THUMB == 2
#define IT(cond) it cond
#define ITT(cond) itt cond

View File

@ -0,0 +1,70 @@
//===-- aeabi_cdcmpeq.c - Test __aeabi_cdcmpeq ----------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file tests __aeabi_cdcmpeq for the compiler_rt library.
//
//===----------------------------------------------------------------------===//
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#if __arm__
#include "call_apsr.h"
extern __attribute__((pcs("aapcs"))) void __aeabi_cdcmpeq(double a, double b);
int test__aeabi_cdcmpeq(double a, double b, int expected)
{
uint32_t cpsr_value = call_apsr_d(a, b, __aeabi_cdcmpeq);
union cpsr cpsr = { .value = cpsr_value };
if (expected != cpsr.flags.z) {
printf("error in __aeabi_cdcmpeq(%f, %f) => Z = %d, expected %d\n",
a, b, cpsr.flags.z, expected);
return 1;
}
return 0;
}
#endif
int main()
{
#if __arm__
if (test__aeabi_cdcmpeq(1.0, 1.0, 1))
return 1;
if (test__aeabi_cdcmpeq(1234.567, 765.4321, 0))
return 1;
if (test__aeabi_cdcmpeq(-123.0, -678.0, 0))
return 1;
if (test__aeabi_cdcmpeq(0.0, -0.0, 1))
return 1;
if (test__aeabi_cdcmpeq(1.0, NAN, 0))
return 1;
if (test__aeabi_cdcmpeq(NAN, 1.0, 0))
return 1;
if (test__aeabi_cdcmpeq(NAN, NAN, 0))
return 1;
if (test__aeabi_cdcmpeq(INFINITY, 1.0, 0))
return 1;
if (test__aeabi_cdcmpeq(0.0, INFINITY, 0))
return 1;
if (test__aeabi_cdcmpeq(-INFINITY, 0.0, 0))
return 1;
if (test__aeabi_cdcmpeq(0.0, -INFINITY, 0))
return 1;
if (test__aeabi_cdcmpeq(INFINITY, INFINITY, 1))
return 1;
if (test__aeabi_cdcmpeq(-INFINITY, -INFINITY, 1))
return 1;
#else
printf("skipped\n");
#endif
return 0;
}

View File

@ -0,0 +1,92 @@
//===-- aeabi_cdcmple.c - Test __aeabi_cdcmple and __aeabi_cdrcmple -------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file tests __aeabi_cdcmple and __aeabi_cdrcmple for the compiler_rt
// library.
//
//===----------------------------------------------------------------------===//
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "call_apsr.h"
#if __arm__
extern __attribute__((pcs("aapcs"))) void __aeabi_cdcmple(double a, double b);
extern __attribute__((pcs("aapcs"))) void __aeabi_cdrcmple(double a, double b);
int test__aeabi_cdcmple(double a, double b, int expected)
{
int32_t cpsr_value = call_apsr_d(a, b, __aeabi_cdcmple);
int32_t r_cpsr_value = call_apsr_d(b, a, __aeabi_cdrcmple);
if (cpsr_value != r_cpsr_value) {
printf("error: __aeabi_cdcmple(%f, %f) != __aeabi_cdrcmple(%f, %f)\n", a, b, b, a);
return 1;
}
int expected_z, expected_c;
if (expected == -1) {
expected_z = 0;
expected_c = 0;
} else if (expected == 0) {
expected_z = 1;
expected_c = 1;
} else {
// a or b is NaN, or a > b
expected_z = 0;
expected_c = 1;
}
union cpsr cpsr = { .value = cpsr_value };
if (expected_z != cpsr.flags.z || expected_c != cpsr.flags.c) {
printf("error in __aeabi_cdcmple(%f, %f) => (Z = %d, C = %d), expected (Z = %d, C = %d)\n",
a, b, cpsr.flags.z, cpsr.flags.c, expected_z, expected_c);
return 1;
}
cpsr.value = r_cpsr_value;
if (expected_z != cpsr.flags.z || expected_c != cpsr.flags.c) {
printf("error in __aeabi_cdrcmple(%f, %f) => (Z = %d, C = %d), expected (Z = %d, C = %d)\n",
a, b, cpsr.flags.z, cpsr.flags.c, expected_z, expected_c);
return 1;
}
return 0;
}
#endif
int main()
{
#if __arm__
if (test__aeabi_cdcmple(1.0, 1.0, 0))
return 1;
if (test__aeabi_cdcmple(1234.567, 765.4321, 1))
return 1;
if (test__aeabi_cdcmple(765.4321, 1234.567, -1))
return 1;
if (test__aeabi_cdcmple(-123.0, -678.0, 1))
return 1;
if (test__aeabi_cdcmple(-678.0, -123.0, -1))
return 1;
if (test__aeabi_cdcmple(0.0, -0.0, 0))
return 1;
if (test__aeabi_cdcmple(1.0, NAN, 1))
return 1;
if (test__aeabi_cdcmple(NAN, 1.0, 1))
return 1;
if (test__aeabi_cdcmple(NAN, NAN, 1))
return 1;
#else
printf("skipped\n");
#endif
return 0;
}

View File

@ -0,0 +1,70 @@
//===-- aeabi_cfcmpeq.c - Test __aeabi_cfcmpeq ----------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file tests __aeabi_cfcmpeq for the compiler_rt library.
//
//===----------------------------------------------------------------------===//
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#if __arm__
#include "call_apsr.h"
extern __attribute__((pcs("aapcs"))) void __aeabi_cfcmpeq(float a, float b);
int test__aeabi_cfcmpeq(float a, float b, int expected)
{
uint32_t cpsr_value = call_apsr_f(a, b, __aeabi_cfcmpeq);
union cpsr cpsr = { .value = cpsr_value };
if (expected != cpsr.flags.z) {
printf("error in __aeabi_cfcmpeq(%f, %f) => Z = %d, expected %d\n",
a, b, cpsr.flags.z, expected);
return 1;
}
return 0;
}
#endif
int main()
{
#if __arm__
if (test__aeabi_cfcmpeq(1.0, 1.0, 1))
return 1;
if (test__aeabi_cfcmpeq(1234.567, 765.4321, 0))
return 1;
if (test__aeabi_cfcmpeq(-123.0, -678.0, 0))
return 1;
if (test__aeabi_cfcmpeq(0.0, -0.0, 1))
return 1;
if (test__aeabi_cfcmpeq(1.0, NAN, 0))
return 1;
if (test__aeabi_cfcmpeq(NAN, 1.0, 0))
return 1;
if (test__aeabi_cfcmpeq(NAN, NAN, 0))
return 1;
if (test__aeabi_cfcmpeq(INFINITY, 1.0, 0))
return 1;
if (test__aeabi_cfcmpeq(0.0, INFINITY, 0))
return 1;
if (test__aeabi_cfcmpeq(-INFINITY, 0.0, 0))
return 1;
if (test__aeabi_cfcmpeq(0.0, -INFINITY, 0))
return 1;
if (test__aeabi_cfcmpeq(INFINITY, INFINITY, 1))
return 1;
if (test__aeabi_cfcmpeq(-INFINITY, -INFINITY, 1))
return 1;
#else
printf("skipped\n");
#endif
return 0;
}

View File

@ -0,0 +1,92 @@
//===-- aeabi_cfcmple.c - Test __aeabi_cfcmple and __aeabi_cfrcmple -------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file tests __aeabi_cfcmple and __aeabi_cfrcmple for the compiler_rt
// library.
//
//===----------------------------------------------------------------------===//
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "call_apsr.h"
#if __arm__
extern __attribute__((pcs("aapcs"))) void __aeabi_cfcmple(float a, float b);
extern __attribute__((pcs("aapcs"))) void __aeabi_cfrcmple(float a, float b);
int test__aeabi_cfcmple(float a, float b, int expected)
{
int32_t cpsr_value = call_apsr_f(a, b, __aeabi_cfcmple);
int32_t r_cpsr_value = call_apsr_f(b, a, __aeabi_cfrcmple);
if (cpsr_value != r_cpsr_value) {
printf("error: __aeabi_cfcmple(%f, %f) != __aeabi_cfrcmple(%f, %f)\n", a, b, b, a);
return 1;
}
int expected_z, expected_c;
if (expected == -1) {
expected_z = 0;
expected_c = 0;
} else if (expected == 0) {
expected_z = 1;
expected_c = 1;
} else {
// a or b is NaN, or a > b
expected_z = 0;
expected_c = 1;
}
union cpsr cpsr = { .value = cpsr_value };
if (expected_z != cpsr.flags.z || expected_c != cpsr.flags.c) {
printf("error in __aeabi_cfcmple(%f, %f) => (Z = %d, C = %d), expected (Z = %d, C = %d)\n",
a, b, cpsr.flags.z, cpsr.flags.c, expected_z, expected_c);
return 1;
}
cpsr.value = r_cpsr_value;
if (expected_z != cpsr.flags.z || expected_c != cpsr.flags.c) {
printf("error in __aeabi_cfrcmple(%f, %f) => (Z = %d, C = %d), expected (Z = %d, C = %d)\n",
a, b, cpsr.flags.z, cpsr.flags.c, expected_z, expected_c);
return 1;
}
return 0;
}
#endif
int main()
{
#if __arm__
if (test__aeabi_cfcmple(1.0, 1.0, 0))
return 1;
if (test__aeabi_cfcmple(1234.567, 765.4321, 1))
return 1;
if (test__aeabi_cfcmple(765.4321, 1234.567, -1))
return 1;
if (test__aeabi_cfcmple(-123.0, -678.0, 1))
return 1;
if (test__aeabi_cfcmple(-678.0, -123.0, -1))
return 1;
if (test__aeabi_cfcmple(0.0, -0.0, 0))
return 1;
if (test__aeabi_cfcmple(1.0, NAN, 1))
return 1;
if (test__aeabi_cfcmple(NAN, 1.0, 1))
return 1;
if (test__aeabi_cfcmple(NAN, NAN, 1))
return 1;
#else
printf("skipped\n");
#endif
return 0;
}

View File

@ -0,0 +1,43 @@
//===-- call_apsr.S - Helpers for ARM EABI floating point tests -----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements helpers for ARM EABI floating point tests for the
// compiler_rt library.
//
//===----------------------------------------------------------------------===//
#include "../../../../lib/builtins/assembly.h"
.syntax unified
// __attribute__((pcs("aapcs")))
// int32_t call_apsr_d(double a, double b, void(*fn)(double, double)) {
// fn(a, b);
// return apsr;
// }
DEFINE_COMPILERRT_PRIVATE_FUNCTION(call_apsr_d)
push {lr}
ldr ip, [sp, #4]
blx ip
mrs r0, apsr
pop {pc}
END_COMPILERRT_FUNCTION(call_apsr_d)
// __attribute__((pcs("aapcs")))
// int32_t call_apsr_f(float a, float b, void(*fn)(float, float)) {
// fn(a, b);
// return apsr;
// }
DEFINE_COMPILERRT_PRIVATE_FUNCTION(call_apsr_f)
push {lr}
blx r2
mrs r0, apsr
pop {pc}
END_COMPILERRT_FUNCTION(call_apsr_f)

View File

@ -0,0 +1,39 @@
//===-- call_apsr.h - Helpers for ARM EABI floating point tests -----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file declares helpers for ARM EABI floating point tests for the
// compiler_rt library.
//
//===----------------------------------------------------------------------===//
#ifndef CALL_APSR_H
#define CALL_APSR_H
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
#error big endian support not implemented
#endif
union cpsr {
struct {
uint32_t filler: 28;
uint32_t v: 1;
uint32_t c: 1;
uint32_t z: 1;
uint32_t n: 1;
} flags;
uint32_t value;
};
extern __attribute__((pcs("aapcs")))
uint32_t call_apsr_f(float a, float b, __attribute__((pcs("aapcs"))) void (*fn)(float, float));
extern __attribute__((pcs("aapcs")))
uint32_t call_apsr_d(double a, double b, __attribute__((pcs("aapcs"))) void (*fn)(double, double));
#endif // CALL_APSR_H