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
I remember this bug. :) IMO, LLVM should never emit pushf / popf. I'm not sure this patch to fix it ever got committed: http://reviews.llvm.org/D6629 On Wed, Jul 29, 2015 at 3:11 PM, Michael Hordijk <hoffbrinkle at hotmail.com> wrote:> > 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 > > _______________________________________________ > LLVM Developers mailing list > LLVMdev at cs.uiuc.edu http://llvm.cs.uiuc.edu > http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20150729/9c60bdb1/attachment.html>
Agreed, never emit pushf/popf. Sorry I never committed the patch, the cmov issue got hairy and I never got to debugging it :-) I can get back to it if there's interest! On Wed, Jul 29, 2015 at 4:12 PM, Reid Kleckner <rnk at google.com> wrote:> I remember this bug. :) IMO, LLVM should never emit pushf / popf. I'm not > sure this patch to fix it ever got committed: > http://reviews.llvm.org/D6629 > > > On Wed, Jul 29, 2015 at 3:11 PM, Michael Hordijk <hoffbrinkle at hotmail.com> > wrote: > >> >> 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 >> >> _______________________________________________ >> LLVM Developers mailing list >> LLVMdev at cs.uiuc.edu http://llvm.cs.uiuc.edu >> http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev >> > >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20150729/6d9ef706/attachment.html>