[LLVMdev] optimizer clobber EFLAGS
Michael Hordijk
hoffbrinkle at hotmail.com
Wed Jul 29 15:11:28 PDT 2015
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 LLVM IR has the what appears to be an appropriate
inline ASM directive:
---
entry:
tail call void @foo() #2
%0 = load i32* @a, align 4, !tbaa !1
%sub = add nsw i32 %0, -1
store i32 %sub, i32* @a, align 4, !tbaa !1
%tobool = icmp eq i32 %sub, 0
tail call void asm sideeffect "",
"~{cc},~{dirflag},~{fpsr},~{flags}"() #2, !srcloc !5
tail call void @foo() #2
br i1 %tobool, label %if.end, label %return
if.end: ; preds = %entry
tail call void @foo() #2
br label %return
return: ; preds = %entry, %if.end
%retval.0 = phi i32 [ 0, %if.end ], [ 1, %entry ]
ret i32 %retval.0
---
The constraint doesn't appear to do anything 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 the new value in EFLAGS
needs to be preserved (so don't clobber it)."
- michael
More information about the llvm-dev
mailing list