Was it legal for the Java compiler to omit getfield opcodes after the first access?

I experimented with a Java port with some C # code , and I was surprised to see that javac 1.8.0_60 emitted getfield opcode every time an object field was accessed.

Here is the Java code:

 public class BigInteger { private int[] bits; private int sign; //... public byte[] ToByteArray() { if (sign == 0) { return new byte[] { 0 }; } byte highByte; int nonZeroDwordIndex = 0; int highDword; if (bits == null) { highByte = (byte)((sign < 0) ? 0xff : 0x00); highDword = sign; } else if (sign == -1) { highByte = (byte)0xff; assert bits.length > 0; assert bits[bits.length - 1] != 0; while (bits[nonZeroDwordIndex] == 0) { nonZeroDwordIndex++; } highDword = ~bits[bits.length - 1]; if (bits.length - 1 == nonZeroDwordIndex) { highDword += 1; } } else { assert sign == 1; highByte = 0x00; highDword = bits[bits.length - 1]; } byte msb; int msbIndex; if ((msb = (byte)(highDword >>> 24)) != highByte) { msbIndex = 3; } else if ((msb = (byte)(highDword >>> 16)) != highByte) { msbIndex = 2; } else if ((msb = (byte)(highDword >>> 8)) != highByte) { msbIndex = 1; } else { msb = (byte)highDword; msbIndex = 0; } boolean needExtraByte = (msb & 0x80) != (highByte & 0x80); byte[] bytes; int curByte = 0; if (bits == null) { bytes = new byte[msbIndex + 1 + (needExtraByte ? 1 : 0)]; assert bytes.length <= 4; } else { bytes = new byte[4 * (bits.length - 1) + msbIndex + 1 + (needExtraByte ? 1 : 0)]; for (int i = 0; i < bits.length - 1; i++) { int dword = bits[i]; if (sign == -1) { dword = ~dword; if (i <= nonZeroDwordIndex) { dword = dword + 1; } } for (int j = 0; j < 4; j++) { bytes[curByte++] = (byte)dword; dword >>>= 8; } } } for (int j = 0; j <= msbIndex; j++) { bytes[curByte++] = (byte)highDword; highDword >>>= 8; } if (needExtraByte) { bytes[bytes.length - 1] = highByte; } return bytes; } } 

As javap reported, javac 1.8.0_60 creates the following bytecode:

  public byte [] ToByteArray ();
     Code:
        0: aload_0
        1: getfield # 3 // Field sign: I
        4: ifne 15
        7: iconst_1
        8: newarray byte
       10: dup
       11: iconst_0
       12: iconst_0
       13: bastore
       14: areturn
       15: iconst_0
       16: istore_2
       17: aload_0
       18: getfield # 2 // Field bits: [I
       21: ifnonnull 48
       24: aload_0
       25: getfield # 3 // Field sign: I
       28: ifge 37
       31: sipush 255
       34: goto 38
       37: iconst_0
       38: i2b
       39: istore_1
       40: aload_0
       41: getfield # 3 // Field sign: I
       44: istore_3
       45: goto 193
       48: aload_0
       49: getfield # 3 // Field sign: I
       52: iconst_m1
       53: if_icmpne 156
       56: iconst_m1
       57: istore_1
       58: getstatic # 11 // Field $ assertionsDisabled: Z
       61: ifne 80
       64: aload_0
       65: getfield # 2 // Field bits: [I
       68: arraylength
       69: ifgt 80
       72: new # 12 // class java / lang / AssertionError
       75: dup
       76: invokespecial # 13 // Method java / lang / AssertionError. "" :() V
       79: athrow
       80: getstatic # 11 // Field $ assertionsDisabled: Z
       83: ifne 109
       86: aload_0
       87: getfield # 2 // Field bits: [I
       90: aload_0
       91: getfield # 2 // Field bits: [I
       94: arraylength
       95: iconst_1
       96: isub
       97: iaload
       98: ifne 109
      101: new # 12 // class java / lang / AssertionError
      104: dup
      105: invokespecial # 13 // Method java / lang / AssertionError. "" :() V
      108: athrow
      109: aload_0
      110: getfield # 2 // Field bits: [I
      113: iload_2
      114: iaload
      115: ifne 124
      118: iinc 2, 1
      121: goto 109
      124: aload_0
      125: getfield # 2 // Field bits: [I
      128: aload_0
      129: getfield # 2 // Field bits: [I
      132: arraylength
      133: iconst_1
      134: isub
      135: iaload
      136: iconst_m1
      137: ixor
      138: istore_3
      139: aload_0
      140: getfield # 2 // Field bits: [I
      143: arraylength
      144: iconst_1
      145: isub
      146: iload_2
      147: if_icmpne 193
      150: iinc 3, 1
      153: goto 193
      156: getstatic # 11 // Field $ assertionsDisabled: Z
      159: ifne 178
      162: aload_0
      163: getfield # 3 // Field sign: I
      166: iconst_1
      167: if_icmpeq 178
      170: new # 12 // class java / lang / AssertionError
      173: dup
      174: invokespecial # 13 // Method java / lang / AssertionError. "" :() V
      177: athrow
      178: iconst_0
      179: istore_1
      180: aload_0
      181: getfield # 2 // Field bits: [I
      184: aload_0
      185: getfield # 2 // Field bits: [I
      188: arraylength
      189: iconst_1
      190: isub
      191: iaload
      192: istore_3
      193: iload_3
      194: bipush 24
      196: iushr
      197: i2b
      198: dup
      199: istore 4
      201: iload_1
      202: if_icmpeq 211
      205: iconst_3
      206: istore 5
      208: goto 254
      211: iload_3
      212: bipush 16
      214: iushr
      215: i2b
      216: dup
      217: istore 4
      219: iload_1
      220: if_icmpeq 229
      223: iconst_2
      224: istore 5
      226: goto 254
      229: iload_3
      230: bipush 8
      232: iushr
      233: i2b
      234: dup
      235: istore 4
      237: iload_1
      238: if_icmpeq 247
      241: iconst_1
      242: istore 5
      244: goto 254
      247: iload_3
      248: i2b
      249: istore 4
      251: iconst_0
      252: istore 5
      254: iload 4
      256: sipush 128
      259: iand
      260: iload_1
      261: sipush 128
      264: iand
      265: if_icmpeq 272
      268: iconst_1
      269: goto 273
      272: iconst_0
      273: istore 6
      275: iconst_0
      276: istore 8
      278: aload_0
      279: getfield # 2 // Field bits: [I
      282: ifnonnull 325
      285: iload 5
      287: iconst_1
      288: iadd
      289: iload 6
      291: ifeq 298
      294: iconst_1
      295: goto 299
      298: iconst_0
      299: iadd
      300: newarray byte
      302: astore 7
      304: getstatic # 11 // Field $ assertionsDisabled: Z
      307: ifne 443
      310: aload 7
      312: arraylength
      313: iconst_4
      314: if_icmple 443
      317: new # 12 // class java / lang / AssertionError
      320: dup
      321: invokespecial # 13 // Method java / lang / AssertionError. "" :() V
      324: athrow
      325: iconst_4
      326: aload_0
      327: getfield # 2 // Field bits: [I
      330: arraylength
      331: iconst_1
      332: isub
      333: imul
      334: iload 5
      336: iadd
      337: iconst_1
      338: iadd
      339: iload 6
      341: ifeq 348
      344: iconst_1
      345: goto 349
      348: iconst_0
      349: iadd
      350: newarray byte
      352: astore 7
      354: iconst_0
      355: istore 9
      357: iload 9
      359: aload_0
      360: getfield # 2 // Field bits: [I
      363: arraylength
      364: iconst_1
      365: isub
      366: if_icmpge 443
      369: aload_0
      370: getfield # 2 // Field bits: [I
      373: iload 9
      375: iaload
      376: istore 10
      378: aload_0
      379: getfield # 3 // Field sign: I
      382: iconst_m1
      383: if_icmpne 404
      386: iload 10
      388: iconst_m1
      389: ixor
      390: istore 10
      392: iload 9
      394: iload_2
      395: if_icmpgt 404
      398: iload 10
      400: iconst_1
      401: iadd
      402: istore 10
      404: iconst_0
      405: istore 11
      407: iload 11
      409: iconst_4
      410: if_icmpge 437
      413: aload 7
      415: iload 8
      417: iinc 8, 1
      420: iload 10
      422: i2b
      423: bastore
      424: iload 10
      426: bipush 8
      428: iushr
      429: istore 10
      431: iinc 11, 1
      434: goto 407
      437: iinc 9, 1
      440: goto 357
      443: iconst_0
      444: istore 9
      446: iload 9
      448: iload 5
      450: if_icmpgt 474
      453: aload 7
      455: iload 8
      457: iinc 8, 1
      460: iload_3
      461: i2b
      462: bastore
      463: iload_3
      464: bipush 8
      466: iushr
      467: istore_3
      468: iinc 9, 1
      471: goto 446
      474: iload 6
      476: ifeq 488
      479: aload 7
      481: aload 7
      483: arraylength
      484: iconst_1
      485: isub
      486: iload_1
      487: bastore
      488: aload 7
      490: areturn

Note that the getfield operation getfield emitted by the compiler every time they were accessed by the sign and bits fields.

Reading § 17.4.5, “Running earlier, from JLS8”, I see no reason why the getfield operation code will be requested every time the sign and bits fields are available (except for the first time).

Would it be legal for the Java compiler to emit only two getfield code getfield and store the field values ​​visible then in local variables of the frame?

+5
source share
1 answer

It is not only legal, but will most likely happen as soon as the code is compiled by the JIT compiler (data recovery is one of the available optimizations).

For example, the code below:

 public class Test { private boolean stop; public static void main(String[] args) throws InterruptedException { Test t = new Test(); new Thread(t::m).start(); // Thread.sleep(1000); System.out.println("stop is now true"); t.stop = true; } private void m() { while (!stop); System.out.println("Finished"); } } 

ends promptly (at least on my machine). This is not guaranteed, but since the field is retrieved each time, there is a point at which the change propagates and is captured.

If I uncomment Thread.sleep(1000) , however, the program never ends because JIT has enough time to optimize the code and replace stop with a hard coded value, i.e. false

+4
source

Source: https://habr.com/ru/post/1233180/


All Articles