[LLVMdev] ARM assembler bug on LLVM 3.5
Mikulas Patocka
mikulas at artax.karlin.mff.cuni.cz
Sat Sep 20 15:19:52 PDT 2014
Hi
I have the following ARM Linux program. The program detects if the
processor has division instruction, if it does, it uses it, otherwise it
uses slower library call.
The program works with gcc, but it doesn't work with clang. clang reports
error on the sdiv instruction in the assembler.
The problem is this - you either compile this program with
-mcpu=cortex-a9, then clang reports error on the sdiv instruction because
cortex a9 doesn't have sdiv. Or - you compile the program with
-mcpu=cortex-a15, then clang compiles it, but it uses full cortex-a15
instruction set and the program crashes on cortex a9 and earlier cores.
Even if I use -no-integrated-as (as suggested in bug 18864), clang still
examines the string in "asm" statement and reports an error. GCC doesn't
examine the string in "asm" and works.
I'd like to ask how to write this program correctly so that it works in
clang. Or - if it's not possible - I'd like to ask if you could drop that
pointless restriction on instruction set in the assembler and be able to
generate all ARM instructions regardless of the cpu switch. This
restriction doesn't exist on x86 - on x86, you can compile the program
with -march=pentium2 and still use SSE instructions in the assembler, no
matter that pentium2 doesn't have SSE. The ARM backend seems overly
protective and prevents such instructions.
Mikulas
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
int have_hardware_division = 0;
int divide(int a, int b)
{
int result;
if (have_hardware_division)
asm (".cpu cortex-a15 \n sdiv %0, %1, %2" : "=r"(result) : "r"(a), "r"(b));
else
result = a / b;
return result;
}
int main(void)
{
int h, i;
unsigned a;
h = open("/proc/self/auxv", O_RDONLY);
if (h != -1) {
uint32_t cap[2];
while (read(h, &cap, 8) == 8) {
if (cap[0] == 16) {
#if defined(__thumb2__)
if (cap[1] & (1 << 18))
have_hardware_division = 1;
#else
if (cap[1] & (1 << 17))
have_hardware_division = 1;
#endif
break;
}
}
close(h);
}
a = 0;
for (i = 1; i < 100000000; i++) {
a += divide(100000000, i);
}
printf("%u\n", a);
return 0;
}
More information about the llvm-dev
mailing list