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; }
On 20 September 2014 15:19, Mikulas Patocka <mikulas at artax.karlin.mff.cuni.cz> wrote:> 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.LLVM always validates inline assembly. This may be too restrictive but it has been for a while and there isn't yet a good incentive to turn that off, even with a flag. This may change in the future for some cases, but your case is not one of them. The problem is that there isn't currently a way to pass flags to the integrated assembler as of now (I'm working on it). When that starts to work, you will be able to just pass "-mcpu=cortex-a9 -Wa,-mcpu=cortex-a15" and it'll do what you want. That should work on GCC, too. Right now, -Wa only works with an external assembler.> 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.Have you tried passing -Wa,-mcpu=cortex-a15 together with -no-integrated-as? cheers, --renato
On Sun, 21 Sep 2014, Renato Golin wrote:> On 20 September 2014 15:19, Mikulas Patocka > <mikulas at artax.karlin.mff.cuni.cz> wrote: > > 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. > > LLVM always validates inline assembly. This may be too restrictive but > it has been for a while and there isn't yet a good incentive to turn > that off, even with a flag. This may change in the future for some > cases, but your case is not one of them.What's the purpose of rejecting "sdiv" and other instructions? I understand that some times ago ARM was used mainly for embedded systems and there was no need to write ARM applications portable to different cores - so rejecting instructions for different cores didn't do any harm. But today it definitelly makes sense to write portable ARM applications that run on different cores.> The problem is that there isn't currently a way to pass flags to the > integrated assembler as of now (I'm working on it).gas unlocks all instructions if these directives are used ".cpu cortex-a15\n .fpu neon-vfpv4". LLVM integrated assembler will choke on it and produce invalid object file. You can try: int main(void) { asm volatile (".cpu cortex-a15\n .fpu neon-vfpv4\n"); return 0; } /usr/bin/ld: Warning: /tmp/as-b084f5.o: Unknown EABI object attribute 488 /usr/bin/ld: /tmp/as-b084f5.o: Unknown mandatory EABI object attribute 8890 /usr/bin/ld: failed to merge target specific data of file /tmp/as-b084f5.o Maybe, all you need to do is fix handling of these directives in the integrated assembler.> When that starts > to work, you will be able to just pass "-mcpu=cortex-a9 > -Wa,-mcpu=cortex-a15" and it'll do what you want. That should work on > GCC, too. > > Right now, -Wa only works with an external assembler. > > > > 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. > > Have you tried passing -Wa,-mcpu=cortex-a15 together with -no-integrated-as?I have tried it and it doesn't work (clang 3.5 in Ubuntu Trusty on ARM). With -no-integrated-as, clang uses the system assembler, but it parses the asm string nonetheless and reports an error on sdiv instruction. Mikulas> cheers, > --renato >