(Condy) Folded of local variables are still considered when calculating the max number of local vars of a method
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Mon Sep 3 10:40:08 UTC 2018
On 02/09/18 19:02, Brian Goetz wrote:
> It sounds like the dead-code elimination is done after we construct
> the LV tables?
I think the issue is that, for javac, the local variable still exists
(e.g. there's a LocalItem for it), it is just never loaded, so you never
see it in the resulting code. But that means that it will be taken into
account when generating local var slots number (**).
In a way, javac doesn't actively perform any dead code elimination -
it's the turning of all variable initializer into constants (which are
dealt with by ldc) which turns code generation lazy, meaning that bits
will only be generated when needed. So, the resulting code gives you the
feeling of dead code elimination, but in reality there is no piece of
code where javac says: it's ok to ditch this local variable. In other
word, DCE is an emergent property of the way in which we lazily generate
code.
You can reproduce a similar behavior with this Java program which only
involves string constants:
class Test {
static foo() {
String hello = "Hello!";
}
}
This gives:
static void foo();
descriptor: ()V
flags: ACC_STATIC
Code:
stack=0, *locals=1,* args_size=0
0: return
LineNumberTable:
line 4: 0
(**) I thought that debug info (e.g. LocalVariableTable) would be
affected too, but in reality when debug info is enabled, const folding
is disabled for var initializer - and the enhanced condy folding support
respects that invariant, so nothing to look at there.
In order to get rid of this behavior we would need to look at avoiding
the call to code.newLocal in Gen::visitVarDef:
public void visitVarDef(JCVariableDecl tree) {
VarSymbol v = tree.sym;
code.newLocal(v); // <----------------------
if (tree.init != null) {
checkStringConstant(tree.init.pos(), v.getConstValue());
if (v.getConstValue() == null || varDebugInfo) {
Assert.check(code.isStatementStart());
genExpr(tree.init, v.erasure(types)).load();
items.makeLocalItem(v).store();
Assert.check(code.isStatementStart());
}
}
checkDimension(tree.pos(), v.type);
}
As that is what's updating the compiler internal state. In principle, if
a variable holds a constant value, then javac will always load that
constant value instead of loading the variable, see Gen::gnExpr:
if (tree.type.constValue() != null) {
// Short circuit any expressions which are constants
tree.accept(classReferenceVisitor);
checkStringConstant(tree.pos(), tree.type.constValue());
result = items.makeImmediateItem(tree.type, tree.type.constValue());
} else {
this.pt = pt;
tree.accept(this);
}
But maybe there are other cases/invariants that would be broken by
skipping the Code::newLocal call entirely - this means we'll have to do
some experiments with the code in order to rectify this. As you can see,
this problem is a general problem with constant folding, and is not
something specific to the Amber condy-folding branch (although I agree
that the new support magnifies the problem quite a bit).
Cheers
Maurizio
>
> On 9/2/2018 1:58 PM, Remi Forax wrote:
>> Hi all,
>> while trying to reproduce a bug in ASM using the constant API +
>> Intrinsics.ldc, i've spot a discrepancy,
>> using the follwing example,
>>
>> package jdk12;
>>
>> import java.lang.constant.ClassDesc;
>> import java.lang.constant.DirectMethodHandleDesc.Kind;
>> import java.lang.constant.DynamicConstantDesc;
>> import java.lang.constant.MethodHandleDesc;
>> import java.lang.constant.MethodTypeDesc;
>> import java.lang.invoke.Intrinsics;
>> import java.lang.invoke.MethodHandles.Lookup;
>>
>> public class ConstantDynamicExample {
>> public static long primitiveExample() {
>> MethodTypeDesc methodDescriptor =
>> MethodTypeDesc.ofDescriptor("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)J");
>> DynamicConstantDesc<Long> dynamicConstant = DynamicConstantDesc.of(
>> MethodHandleDesc.of(Kind.STATIC,
>> ClassDesc.of("jdk12.ConstantDynamicExample"), "bsm", methodDescriptor));
>> return Intrinsics.ldc(dynamicConstant);
>> }
>>
>> private static long bsm(Lookup lookup, String name, Class<?> type) {
>> return 3L;
>> }
>> public static void main(String[] args) {
>> System.out.println(primitiveExample());
>> }
>> }
>>
>>
>> Using javap, you can see that the dynamicConstant is folded but the
>> number of local variable (locals=2) still count the variables that
>> doesn't exist anymore
>>
>> public static long primitiveExample();
>> descriptor: ()J
>> flags: (0x0009) ACC_PUBLIC, ACC_STATIC
>> Code:
>> stack=2, locals=2, args_size=0
>> 0: ldc2_w #2 // Dynamic #0:_:J
>> 3: lreturn
>> LineNumberTable:
>> line 43: 0
>>
>> regards,
>> Rémi
>
More information about the amber-dev
mailing list