ARMv8, GCC 4.9 and necessary code optimized away?
Kyrill Tkachov
kyrylo.tkachov@foss.arm.com
Tue May 31 12:56:00 GMT 2016
On 30/05/16 05:00, Jeffrey Walton wrote:
> 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?
>
> Jeff
>
> ********************
>
> $ 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;
> else
> {
> 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);
> }
Like Florian said, 'w' is not being used so the compiler may optimise the
whole sequence away.
On a side note, if you want to detect the availability of certain extensions
at runtime have you considered using the hwcaps mechanism?
https://community.arm.com/groups/android-community/blog/2014/10/10/runtime-detection-of-cpu-features-on-an-armv8-a-cpu
Kyrill
>
> sigprocmask(SIG_SETMASK, (sigset_t*)&oldMask, NULL);
> signal(SIGILL, oldHandler);
> return result;
> #else
> return false;
> #endif
> }
>
> ********************
>
> 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;
> (gdb)
> 425 {
> (gdb)
> 445 volatile SigHandler oldHandler = signal(SIGILL, SigIllHandlerCRC32);
> (gdb)
> 443 volatile bool result = true;
> (gdb)
> 445 volatile SigHandler oldHandler = signal(SIGILL, SigIllHandlerCRC32);
> (gdb)
> 446 if (oldHandler == SIG_ERR)
> (gdb)
> 450 if (sigprocmask(0, NULL, (sigset_t*)&oldMask))
> (gdb)
> 453 if (setjmp(s_jmpNoCRC32))
> (gdb)
> 463 sigprocmask(SIG_SETMASK, (sigset_t*)&oldMask, NULL);
> (gdb)
> 464 signal(SIGILL, oldHandler);
> (gdb)
> 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