1 // Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file 2 // for details. All rights reserved. Use of this source code is governed by a 3 // BSD-style license that can be found in the LICENSE file. 4 package com.android.tools.r8.smali; 5 6 import static org.junit.Assert.assertEquals; 7 import static org.junit.Assert.assertTrue; 8 import static org.junit.Assert.fail; 9 10 import com.android.tools.r8.code.Const4; 11 import com.android.tools.r8.code.DivIntLit8; 12 import com.android.tools.r8.code.Instruction; 13 import com.android.tools.r8.code.RemIntLit8; 14 import com.android.tools.r8.code.Return; 15 import com.android.tools.r8.code.ReturnWide; 16 import com.android.tools.r8.graph.DexCode; 17 import com.android.tools.r8.graph.DexEncodedMethod; 18 import com.android.tools.r8.ir.code.Cmp.Bias; 19 import com.android.tools.r8.ir.code.If.Type; 20 import com.android.tools.r8.ir.code.SingleConstant; 21 import com.android.tools.r8.ir.code.WideConstant; 22 import com.google.common.collect.ImmutableList; 23 import java.util.ArrayList; 24 import java.util.Collections; 25 import java.util.List; 26 import org.junit.Test; 27 28 public class ConstantFoldingTest extends SmaliTestBase { 29 generateBinopTest(String type, String op, List<Long> values, Long result)30 public void generateBinopTest(String type, String op, List<Long> values, Long result) { 31 boolean wide = type.equals("long") || type.equals("double"); 32 StringBuilder source = new StringBuilder(); 33 int factor = wide ? 2 : 1; 34 for (int i = 0; i < values.size(); i++) { 35 source.append(" "); 36 source.append(wide ? "const-wide " : "const "); 37 source.append("v" + (i * factor)); 38 source.append(", "); 39 source.append("0x" + Long.toHexString(values.get(i))); 40 source.append(wide ? "L" : ""); 41 source.append("\n"); 42 } 43 44 for (int i = 0; i < values.size() - 1; i++) { 45 source.append(" "); 46 source.append(op + "-" + type + "/2addr "); 47 source.append("v" + ((i + 1) * factor)); 48 source.append(", "); 49 source.append("v" + (i * factor)); 50 source.append("\n"); 51 } 52 53 source.append(" "); 54 source.append(wide ? "return-wide " : "return "); 55 source.append("v" + ((values.size() - 1) * factor)); 56 57 DexEncodedMethod method = oneMethodApplication( 58 type, Collections.singletonList(type), 59 values.size() * factor, 60 source.toString()); 61 DexCode code = method.getCode().asDexCode(); 62 assertEquals(2, code.instructions.length); 63 if (wide) { 64 assertTrue(code.instructions[0] instanceof WideConstant); 65 assertEquals(result.longValue(), ((WideConstant) code.instructions[0]).decodedValue()); 66 assertTrue(code.instructions[1] instanceof ReturnWide); 67 } else { 68 assertTrue(code.instructions[0] instanceof SingleConstant); 69 assertEquals( 70 result.longValue(), (long) ((SingleConstant) code.instructions[0]).decodedValue()); 71 assertTrue(code.instructions[1] instanceof Return); 72 } 73 } 74 floatBits(float f)75 private long floatBits(float f) { 76 return Float.floatToIntBits(f); 77 } 78 doubleBits(double d)79 private long doubleBits(double d) { 80 return Double.doubleToLongBits(d); 81 } 82 83 ImmutableList<Long> arguments = ImmutableList.of(1L, 2L, 3L, 4L); 84 ImmutableList<Long> floatArguments = ImmutableList.of( 85 floatBits(1.0f), floatBits(2.0f), floatBits(3.0f), floatBits(4.0f)); 86 ImmutableList<Long> doubleArguments = ImmutableList.of( 87 doubleBits(1.0), doubleBits(2.0), doubleBits(3.0), doubleBits(4.0)); 88 89 @Test addFold()90 public void addFold() { 91 generateBinopTest("int", "add", arguments, 10L); 92 generateBinopTest("long", "add", arguments, 10L); 93 generateBinopTest("float", "add", floatArguments, floatBits(10.0f)); 94 generateBinopTest("double", "add", doubleArguments, doubleBits(10.0)); 95 } 96 97 @Test mulFold()98 public void mulFold() { 99 generateBinopTest("int", "mul", arguments, 24L); 100 generateBinopTest("long", "mul", arguments, 24L); 101 generateBinopTest("float", "mul", floatArguments, floatBits(24.0f)); 102 generateBinopTest("double", "mul", doubleArguments, doubleBits(24.0)); 103 } 104 105 @Test subFold()106 public void subFold() { 107 generateBinopTest("int", "sub", arguments.reverse(), -2L); 108 generateBinopTest("long", "sub", arguments.reverse(), -2L); 109 generateBinopTest("float", "sub", floatArguments.reverse(), floatBits(-2.0f)); 110 generateBinopTest("double", "sub", doubleArguments.reverse(), doubleBits(-2.0)); 111 } 112 113 @Test divFold()114 public void divFold() { 115 ImmutableList<Long> arguments = ImmutableList.of(2L, 24L, 48L, 4L); 116 ImmutableList<Long> floatArguments = ImmutableList.of( 117 floatBits(2.0f), floatBits(24.0f), floatBits(48.0f), floatBits(4.0f)); 118 ImmutableList<Long> doubleArguments = ImmutableList.of( 119 doubleBits(2.0), doubleBits(24.0), doubleBits(48.0), doubleBits(4.0)); 120 121 generateBinopTest("int", "div", arguments, 1L); 122 generateBinopTest("long", "div", arguments, 1L); 123 generateBinopTest("float", "div", floatArguments, floatBits(1.0f)); 124 generateBinopTest("double", "div", doubleArguments, doubleBits(1.0)); 125 } 126 127 128 @Test remFold()129 public void remFold() { 130 ImmutableList<Long> arguments = ImmutableList.of(10L, 6L, 3L, 2L); 131 ImmutableList<Long> floatArguments = ImmutableList.of( 132 floatBits(10.0f), floatBits(6.0f), floatBits(3.0f), floatBits(2.0f)); 133 ImmutableList<Long> doubleArguments = ImmutableList.of( 134 doubleBits(10.0), doubleBits(6.0), doubleBits(3.0), doubleBits(2.0)); 135 136 generateBinopTest("int", "rem", arguments, 2L); 137 generateBinopTest("long", "rem", arguments, 2L); 138 generateBinopTest("float", "rem", floatArguments, floatBits(2.0f)); 139 generateBinopTest("double", "rem", doubleArguments, doubleBits(2.0)); 140 } 141 142 @Test divIntFoldDivByZero()143 public void divIntFoldDivByZero() { 144 DexEncodedMethod method = oneMethodApplication( 145 "int", Collections.singletonList("int"), 146 2, 147 " const/4 v0, 1 ", 148 " const/4 v1, 0 ", 149 " div-int/2addr v0, v1 ", 150 " return v0\n " 151 ); 152 DexCode code = method.getCode().asDexCode(); 153 // Division by zero is not folded, but div-int/lit8 is used. 154 assertEquals(3, code.instructions.length); 155 assertTrue(code.instructions[0] instanceof Const4); 156 assertTrue(code.instructions[1] instanceof DivIntLit8); 157 assertEquals(0, ((DivIntLit8) code.instructions[1]).CC); 158 assertTrue(code.instructions[2] instanceof Return); 159 } 160 161 @Test divIntFoldRemByZero()162 public void divIntFoldRemByZero() { 163 DexEncodedMethod method = oneMethodApplication( 164 "int", Collections.singletonList("int"), 165 2, 166 " const/4 v0, 1 ", 167 " const/4 v1, 0 ", 168 " rem-int/2addr v0, v1 ", 169 " return v0\n " 170 ); 171 DexCode code = method.getCode().asDexCode(); 172 // Division by zero is not folded, but rem-int/lit8 is used. 173 assertEquals(3, code.instructions.length); 174 assertTrue(code.instructions[0] instanceof Const4); 175 assertTrue(code.instructions[1] instanceof RemIntLit8); 176 assertEquals(0, ((RemIntLit8) code.instructions[1]).CC); 177 assertTrue(code.instructions[2] instanceof Return); 178 } 179 generateUnopTest(String type, String op, Long value, Long result)180 public void generateUnopTest(String type, String op, Long value, Long result) { 181 boolean wide = type.equals("long") || type.equals("double"); 182 StringBuilder source = new StringBuilder(); 183 source.append(" "); 184 source.append(wide ? "const-wide " : "const "); 185 source.append("v0 , "); 186 source.append("0x" + Long.toHexString(value)); 187 source.append(wide ? "L" : ""); 188 source.append("\n"); 189 190 source.append(" "); 191 source.append(op + "-" + type + " v0, v0\n"); 192 193 source.append(" "); 194 source.append(wide ? "return-wide v0" : "return v0"); 195 196 DexEncodedMethod method = oneMethodApplication( 197 type, Collections.singletonList(type), 198 wide ? 2 : 1, 199 source.toString()); 200 DexCode code = method.getCode().asDexCode(); 201 assertEquals(2, code.instructions.length); 202 if (wide) { 203 assertTrue(code.instructions[0] instanceof WideConstant); 204 assertEquals(result.longValue(), ((WideConstant) code.instructions[0]).decodedValue()); 205 assertTrue(code.instructions[1] instanceof ReturnWide); 206 } else { 207 assertTrue(code.instructions[0] instanceof SingleConstant); 208 assertEquals( 209 result.longValue(), (long) ((SingleConstant) code.instructions[0]).decodedValue()); 210 assertTrue(code.instructions[1] instanceof Return); 211 } 212 } 213 214 @Test negFold()215 public void negFold() { 216 generateUnopTest("int", "neg", 2L, -2L); 217 generateUnopTest("int", "neg", -2L, 2L); 218 generateUnopTest("long", "neg", 2L, -2L); 219 generateUnopTest("long", "neg", -2L, 2L); 220 generateUnopTest("float", "neg", floatBits(2.0f), floatBits(-2.0f)); 221 generateUnopTest("float", "neg", floatBits(-2.0f), floatBits(2.0f)); 222 generateUnopTest("float", "neg", floatBits(0.0f), floatBits(-0.0f)); 223 generateUnopTest("float", "neg", floatBits(-0.0f), floatBits(0.0f)); 224 generateUnopTest("double", "neg", doubleBits(2.0), doubleBits(-2.0)); 225 generateUnopTest("double", "neg", doubleBits(-2.0), doubleBits(2.0)); 226 generateUnopTest("double", "neg", doubleBits(0.0), doubleBits(-0.0)); 227 generateUnopTest("double", "neg", doubleBits(-0.0), doubleBits(0.0)); 228 } 229 assertConstValue(int expected, Instruction insn)230 private void assertConstValue(int expected, Instruction insn) { 231 assertTrue(insn instanceof SingleConstant); 232 assertEquals(expected, ((SingleConstant) insn).decodedValue()); 233 } 234 assertConstValue(long expected, Instruction insn)235 private void assertConstValue(long expected, Instruction insn) { 236 assertTrue(insn instanceof WideConstant); 237 assertEquals(expected, ((WideConstant) insn).decodedValue()); 238 } 239 testLogicalOperatorsFolding(String op, int[] v)240 public void testLogicalOperatorsFolding(String op, int[] v) { 241 int v0 = v[0]; 242 int v1 = v[1]; 243 int v2 = v[2]; 244 int v3 = v[3]; 245 246 int expected = 0; 247 switch (op) { 248 case "and": 249 expected = v0 & v1 & v2 & v3; 250 break; 251 case "or": 252 expected = v0 | v1 | v2 | v3; 253 break; 254 case "xor": 255 expected = v0 ^ v1 ^ v2 ^ v3; 256 break; 257 default: 258 fail("Unsupported logical binop " + op); 259 } 260 261 DexEncodedMethod method = oneMethodApplication( 262 "int", Collections.singletonList("int"), 263 4, 264 " const v0, " + v0, 265 " const v1, " + v1, 266 " const v2, " + v2, 267 " const v3, " + v3, 268 // E.g. and-int//2addr v1, v0 269 " " + op + "-int/2addr v1, v0 ", 270 " " + op + "-int/2addr v2, v1 ", 271 " " + op + "-int/2addr v3, v2 ", 272 " return v3\n " 273 ); 274 DexCode code = method.getCode().asDexCode(); 275 // Test that this just returns a constant. 276 assertEquals(2, code.instructions.length); 277 assertConstValue(expected, code.instructions[0]); 278 assertTrue(code.instructions[1] instanceof Return); 279 } 280 281 @Test logicalOperatorsFolding()282 public void logicalOperatorsFolding() { 283 int[][] testValues = new int[][]{ 284 new int[]{0x00, 0x00, 0x00, 0x00}, 285 new int[]{0x0b, 0x06, 0x03, 0x00}, 286 new int[]{0x0f, 0x07, 0x03, 0x01}, 287 new int[]{0x08, 0x04, 0x02, 0x01}, 288 }; 289 290 for (int[] values : testValues) { 291 testLogicalOperatorsFolding("and", values); 292 testLogicalOperatorsFolding("or", values); 293 testLogicalOperatorsFolding("xor", values); 294 } 295 } 296 testShiftOperatorsFolding(String op, int[] v)297 private void testShiftOperatorsFolding(String op, int[] v) { 298 int v0 = v[0]; 299 int v1 = v[1]; 300 int v2 = v[2]; 301 int v3 = v[3]; 302 303 int expected = 0; 304 switch (op) { 305 case "shl": 306 v0 = v0 << v1; 307 v0 = v0 << v2; 308 v0 = v0 << v3; 309 break; 310 case "shr": 311 v0 = v0 >> v1; 312 v0 = v0 >> v2; 313 v0 = v0 >> v3; 314 break; 315 case "ushr": 316 v0 = v0 >>> v1; 317 v0 = v0 >>> v2; 318 v0 = v0 >>> v3; 319 break; 320 default: 321 fail("Unsupported shift " + op); 322 } 323 expected = v0; 324 325 DexEncodedMethod method = oneMethodApplication( 326 "int", Collections.singletonList("int"), 327 4, 328 " const v0, " + v[0], 329 " const v1, " + v[1], 330 " const v2, " + v[2], 331 " const v3, " + v[3], 332 // E.g. and-int//2addr v1, v0 333 " " + op + "-int/2addr v0, v1 ", 334 " " + op + "-int/2addr v0, v2 ", 335 " " + op + "-int/2addr v0, v3 ", 336 " return v0\n " 337 ); 338 DexCode code = method.getCode().asDexCode(); 339 // Test that this just returns a constant. 340 assertEquals(2, code.instructions.length); 341 assertConstValue(expected, code.instructions[0]); 342 assertTrue(code.instructions[1] instanceof Return); 343 } 344 345 @Test shiftOperatorsFolding()346 public void shiftOperatorsFolding() { 347 int[][] testValues = new int[][]{ 348 new int[]{0x01, 0x01, 0x01, 0x01}, 349 new int[]{0x01, 0x02, 0x03, 0x04}, 350 new int[]{0x7f000000, 0x01, 0x2, 0x03}, 351 new int[]{0x80000000, 0x01, 0x2, 0x03}, 352 new int[]{0xffffffff, 0x01, 0x2, 0x03}, 353 }; 354 355 for (int[] values : testValues) { 356 testShiftOperatorsFolding("shl", values); 357 testShiftOperatorsFolding("shr", values); 358 testShiftOperatorsFolding("ushr", values); 359 } 360 } 361 testShiftOperatorsFoldingWide(String op, long[] v)362 private void testShiftOperatorsFoldingWide(String op, long[] v) { 363 long v0 = v[0]; 364 int v2 = (int) v[1]; 365 int v4 = (int) v[2]; 366 int v6 = (int) v[3]; 367 368 long expected = 0; 369 switch (op) { 370 case "shl": 371 v0 = v0 << v2; 372 v0 = v0 << v4; 373 v0 = v0 << v6; 374 break; 375 case "shr": 376 v0 = v0 >> v2; 377 v0 = v0 >> v4; 378 v0 = v0 >> v6; 379 break; 380 case "ushr": 381 v0 = v0 >>> v2; 382 v0 = v0 >>> v4; 383 v0 = v0 >>> v6; 384 break; 385 default: 386 fail("Unsupported shift " + op); 387 } 388 expected = v0; 389 390 DexEncodedMethod method = oneMethodApplication( 391 "long", Collections.singletonList("long"), 392 5, 393 " const-wide v0, 0x" + Long.toHexString(v[0]) + "L", 394 " const v2, " + v[1], 395 " const v3, " + v[2], 396 " const v4, " + v[3], 397 // E.g. and-long//2addr v1, v0 398 " " + op + "-long/2addr v0, v2 ", 399 " " + op + "-long/2addr v0, v3 ", 400 " " + op + "-long/2addr v0, v4 ", 401 " return-wide v0\n " 402 ); 403 DexCode code = method.getCode().asDexCode(); 404 // Test that this just returns a constant. 405 assertEquals(2, code.instructions.length); 406 assertConstValue(expected, code.instructions[0]); 407 assertTrue(code.instructions[1] instanceof ReturnWide); 408 } 409 410 @Test shiftOperatorsFoldingWide()411 public void shiftOperatorsFoldingWide() { 412 long[][] testValues = new long[][]{ 413 new long[]{0x01, 0x01, 0x01, 0x01}, 414 new long[]{0x01, 0x02, 0x03, 0x04}, 415 new long[]{0x7f0000000000L, 0x01, 0x2, 0x03}, 416 new long[]{0x800000000000L, 0x01, 0x2, 0x03}, 417 new long[]{0x7f00000000000000L, 0x01, 0x2, 0x03}, 418 new long[]{0x8000000000000000L, 0x01, 0x2, 0x03}, 419 new long[]{0xffffffffffffffffL, 0x01, 0x2, 0x03}, 420 }; 421 422 for (long[] values : testValues) { 423 testShiftOperatorsFoldingWide("shl", values); 424 testShiftOperatorsFoldingWide("shr", values); 425 testShiftOperatorsFoldingWide("ushr", values); 426 } 427 } 428 429 @Test notIntFold()430 public void notIntFold() { 431 int[] testValues = new int[]{0, 1, 0xff, 0xffffffff, 0xff000000, 0x80000000}; 432 for (int value : testValues) { 433 DexEncodedMethod method = oneMethodApplication( 434 "int", Collections.emptyList(), 435 1, 436 " const v0, " + value, 437 " not-int v0, v0", 438 " return v0" 439 ); 440 DexCode code = method.getCode().asDexCode(); 441 assertEquals(2, code.instructions.length); 442 assertConstValue(~value, code.instructions[0]); 443 assertTrue(code.instructions[1] instanceof Return); 444 } 445 } 446 447 @Test notLongFold()448 public void notLongFold() { 449 long[] testValues = new long[]{ 450 0L, 451 1L, 452 0xffL, 453 0xffffffffffffffffL, 454 0x00ffffffffffffffL, 455 0xff00000000000000L, 456 0x8000000000000000L 457 }; 458 for (long value : testValues) { 459 DexEncodedMethod method = oneMethodApplication( 460 "long", Collections.emptyList(), 461 2, 462 " const-wide v0, 0x" + Long.toHexString(value) + "L", 463 " not-long v0, v0", 464 " return-wide v0" 465 ); 466 DexCode code = method.getCode().asDexCode(); 467 assertEquals(2, code.instructions.length); 468 assertConstValue(~value, code.instructions[0]); 469 assertTrue(code.instructions[1] instanceof ReturnWide); 470 } 471 } 472 473 @Test negIntFold()474 public void negIntFold() { 475 int[] testValues = new int[]{0, 1, 0xff, 0xffffffff, 0xff000000, 0x80000000}; 476 for (int value : testValues) { 477 DexEncodedMethod method = oneMethodApplication( 478 "int", Collections.emptyList(), 479 1, 480 " const v0, " + value, 481 " neg-int v0, v0", 482 " return v0" 483 ); 484 DexCode code = method.getCode().asDexCode(); 485 assertEquals(2, code.instructions.length); 486 assertConstValue(-value, code.instructions[0]); 487 assertTrue(code.instructions[1] instanceof Return); 488 } 489 } 490 491 @Test negLongFold()492 public void negLongFold() { 493 long[] testValues = new long[]{ 494 0L, 495 1L, 496 0xffL, 497 0xffffffffffffffffL, 498 0x00ffffffffffffffL, 499 0xff00000000000000L, 500 0x8000000000000000L 501 }; 502 for (long value : testValues) { 503 DexEncodedMethod method = oneMethodApplication( 504 "long", Collections.emptyList(), 505 2, 506 " const-wide v0, 0x" + Long.toHexString(value) + "L", 507 " neg-long v0, v0", 508 " return-wide v0" 509 ); 510 DexCode code = method.getCode().asDexCode(); 511 assertEquals(2, code.instructions.length); 512 long expected = -value; 513 assertConstValue(-value, code.instructions[0]); 514 assertTrue(code.instructions[1] instanceof ReturnWide); 515 } 516 } 517 518 @Test cmpFloatFold()519 public void cmpFloatFold() { 520 String[] ifOpcode = new String[6]; 521 ifOpcode[Type.EQ.ordinal()] = "if-eqz"; 522 ifOpcode[Type.NE.ordinal()] = "if-nez"; 523 ifOpcode[Type.LE.ordinal()] = "if-lez"; 524 ifOpcode[Type.GE.ordinal()] = "if-gez"; 525 ifOpcode[Type.LT.ordinal()] = "if-ltz"; 526 ifOpcode[Type.GT.ordinal()] = "if-gtz"; 527 528 class FloatTestData { 529 530 final float a; 531 final float b; 532 final boolean results[]; 533 534 FloatTestData(float a, float b) { 535 this.a = a; 536 this.b = b; 537 results = new boolean[6]; 538 results[Type.EQ.ordinal()] = a == b; 539 results[Type.NE.ordinal()] = a != b; 540 results[Type.LE.ordinal()] = a <= b; 541 results[Type.GE.ordinal()] = a >= b; 542 results[Type.LT.ordinal()] = a < b; 543 results[Type.GT.ordinal()] = a > b; 544 } 545 } 546 547 float[] testValues = new float[]{ 548 Float.NEGATIVE_INFINITY, 549 -100.0f, 550 -0.0f, 551 0.0f, 552 100.0f, 553 Float.POSITIVE_INFINITY, 554 Float.NaN 555 }; 556 557 List<FloatTestData> tests = new ArrayList<>(); 558 for (int i = 0; i < testValues.length; i++) { 559 for (int j = 0; j < testValues.length; j++) { 560 tests.add(new FloatTestData(testValues[i], testValues[j])); 561 } 562 } 563 564 for (FloatTestData test : tests) { 565 for (Type type : Type.values()) { 566 for (Bias bias : Bias.values()) { 567 if (bias == Bias.NONE) { 568 // Bias NONE is only for long comparison. 569 continue; 570 } 571 // If no NaNs are involved either bias produce the same result. 572 if (Float.isNaN(test.a) || Float.isNaN(test.b)) { 573 // For NaN comparison only test with the bias that provide Java semantics. 574 // The Java Language Specification 4.2.3. Floating-Point Types, Formats, and Values 575 // says: 576 // 577 // The numerical comparison operators <, <=, >, and >= return false if either or both 578 // operands are NaN 579 if ((type == Type.GE || type == Type.GT) && bias == Bias.GT) { 580 continue; 581 } 582 if ((type == Type.LE || type == Type.LT) && bias == Bias.LT) { 583 continue; 584 } 585 } 586 String cmpInstruction; 587 if (bias == Bias.LT) { 588 cmpInstruction = " cmpl-float v0, v0, v1"; 589 } else { 590 cmpInstruction = " cmpg-float v0, v0, v1"; 591 } 592 DexEncodedMethod method = oneMethodApplication( 593 "int", Collections.emptyList(), 594 2, 595 " const v0, 0x" + Integer.toHexString(Float.floatToRawIntBits(test.a)), 596 " const v1, 0x" + Integer.toHexString(Float.floatToRawIntBits(test.b)), 597 cmpInstruction, 598 " " + ifOpcode[type.ordinal()] + " v0, :label_2", 599 " const v0, 0", 600 ":label_1", 601 " return v0", 602 ":label_2", 603 " const v0, 1", 604 " goto :label_1" 605 ); 606 DexCode code = method.getCode().asDexCode(); 607 assertEquals(2, code.instructions.length); 608 int expected = test.results[type.ordinal()] ? 1 : 0; 609 assertConstValue(expected, code.instructions[0]); 610 assertTrue(code.instructions[1] instanceof Return); 611 } 612 } 613 } 614 } 615 616 @Test cmpDoubleFold()617 public void cmpDoubleFold() { 618 String[] ifOpcode = new String[6]; 619 ifOpcode[Type.EQ.ordinal()] = "if-eqz"; 620 ifOpcode[Type.NE.ordinal()] = "if-nez"; 621 ifOpcode[Type.LE.ordinal()] = "if-lez"; 622 ifOpcode[Type.GE.ordinal()] = "if-gez"; 623 ifOpcode[Type.LT.ordinal()] = "if-ltz"; 624 ifOpcode[Type.GT.ordinal()] = "if-gtz"; 625 626 class DoubleTestData { 627 628 final double a; 629 final double b; 630 final boolean results[]; 631 632 DoubleTestData(double a, double b) { 633 this.a = a; 634 this.b = b; 635 results = new boolean[6]; 636 results[Type.EQ.ordinal()] = a == b; 637 results[Type.NE.ordinal()] = a != b; 638 results[Type.LE.ordinal()] = a <= b; 639 results[Type.GE.ordinal()] = a >= b; 640 results[Type.LT.ordinal()] = a < b; 641 results[Type.GT.ordinal()] = a > b; 642 } 643 } 644 645 double[] testValues = new double[]{ 646 Double.NEGATIVE_INFINITY, 647 -100.0f, 648 -0.0f, 649 0.0f, 650 100.0f, 651 Double.POSITIVE_INFINITY, 652 Double.NaN 653 }; 654 655 List<DoubleTestData> tests = new ArrayList<>(); 656 for (int i = 0; i < testValues.length; i++) { 657 for (int j = 0; j < testValues.length; j++) { 658 tests.add(new DoubleTestData(testValues[i], testValues[j])); 659 } 660 } 661 662 for (DoubleTestData test : tests) { 663 for (Type type : Type.values()) { 664 for (Bias bias : Bias.values()) { 665 if (bias == Bias.NONE) { 666 // Bias NONE is only for long comparison. 667 continue; 668 } 669 if (Double.isNaN(test.a) || Double.isNaN(test.b)) { 670 // For NaN comparison only test with the bias that provide Java semantics. 671 // The Java Language Specification 4.2.3. Doubleing-Point Types, Formats, and Values 672 // says: 673 // 674 // The numerical comparison operators <, <=, >, and >= return false if either or both 675 // operands are NaN 676 if ((type == Type.GE || type == Type.GT) && bias == Bias.GT) { 677 continue; 678 } 679 if ((type == Type.LE || type == Type.LT) && bias == Bias.LT) { 680 continue; 681 } 682 } 683 String cmpInstruction; 684 if (bias == Bias.LT) { 685 cmpInstruction = " cmpl-double v0, v0, v2"; 686 } else { 687 cmpInstruction = " cmpg-double v0, v0, v2"; 688 } 689 DexEncodedMethod method = oneMethodApplication( 690 "int", Collections.emptyList(), 691 4, 692 " const-wide v0, 0x" + Long.toHexString(Double.doubleToRawLongBits(test.a)) + "L", 693 " const-wide v2, 0x" + Long.toHexString(Double.doubleToRawLongBits(test.b)) + "L", 694 cmpInstruction, 695 " " + ifOpcode[type.ordinal()] + " v0, :label_2", 696 " const v0, 0", 697 ":label_1", 698 " return v0", 699 ":label_2", 700 " const v0, 1", 701 " goto :label_1" 702 ); 703 DexCode code = method.getCode().asDexCode(); 704 assertEquals(2, code.instructions.length); 705 int expected = test.results[type.ordinal()] ? 1 : 0; 706 assertConstValue(expected, code.instructions[0]); 707 assertTrue(code.instructions[1] instanceof Return); 708 } 709 } 710 } 711 } 712 713 @Test cmpLongFold()714 public void cmpLongFold() { 715 long[][] longValues = new long[][]{ 716 {Long.MIN_VALUE, 1L}, 717 {Long.MAX_VALUE, 1L}, 718 {Long.MIN_VALUE, 0L}, 719 {Long.MAX_VALUE, 0L}, 720 {Long.MIN_VALUE, -1L}, 721 {Long.MAX_VALUE, -1L}, 722 }; 723 724 for (long[] values : longValues) { 725 DexEncodedMethod method = oneMethodApplication( 726 "int", Collections.emptyList(), 727 4, 728 " const-wide v0, 0x" + Long.toHexString(values[0]) + "L", 729 " const-wide v2, 0x" + Long.toHexString(values[1]) + "L", 730 " cmp-long v0, v0, v2", 731 " return v0" 732 ); 733 DexCode code = method.getCode().asDexCode(); 734 assertEquals(2, code.instructions.length); 735 assertConstValue(Long.compare(values[0], values[1]), code.instructions[0]); 736 assertTrue(code.instructions[1] instanceof Return); 737 } 738 } 739 } 740