[concurrency-interest] a volatile bug?
Christian Thalinger
christian.thalinger at oracle.com
Wed May 16 16:42:22 PDT 2012
On May 16, 2012, at 2:22 PM, Aleksey Shipilev wrote:
> Well, I do not want to sound alarming, but... if I understand the C1
> code correctly, then C1 GVN does not account prior volatile reads at
> all. I can not find any code in C1 GVN code which actually prevents
> killing second non-volatile read after volatile one, which is required
> by JMM semantics.
Good investigation. That seems to be the case.
>
> I think I'll stop here. The impact of this issue is limited, given
> most of the guys run -server (even by default on most machines), so
> there is always the workaround for running with -server. Also, I would
> *speculate* turning off GVN with -XX:-UseGlobalValueNumbering when
> running with -client is still a workaround, but kind of insane one,
> since it can *severely* degrade performance.
>
> Words of wisdom: I'm using this command-line to print out GVN tracing:
> $ ~/Install/jdk7u4/fastdebug/bin/java -XX:+PrintCompilation
> -XX:+PrintDominators -XX:+PrintCompilation -XX:+PrintValueNumbering
> -Xbatch -XX:CompileOnly=Test.$1,Test -client Test 2>&1 | tee asm.log
>
> Can anyone more proficient in C1 code confirm this?
I can confirm the bug and I have a fix for it.
-- Chris
>
> -Aleksey.
>
> On Thu, May 17, 2012 at 12:41 AM, Aleksey Shipilev
> <aleksey.shipilev at gmail.com> wrote:
>> In my case, there are always two compiled versions for Test$1.run, one
>> with cached $b, second one is with correct read for $b. I'd guess
>> pastebin version had the second one.
>>
>> -Aleksey.
>>
>> On Thu, May 17, 2012 at 12:34 AM, Vitaly Davidovich <vitalyd at gmail.com> wrote:
>>> I looked at the assembly on SO again (the pastebin link) and it seems to be
>>> correct actually: after 'a' is cmp'ed against zero, 'b' is read from
>>> memory. But now someone is saying there that it sometimes generates the
>>> correct assembly and other times not - very strange.
>>> 0x025bd2b9: cmp $0x0,%edx
>>>
>>> 30. 0x025bd2bc: je 0x025bd2a8 ;
>>>
>>> 32. 0x025bd2be: mov $0x147062e8,%edx ; {oop('test/TestVolatile')}
>>>
>>> 33. 0x025bd2c3: mov 0x1c4(%edx),%edx ;*getstatic b
>>>
>>> 34. ; - test.TestVolatile::run at 10 (line 17)
>>>
>>> 35. 0x025bd2c9: cmp $0x0,%edx
>>>
>>> Sent from my phone
>>>
>>> On May 16, 2012 3:55 PM, "Aleksey Shipilev" <aleksey.shipilev at gmail.com>
>>> wrote:
>>>>
>>>> Update. GVN is clearly under suspicion -XX:-UseGlobalValueNumbering
>>>> mitigates the bug in my setup. Digging through C1 codebase to see
>>>> rules for volatiles.
>>>>
>>>> -Aleksey.
>>>>
>>>> On Wed, May 16, 2012 at 11:23 PM, Aleksey Shipilev
>>>> <aleksey.shipilev at gmail.com> wrote:
>>>>> All right, here's what is on the table.
>>>>>
>>>>> This bug is reproduced for me on Linux i686 with:
>>>>> java version "1.7.0_04"
>>>>> Java(TM) SE Runtime Environment (build 1.7.0_04-b20)
>>>>> Java HotSpot(TM) Client VM (build 23.0-b21, mixed mode)
>>>>>
>>>>> It reproduces immediately only with -client.
>>>>> Both -server and -Xint do NOT reproduce the bug.
>>>>> The code is there in original SO post
>>>>>
>>>>> http://stackoverflow.com/questions/10620680/why-volatile-in-java-5-doesnt-synchronize-cached-copies-of-variables-with-main
>>>>>
>>>>> C1 seems to miscompile run(), and indeed does CSE for local:
>>>>>
>>>>> # {method} 'run' '()V' in 'Test$1'
>>>>> [Verified Entry Point]
>>>>> 0xb4a91e80: mov %eax,-0x4000(%esp)
>>>>> 0xb4a91e87: push %ebp
>>>>> 0xb4a91e88: sub $0x18,%esp ;*invokestatic access$000
>>>>> ; - Test$1::run at 0 (line 11)
>>>>> 0xb4a91e8b: mov $0xa09c4270,%edx ; {oop(a 'java/lang/Class' =
>>>>> 'Test')}
>>>>>>>>>>> 0xb4a91e90: mov 0x74(%edx),%edx ;*getstatic b <<<<<----
>>>>>>>>>>> loads $b to %edx
>>>>> ; - Test::access$000 at 0 (line 1)
>>>>> ; - Test$1::run at 0 (line 11)
>>>>> 0xb4a91e93: jmp 0xb4a91e9e ; OopMap{off=40}
>>>>> ;*goto
>>>>> ; - Test$1::run at 10 (line 13)
>>>>> 0xb4a91e98: test %eax,0xb77a9100 ;*goto
>>>>> ; - Test$1::run at 10 (line 13)
>>>>> ; {poll}
>>>>> 0xb4a91e9e: mov $0xa09c4270,%ecx ; {oop(a 'java/lang/Class' =
>>>>> 'Test')}
>>>>>>>>>> 0xb4a91ea3: mov 0x70(%ecx),%ecx ;*getstatic a <<<<<
>>>>>>>>>> volatile read for $a
>>>>> ; - Test::access$100 at 0 (line 1)
>>>>> ; - Test$1::run at 4 (line 13)
>>>>> 0xb4a91ea6: cmp $0x0,%ecx // <---- $a is at %ecx
>>>>> 0xb4a91ea9: je 0xb4a91e98 ;*ifne
>>>>> ; - Test$1::run at 7 (line 13)
>>>>> >>>> 0xb4a91eab: cmp $0x0,%edx // <<<<<<---- $b is cached in
>>>>> %edx here
>>>>> 0xb4a91eae: jne 0xb4a91ed8 ;*ifne
>>>>> ; - Test$1::run at 16 (line 17)
>>>>> 0xb4a91eb4: nopl 0x0(%eax)
>>>>> 0xb4a91eb8: jmp 0xb4a91f0e ; {no_reloc}
>>>>> 0xb4a91ebd: xchg %ax,%ax
>>>>> 0xb4a91ec0: jmp 0xb4a91f28 ; implicit exception:
>>>>> dispatches to 0xb4a91f18
>>>>> 0xb4a91ec5: nop ;*getstatic out
>>>>> ; - Test$1::run at 19 (line 18)
>>>>> 0xb4a91ec6: cmp (%ecx),%eax ; implicit exception:
>>>>> dispatches to 0xb4a91f32
>>>>> 0xb4a91ec8: mov $0xa09c6488,%edx ;*invokevirtual println
>>>>> ; - Test$1::run at 24 (line 18)
>>>>> ; {oop("error")}
>>>>>
>>>>>
>>>>> Thanks,
>>>>> Aleksey.
>>>>>
>>>>> On Wed, May 16, 2012 at 11:12 PM, Vitaly Davidovich <vitalyd at gmail.com>
>>>>> wrote:
>>>>>> It can be a compiler (mis)optimization that causes this, and not x86
>>>>>> memory
>>>>>> ordering.
>>>>>>
>>>>>> Someone posted the assembly output in the comments on SO and it does
>>>>>> seem
>>>>>> like there's a place that loads 'b' from the stack rather than memory.
>>>>>> Hans' theory of CSE sounds plausible - can someone repro this without
>>>>>> that
>>>>>> "int tt = b;" line?
>>>>>>
>>>>>> Adding hotspot compiler guys in case they want to chime in.
>>>>>>
>>>>>> Sent from my phone
>>>>>>
>>>>>> On May 16, 2012 3:07 PM, "Aleksey Shipilev"
>>>>>> <aleksey.shipilev at gmail.com>
>>>>>> wrote:
>>>>>>>
>>>>>>> On Wed, May 16, 2012 at 10:40 PM, Boehm, Hans <hans.boehm at hp.com>
>>>>>>> wrote:
>>>>>>>> A JDK bug AND a serious test suite omission?
>>>>>>>
>>>>>>> Stress tests would probably JIT-compile the code in question. See
>>>>>>> below.
>>>>>>>
>>>>>>>> But is the problem real? Can it be reproduced on a mainstream JVM?
>>>>>>>
>>>>>>> Same question.
>>>>>>>
>>>>>>>> Note that the example in the original posting also read b before the
>>>>>>>> loop,
>>>>>>>> so naïve common subexpression elimination would cause the bug.
>>>>>>>> Hopefully
>>>>>>>> nobody does CSE in cases like this.
>>>>>>>
>>>>>>> FWIW, the test case in SO would probably not hit any compilation
>>>>>>> threshold in HotSpot, so it could be executed in interpreter. Then,
>>>>>>> assuming the interpreter does not reorder Java code, and assuming
>>>>>>> original SO poster runs Windows, and hence x86, and hence has TSO,
>>>>>>> this bug seems very unlikely. I would be surprised if it actually
>>>>>>> *can* be reproduced. That makes the whole story rather interesting.
>>>>>>>
>>>>>>> -Aleksey.
>>>>>>>
>>>>>>> _______________________________________________
>>>>>>> Concurrency-interest mailing list
>>>>>>> Concurrency-interest at cs.oswego.edu
>>>>>>> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
More information about the hotspot-compiler-dev
mailing list