• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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