JDK-8058610 : Pessimistic LMUL used where optimistic should be
  • Type: Bug
  • Component: core-libs
  • Sub-Component: jdk.nashorn
  • Affected Version: 8u40
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2014-09-17
  • Updated: 2015-06-04
  • Resolved: 2014-10-21
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8 JDK 9
8u40Fixed 9 b37Fixed
Description
function doubleFoo(x) {
    return x.foo * 2;
}
print(doubleFoo({foo: 1 << 31}));

This emits LMUL instead of invokedynamic lmul for the multiplication. That's wrong.
Comments
The problem actually isn't the pessimistic LMUL (or rather, it's not the only way to look at it). The code generator correctly infers that since its arguments are ints, the long multiplication can never overflow. The problem in this case is that we aggressively push conversions into dynamic getter signatures. Since LMUL must have long arguments, even though the getter for x.foo is typed as %INT, it'll be widened to %LONG when the expected operand type is pushed into its signature. This can then be used to sneak in a larger-than-int long argument -- exactly what our proof-of-concept demonstrates. So there are two solutions to the problem: a) keep pushing the conversion into the signature, but then we must emit an optimistic LMUL, or b) keep emitting a pessimistic LMUL, but then we must replace a {%L}x.foo with {%I}x.foo;I2L (separate optimistic getter type from operand conversion). Both solutions are equally valid, but right now it's easier to implement solution b) in the code generator, so I'll go with that.
21-10-2014

This test program: function mul(x) { return x.foo * x.bar; } print("=== mul ===") print(mul({foo: 2147483647, bar: 2147483647})); // 2^31 print(mul({foo: 17179869184, bar: 2147483647})); // 2^34 function self_mul(x) { return x.foo *= x.bar; } print("=== self_mul ===") print(self_mul({foo: 2147483647, bar: 2147483647})); // 2^31 print(self_mul({foo: 17179869184, bar: 2147483647})); // 2^34 var long = (function() { var Long = Java.type("java.lang.Long"); return function(x) { return new Long(x).longValue(); } })(); function sub(x) { return x.foo - x.bar; } print("=== sub ===") print(sub({foo: 2147483647, bar: -2147483647})); // 2^31 print(sub({foo: long("9223372036854775807"), bar: long("-9223372036854775807")})); // 2^63-1 function self_sub(x) { return x.foo -= x.bar; } print("=== self_sub ===") print(self_sub({foo: 2147483647, bar: -2147483647})); // 2^31 print(self_sub({foo: long("9223372036854775807"), bar: long("-9223372036854775807")})); // 2^63-1 prints: === mul === 4611686014132420600 -17179869184 === self_mul === 4611686014132420600 -17179869184 === sub === 4294967294 -2 === self_sub === 4294967294 -2 where the expected output would be: === mul === 4611686014132420600 36893488130239234000 === self_mul === 4611686014132420600 36893488130239234000 === sub === 4294967294 18446744073709552000 === self_sub === 4294967294 18446744073709552000
21-10-2014