ARMv8, GCC 4.9 and necessary code optimized away?

Jeffrey Walton
Mon May 30 04:01:00 GMT 2016

Hi Everyone,

I'm working on an ARMv8 Mustang server board. Its an early ARMv8 board
(I believe its the first ARM-64 board), and its missing CRC32 and
Crypto extensions.

We have runtime feature tests that attempts to execute an instruction,
like CRC32 or AES, and catches the SIGILL if the instruction is
missing. Its kind of necessary to do it this way since reading a MSR
(ARM's equivalent of CPUID probing) results in a SIGILL for userland
programs (it requires Exception Level 1 or above).

It appears GCC is optimizing away the intrinsics we placed that test
for the features. Later, because of the missing test, HasFeatureX()
returns TRUE and the program dies with a SIGILL.

The code is below. Its not clear to me if GCC is optimizing away the
code because it determines the call to setjmp() never fails, or if it
determines the ARM intrinsics are dead code.

How can we get the expected/desired behavior?



$ gcc --version
gcc (Debian/Linaro 4.9.2-10) 4.9.2

$ lsb_release -a
No LSB modules are available.
Distributor ID:    Debian
Description:    Debian GNU/Linux 8.4 (jessie)
Release:    8.4
Codename:    jessie


Here is the CRC32 runtime feature test. The problem does not happen at
-O0. Tricks like making TryCRC32 volatile did not work.

static jmp_buf s_jmpNoCRC32;
static void SigIllHandlerCRC32(int)
    longjmp(s_jmpNoCRC32, 1);

static bool TryCRC32()
#if defined(__ARM_FEATURE_CRC32)
    // longjmp and clobber warnings. Volatile is required.
    volatile bool result = true;

    volatile SigHandler oldHandler = signal(SIGILL, SigIllHandlerCRC32);
    if (oldHandler == SIG_ERR)
        result = false;

    volatile sigset_t oldMask;
    if (sigprocmask(0, NULL, (sigset_t*)&oldMask))
        result = false;

    if (setjmp(s_jmpNoCRC32))
        result = false;
        uint32_t w=0, x=0; uint16_t y=0; uint8_t z=0;
        w = __crc32cw(w,x);
        w = __crc32ch(w,y);
        w = __crc32cb(w,z);

    sigprocmask(SIG_SETMASK, (sigset_t*)&oldMask, NULL);
    signal(SIGILL, oldHandler);
    return result;
    return false;


Here's what it looks like under the debugger.

Breakpoint 1, TryCRC32 () at cpu.cpp:425
425    {
(gdb) s
445        volatile SigHandler oldHandler = signal(SIGILL, SigIllHandlerCRC32);
(gdb) n
443        volatile bool result = true;
425    {
445        volatile SigHandler oldHandler = signal(SIGILL, SigIllHandlerCRC32);
443        volatile bool result = true;
445        volatile SigHandler oldHandler = signal(SIGILL, SigIllHandlerCRC32);
446        if (oldHandler == SIG_ERR)
450        if (sigprocmask(0, NULL, (sigset_t*)&oldMask))
453        if (setjmp(s_jmpNoCRC32))
463        sigprocmask(SIG_SETMASK, (sigset_t*)&oldMask, NULL);
464        signal(SIGILL, oldHandler);
465        return result;
(gdb) p result
$1 = true


$ gdb -batch -ex 'disassemble TryCRC32' cpu.o
Dump of assembler code for function TryCRC32():
   0x0000000000000148 <+0>:    stp    x29, x30, [sp,#-160]!
   0x000000000000014c <+4>:    adrp    x1, 0x0 <SigIllHandlerNEON(int)>
   0x0000000000000150 <+8>:    mov    w2, #0x1                       // #1
   0x0000000000000154 <+12>:    mov    x29, sp
   0x0000000000000158 <+16>:    mov    w0, #0x4                       // #4
   0x000000000000015c <+20>:    add    x1, x1, #0x0
   0x0000000000000160 <+24>:    strb    w2, [x29,#23]
   0x0000000000000164 <+28>:    bl    0x164 <TryCRC32()+28>
   0x0000000000000168 <+32>:    str    x0, [x29,#24]
   0x000000000000016c <+36>:    ldr    x0, [x29,#24]
   0x0000000000000170 <+40>:    cmn    x0, #0x1
   0x0000000000000174 <+44>:    b.eq    0x1d4 <TryCRC32()+140>
   0x0000000000000178 <+48>:    add    x2, x29, #0x20
   0x000000000000017c <+52>:    mov    x1, #0x0                       // #0
   0x0000000000000180 <+56>:    mov    w0, #0x0                       // #0
   0x0000000000000184 <+60>:    bl    0x184 <TryCRC32()+60>
   0x0000000000000188 <+64>:    cbnz    w0, 0x1cc <TryCRC32()+132>
   0x000000000000018c <+68>:    adrp    x0, 0x0 <SigIllHandlerNEON(int)>
   0x0000000000000190 <+72>:    add    x0, x0, #0x0
   0x0000000000000194 <+76>:    add    x0, x0, #0x138
   0x0000000000000198 <+80>:    bl    0x198 <TryCRC32()+80>
   0x000000000000019c <+84>:    cbz    w0, 0x1a4 <TryCRC32()+92>
   0x00000000000001a0 <+88>:    strb    wzr, [x29,#23]
   0x00000000000001a4 <+92>:    mov    x2, #0x0                       // #0
   0x00000000000001a8 <+96>:    add    x1, x29, #0x20
   0x00000000000001ac <+100>:    mov    w0, #0x2                       // #2
   0x00000000000001b0 <+104>:    bl    0x1b0 <TryCRC32()+104>
   0x00000000000001b4 <+108>:    ldr    x1, [x29,#24]
   0x00000000000001b8 <+112>:    mov    w0, #0x4                       // #4
   0x00000000000001bc <+116>:    bl    0x1bc <TryCRC32()+116>
   0x00000000000001c0 <+120>:    ldrb    w0, [x29,#23]
   0x00000000000001c4 <+124>:    ldp    x29, x30, [sp],#160
   0x00000000000001c8 <+128>:    ret
   0x00000000000001cc <+132>:    strb    wzr, [x29,#23]
   0x00000000000001d0 <+136>:    b    0x18c <TryCRC32()+68>
   0x00000000000001d4 <+140>:    strb    wzr, [x29,#23]
   0x00000000000001d8 <+144>:    b    0x178 <TryCRC32()+48>
End of assembler dump.

More information about the Gcc-help mailing list