Boxed types and constat propagation
Rémi Forax
forax at univ-mlv.fr
Mon Apr 23 11:18:48 PDT 2012
On 04/23/2012 06:37 PM, Kohsuke Kawaguchi wrote:
> 2012/4/22 Rémi Forax<forax at univ-mlv.fr>:
>>> So as you can see, 30 and 12 are not recognized as constants.
>> You're right, 30 and 12 are not recognized as int constant
>> but they are recognized as java/lang/Integer constant.
>>
>> 0x00007f785ca29706: mov $0x7d66619d8,%r10 ; {oop(a 'java/ang/nteger' = 30)}
>>
>> so escape analysis works but Hotspot doesn't trust
>> final field thus doesn't consider the value in the Integer
>> as a constant.
> I see why HotSpot can't in general trust the 'final' keyword, but it
> sure would be nice if it can at least for these boxed types. After
> all, if Integer.value is modified via reflection, all kinds of weird
> things start to happen...
I fully agree :)
Also it's good to know that Graal, the JIT written in Java
on top of Hotspot, trusts final by default and doesn't seem
to have real trouble to run usual benchmarks.
It's a prototype so I suppose that at some point they will have to
change that but anyway it is possible to consider some final fields
as constants without breaking all programs.
>
>>> After a few more experiments, I realize the root cause of this isn't
>>> so much as JSR-292 but more in HotSpot. For example, the following
>>> method produces the following assembly code, and as you can see it's
>>> failing to optimize body() into just "return false".
>> In fact, Hotspot doesn't fail.
>> But Hotspot also obfuscates the code :(
>>
>>> So my question is:
>>>
>>> - Am I missing something?
>> yes :)
>> in that case, the body of 'body' is
>> (there is a ret at 0x00007fadf244130c)
>>
>> 0x00007fadf24412e6: mov $0x7d6602fe0,%r10 ; {oop(a 'java/lang/Class' = 'java/lang/Boolean')}
>> 0x00007fadf24412f0: mov 0x74(%r10),%r8d ;*getstatic FALSE
> This is doing "Boolean %r8 = Boolean.FALSE"
>
>> java.lang.Boolean::valueOf at 10 (line 149)
>> ; -
>> BoxedBooleanInlineTest::bool1 at 1 (line 29)
>> ; -
>> BoxedBooleanInlineTest::body at 1 (line 21)
>> 0x00007fadf24412f4: movzbl 0xc(%r12,%r8,8),%r11d
> This is doing "%r11 = %r8.value" with proper sign bit expansion
>
>> 0x00007fadf24412fa: test %r11d,%r11d
> and this is testing if %r11 is 0.
I have forgotten to say that I barely able to read the assembler code.
So here is my mistake, I was thinking this code was useless.
test is always something mysterious to me.
>
>> 0x00007fadf24412fd: jne 0x00007fadf244130d ;*iconst_0
>> ; -
>> BoxedBooleanInlineTest::body at 24 (line 21)
>> 0x00007fadf24412ff: xor %eax,%eax ;*ireturn
> and yes, this is "return 0"
>
>> The code is fully optimized xor %eax, %eax put 0 in eax,
>> which is by convention the register containing the return value
>> and the rest of the code is not used.
>> The question here is why Hostspot generates the unnecessary codes
>> above the xor instruction. The code tests that zero is equals to zero
>> before returning zero :)
> I don't see that as a question. As I said and as you observed earlier,
> this code is *not* fully optimized, in that it doesn't treat
> "Boolean.FALSE.value" as a constant. And under that assumption, every
> instruction in the above code does make sense.
You're right.
>
>
>>> - How do other language implementers cope with this?
>> You are the first as far as I know to use only a tree of method handles
>> to implement expressions. The rest of us generates bytecodes
>> and have a compiler that does constant propagation.
> OK, that's good to know, but I'm not seeing how that changes the picture.
>
> The issue is that, as things stand now, the language runtime would
> have to do the type inference to use primitive types instead of
> Object, or else it won't benefit from constant propagation (which
> triggers other optimizations like dead code elimination and further
> escape analysis.)
>
> Say I compile "a.foo()&& a.bar()" in JEXL to a byte code (or "a.foo
> && a.bar" in JRuby if that helps people understand the example
> better.) Unless I can somehow prove that "a.foo()" returns a boolean,
> I'd have to treat it like they return Object. And so even if at the
> runtime HotSpot discovers that "a" is mostly bound to a specific type
> that implements foo() as "return true", it ends up in the assembly
> code like my example (with the guard if statement on top to check if
> the type of 'a' is of this common type), not the equivalent of "return
> false" --- suboptimal result.
>
> Yet as I see it, such type inference is hard/impossible for most
> dynamic languages. I certainly don't see how JRuby can do that. And I
> thought the whole point of this new addition in Java7 was to let
> HotSpot do these optimizations, as opposed to have each language
> runtime do it.
>
> So again, I don't see how shifting from method handle tree to byte
> code generation would somehow make this non-issue.
In fact, it's easy to know that a.foo() is a boolean because
&& can be only applied on booleans so the invokedynamic call
corresponding to a.foo() should return a boolean.
From the compiler perspective, you have to back-propagate
the excepted type from the root to the leaf.
>
>
> But I think I got the answer to half of my questions anyway --- that I
> wasn't failing to have HotSpot optimize to its fullest potential, and
> instead it's the design choice / current shortcoming in HotSpot that
> it doesn't consider "Boolean.FALSE.value" as "false".
>
> That leaves me just with the other question, that how other
> implementors are coping with this. The observation from Mark was
> interesting --- maybe it actually doesn't cost as much as it looks in
> the assembly, with branch prediction and Boolean.FALSE on cache, etc.
> OTOH, I still think the constant propagation can trigger more
> optimizations and those add up to something non-trivial...
>
and there is another problem which is that currently the escape analysis
doesn't propagate information through an invokedynamic
(it consider target.invokeExact as a native call).
and yes, the current state, final fieds are constants, getClass() == A.class
is not recognized as a kind of instanceof, invokedynamic doesn't play
well with
escape analysis should be fixed.
cheers,
Rémi
More information about the mlvm-dev
mailing list