Stavo sperimentando una porta Java di some C# code e sono stato sorpreso di vedere che javac 1.8.0_60 emetteva un opcode getfield
ogni volta che si accede a un campo oggetto.Sarebbe legale per un compilatore Java omettere gli opcode di getfield dopo il primo accesso?
Ecco il codice Java:
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;
}
}
Secondo quanto riportato da javap, javac 1.8.0_60 produce i seguenti 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
Nota che un codice operativo getfield
è stata emessa dal compilatore di volta in volta accesso ai campi sign
e bits
.
§17.4.5 lettura, Happens-prima di ordine, di JLS8, non sto vedendo il motivo per cui sarebbe necessario per emettere un codice operativo getfield
ogni volta che i campi sign
e bits
si accede (diverso la prima volta).
Sarebbe legale per un compilatore Java emettere solo due codici opzionali getfield
e salvare i valori allora visibili dei campi nelle variabili locali del frame?
Grazie per la risposta. Ho provato [jitwatch] (https://github.com/AdoptOpenJDK/jitwatch) su un programma che chiama ToByteArray() su vari input, ripetutamente, e dormendo tra le esecuzioni. Il JIT ricompila il metodo 4 volte durante una corsa del mio test. Nella compilazione finale, l'assembly x86-64 ottiene i valori dei campi 'sign' e' bits' solo una volta. Ho anche provato una versione in cui ho usato esplicitamente le variabili locali. L'assembly x86-64 di questa versione "locals" ottiene anche i valori dei campi 'sign' e' bits' solo una volta. È interessante notare che la versione "membri" è 34 byte asm più breve. –
@JohnM "* L'ottimizzatore non assumerà che una variabile non cambierà mai solo perché non ha un secondo *" => non è quello che sto dicendo. 'stop' non viene mai modificato in' m' ed è una variabile locale, non volatile. Quindi l'ottimizzatore può riscrivere legalmente 'm' come:' booleano localStop = stop; while (! localStop); '(e lo fa). Se 'stop' fosse stato 'volatile', non sarebbe stato un cambiamento legale. Il punto del sonno è di dare abbastanza tempo per la JIT per dare il via e compilare il codice. – assylias