optimizer clobbering EFLAGS

Using Clang/LLVM 3.6.0 we are observing a case where the optimizations are clobbering EFLAGS on x86_64. This is inconvenient when the status of bit 9 (IF), which controls interrupts, changes.

Here's a simple test program. Assume that the external function foo() modifies the IF bit in EFLAGS.

#include <stdlib.h>
#include <stdbool.h>

void foo(void);
int a;

int bar(void)
{
         foo();

         bool const zero = a -= 1;

         asm volatile ("" : : : "cc");
         foo();

         if (zero) {
                 return EXIT_FAILURE;
         }

         foo();

         return EXIT_SUCCESS;
}

And it's compiled using the following command line:

clang -O2 -c -o clang-eflag.o clang-eflag.c

Produces this output:

$ objdump -S clang-eflag.o

clang-eflag.o: file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <bar>:
    0: 53 push %rbx
    1: e8 00 00 00 00 callq 6 <bar+0x6>
    6: ff 0d 00 00 00 00 decl 0x0(%rip) # c <bar+0xc>
    c: 9c pushfq
    d: 5b pop %rbx
    e: e8 00 00 00 00 callq 13 <bar+0x13>
   13: b8 01 00 00 00 mov $0x1,%eax
   18: 53 push %rbx
   19: 9d popfq
   1a: 75 07 jne 23 <bar+0x23>
   1c: e8 00 00 00 00 callq 21 <bar+0x21>
   21: 31 c0 xor %eax,%eax
   23: 5b pop %rbx
   24: c3 retq

The critical bits here are that at 0xc/0xd, we save the value of EFLAGS into %rbx. We then call foo() (which changes bit 9 of EFLAGS). We then 0x18/0x19 reload EFLAGS from the stale value in %rbx thus clobbering the value of bit 9 leaving interrupts in an unexpected state.

You may notice that I've tried the asm volatile ("" : : : "cc") constraint. The generated output is identical with and without the constraint. Which is not totally surprising. I'm thinking that the "cc" constraint tells the optimizer that "EFLAGS has been clobbered". What we need is a way to tell the optimizer that "EFLAGS has been clobbered and needs to be preserved."

- michael