[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