• 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 
5 package com.android.tools.r8.smali;
6 
7 import static org.junit.Assert.assertEquals;
8 import static org.junit.Assert.assertTrue;
9 
10 import com.android.tools.r8.code.Const4;
11 import com.android.tools.r8.code.IfEqz;
12 import com.android.tools.r8.code.IfGez;
13 import com.android.tools.r8.code.IfGtz;
14 import com.android.tools.r8.code.IfLez;
15 import com.android.tools.r8.code.IfLtz;
16 import com.android.tools.r8.code.IfNez;
17 import com.android.tools.r8.code.InvokeVirtual;
18 import com.android.tools.r8.code.Return;
19 import com.android.tools.r8.code.ReturnObject;
20 import com.android.tools.r8.graph.DexCode;
21 import com.android.tools.r8.graph.DexEncodedMethod;
22 import com.android.tools.r8.ir.code.If.Type;
23 import com.google.common.collect.ImmutableList;
24 import com.google.common.collect.Lists;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.List;
28 import org.junit.Test;
29 
30 public class IfSimplificationTest extends SmaliTestBase {
31 
32   static String[] ifOpcode;
33   static {
34     ifOpcode = new String[6];
35     ifOpcode[Type.EQ.ordinal()] = "if-eq";
36     ifOpcode[Type.NE.ordinal()] = "if-ne";
37     ifOpcode[Type.LE.ordinal()] = "if-le";
38     ifOpcode[Type.GE.ordinal()] = "if-ge";
39     ifOpcode[Type.LT.ordinal()] = "if-lt";
40     ifOpcode[Type.GT.ordinal()] = "if-gt";
41   }
42 
43   @Test
ifZeroNeqZero()44   public void ifZeroNeqZero() {
45     DexEncodedMethod method = oneMethodApplication(
46         "int",
47         Collections.emptyList(),
48         1,
49         "  const v0, 0",
50         "  if-nez v0, :label_2",
51         ":label_1",
52         "  return v0",
53         ":label_2",
54         "  const v0, 1",
55         "  goto :label_1");
56     DexCode code = method.getCode().asDexCode();
57     assertEquals(2, code.instructions.length);
58     assertTrue(code.instructions[0] instanceof Const4);
59     assertEquals(0, ((Const4) code.instructions[0]).B);
60     assertTrue(code.instructions[1] instanceof Return);
61   }
62 
63   @Test
ifTwoEqZero()64   public void ifTwoEqZero() {
65     DexEncodedMethod method = oneMethodApplication(
66         "int",
67         Collections.emptyList(),
68         1,
69         "  const v0, 2",
70         "  if-eqz v0, :label_2",
71         ":label_1",
72         "  return v0",
73         ":label_2",
74         "  const v0, 1",
75         "  goto :label_1");
76     DexCode code = method.getCode().asDexCode();
77     assertEquals(2, code.instructions.length);
78     assertTrue(code.instructions[0] instanceof Const4);
79     assertEquals(2, ((Const4) code.instructions[0]).B);
80     assertTrue(code.instructions[1] instanceof Return);
81   }
82 
83   @Test
b()84   public void b() {
85     DexEncodedMethod method = oneMethodApplication(
86         "int",
87         Collections.singletonList("int"),
88         1,
89         "  const v0, 0",
90         "  if-nez v0, :label_2",
91         ":label_1",
92         "  return v0",
93         ":label_2",
94         "  if-nez p0, :label_3",
95         "  const v0, 1",
96         "  goto :label_1",
97         ":label_3",
98         "  const v0, 2",
99         "  goto :label_1");
100     DexCode code = method.getCode().asDexCode();
101     assertEquals(2, code.instructions.length);
102     assertTrue(code.instructions[0] instanceof Const4);
103     assertEquals(0, ((Const4) code.instructions[0]).B);
104     assertTrue(code.instructions[1] instanceof Return);
105   }
106 
107   @Test
c()108   public void c() {
109     DexEncodedMethod method = oneMethodApplication(
110         "int",
111         Collections.singletonList("int"),
112         1,
113         "  const v0, 0",
114         "  if-nez v0, :label_2",
115         ":label_1",
116         "  return v0",
117         ":label_2",
118         "  if-nez p0, :label_3",
119         "  const v0, 1",
120         "  goto :label_1",
121         ":label_3",
122         "  const p0, 0",
123         "  goto :label_2");
124     DexCode code = method.getCode().asDexCode();
125     assertEquals(2, code.instructions.length);
126     assertTrue(code.instructions[0] instanceof Const4);
127     assertEquals(0, ((Const4) code.instructions[0]).B);
128     assertTrue(code.instructions[1] instanceof Return);
129   }
130 
131   @Test
d()132   public void d() {
133     DexEncodedMethod method = oneMethodApplication(
134         "int",
135         Collections.singletonList("int"),
136         1,
137         "  const v0, 0",
138         "  if-nez v0, :label_2",
139         ":label_1",
140         "  return v0",
141         ":label_2",
142         "  if-nez p0, :label_3",
143         "  const v0, 1",
144         "  goto :label_4",
145         ":label_3",
146         "  const p0, 0",
147         "  goto :label_2",
148         ":label_4",
149         "  if-nez p0, :label_5",
150         "  const v0, 1",
151         "  goto :label_4",
152         ":label_5",
153         "  const p0, 0",
154         "  goto :label_2");
155     DexCode code = method.getCode().asDexCode();
156     assertEquals(2, code.instructions.length);
157     assertTrue(code.instructions[0] instanceof Const4);
158     assertEquals(0, ((Const4) code.instructions[0]).B);
159     assertTrue(code.instructions[1] instanceof Return);
160   }
161 
162   @Test
e()163   public void e() {
164     DexEncodedMethod method = oneMethodApplication(
165         "int",
166         ImmutableList.of("int", "int", "int"),
167         1,
168         "  const v0, 0",
169         "  if-nez v0, :x",
170         "  const v0, 1",
171         "  if-nez p0, :x",
172         "  const v0, 2",
173         "  if-nez p1, :x",
174         "  const v0, 3",
175         "  if-nez p2, :return",
176         "  const v0, 4",
177         "  goto :return",
178         ":x",
179         "  add-int v0, v0, p0",
180         ":return",
181         "  return v0");
182     DexCode code = method.getCode().asDexCode();
183     assertEquals(12, code.instructions.length);
184     assertTrue(code.instructions[11] instanceof Return);
185   }
186 
187   @Test
f()188   public void f() {
189     DexEncodedMethod method = oneMethodApplication(
190         "int",
191         Collections.singletonList("int"),
192         1,
193         "  const v0, 0",
194         "  if-nez v0, :label_2",
195         ":label_1",
196         "  return v0",
197         ":label_2",
198         "  const v0, 1",
199         "  goto :label_2");
200     DexCode code = method.getCode().asDexCode();
201     assertEquals(2, code.instructions.length);
202     assertTrue(code.instructions[0] instanceof Const4);
203     assertEquals(0, ((Const4) code.instructions[0]).B);
204     assertTrue(code.instructions[1] instanceof Return);
205   }
206 
207   @Test
simplifyNonZeroTests()208   public void simplifyNonZeroTests() {
209     class TestData {
210 
211       final int a;
212       final int b;
213       final boolean results[];
214 
215       TestData(int a, int b) {
216         this.a = a;
217         this.b = b;
218         results = new boolean[6];
219         results[Type.EQ.ordinal()] = a == b;
220         results[Type.NE.ordinal()] = a != b;
221         results[Type.LE.ordinal()] = a <= b;
222         results[Type.GE.ordinal()] = a >= b;
223         results[Type.LT.ordinal()] = a < b;
224         results[Type.GT.ordinal()] = a > b;
225       }
226     }
227 
228     int[] testValues = new int[]{
229         100,
230         1,
231         0,
232         -1,
233         100
234     };
235 
236     List<TestData> tests = new ArrayList<>();
237     for (int i = 0; i < testValues.length; i++) {
238       for (int j = 0; j < testValues.length; j++) {
239         tests.add(new TestData(testValues[i], testValues[j]));
240       }
241     }
242 
243     for (TestData test : tests) {
244       for (Type type : Type.values()) {
245         DexEncodedMethod method = oneMethodApplication(
246             "int",
247             Collections.singletonList("int"),
248             2,
249             "  const v0, 0x" + Integer.toHexString(test.a),
250             "  const v1, 0x" + Integer.toHexString(test.b),
251             "  " + ifOpcode[type.ordinal()] + " v0, v1, :label_2",
252             "  const v0, 0",
253             ":label_1",
254             "  return v0",
255             ":label_2",
256             "  const v0, 1",
257             "  goto :label_1");
258         DexCode code = method.getCode().asDexCode();
259         assertEquals(2, code.instructions.length);
260         assertTrue(code.instructions[0] instanceof Const4);
261         int expected = test.results[type.ordinal()] ? 1 : 0;
262         assertEquals(expected, ((Const4) code.instructions[0]).B);
263         assertTrue(code.instructions[1] instanceof Return);
264       }
265     }
266   }
267 
runRewriteIfWithConstZeroTest(Type type, boolean zeroLeft, Class expected)268   public void runRewriteIfWithConstZeroTest(Type type, boolean zeroLeft, Class expected) {
269     String ifInstruction;
270     if (zeroLeft) {
271       ifInstruction = "  " + ifOpcode[type.ordinal()] + " v0, v1, :label_2";
272     } else {
273       ifInstruction = "  " + ifOpcode[type.ordinal()] + " v1, v0, :label_2";
274     }
275 
276     DexEncodedMethod method = oneMethodApplication(
277         "int",
278         Collections.singletonList("int"),
279         1,
280         "  const v0, 0x00",
281         ifInstruction,
282         "  const v0, 0",
283         ":label_1",
284         "  return v0",
285         ":label_2",
286         "  const v0, 1",
287         "  goto :label_1");
288     DexCode code = method.getCode().asDexCode();
289     assertEquals(5, code.instructions.length);
290     assertTrue(expected.isInstance(code.instructions[0]));
291     assertTrue(code.instructions[4] instanceof Return);
292   }
293 
294   @Test
testRewriteIfWithConstZero()295   public void testRewriteIfWithConstZero() {
296     runRewriteIfWithConstZeroTest(Type.EQ, true, IfEqz.class);
297     runRewriteIfWithConstZeroTest(Type.NE, true, IfNez.class);
298     runRewriteIfWithConstZeroTest(Type.LE, true, IfGez.class);
299     runRewriteIfWithConstZeroTest(Type.GE, true, IfLez.class);
300     runRewriteIfWithConstZeroTest(Type.LT, true, IfGtz.class);
301     runRewriteIfWithConstZeroTest(Type.GT, true, IfLtz.class);
302 
303     runRewriteIfWithConstZeroTest(Type.EQ, false, IfEqz.class);
304     runRewriteIfWithConstZeroTest(Type.NE, false, IfNez.class);
305     runRewriteIfWithConstZeroTest(Type.LE, false, IfLez.class);
306     runRewriteIfWithConstZeroTest(Type.GE, false, IfGez.class);
307     runRewriteIfWithConstZeroTest(Type.LT, false, IfLtz.class);
308     runRewriteIfWithConstZeroTest(Type.GT, false, IfGtz.class);
309   }
310 
311   @Test
x()312   public void x() {
313     DexEncodedMethod method = oneMethodApplication(
314         "Test",
315         Lists.newArrayList("Test", "java.lang.String[]", "java.lang.String",
316             "java.lang.String[]", "java.lang.String"),
317         10,
318         "          const/4             v4, 0x00  # 0",
319         "          invoke-virtual      { v10 }, LTest;->a()LTest;",
320         "          if-nez              v4, :label_8",
321         "          move-object         v0, v4",
322         "      :label_7",
323         "          return-object       v0",
324         "      :label_8",
325         "          invoke-static       { v14 }, LTest;->a([Ljava/lang/String;)LTest;",
326         "          move-result-object  v2",
327         "          invoke-virtual      { v2 }, LTest;->a()Z",
328         "          move-result         v0",
329         "          if-nez              v0, :label_20",
330         "          move-object         v0, v4",
331         "          goto                :label_7",
332         "      :label_20",
333         "          iget-wide           v0, v2, LTest;->a:J",
334         "          iget-wide           v6, v2, LTest;->b:J",
335         "          invoke-virtual      { v2 }, LTest;->c()Z",
336         "          move-result         v2",
337         "          if-eqz              v2, :label_33",
338         "          invoke-virtual      { v4 }, LTest;->a()V",
339         "      :label_33",
340         "          new-instance        v5, LTest;",
341         "          sget-object         v2, LTest;->a:[Ljava/lang/String;",
342         "          invoke-direct       { v5, v2 }, LTest;-><init>([Ljava/lang/String;)V",
343         "          invoke-virtual      { v10 }, LTest;->a()LTest;",
344         "          invoke-virtual      { v4, v0, v1, v6, v7 }, LTest;->a(JJ)Ljava/util/List;",
345         "          move-result-object  v2",
346         "          invoke-interface    { v2 }, Ljava/util/List;->iterator()Ljava/util/Iterator;",
347         "          move-result-object  v6",
348         "          move-wide           v2, v0",
349         "      :label_52",
350         "          invoke-interface    { v6 }, Ljava/util/Iterator;->hasNext()Z",
351         "          move-result         v0",
352         "          if-eqz              v0, :label_107",
353         "          invoke-interface    { v6 }, Ljava/util/Iterator;->next()Ljava/lang/Object;",
354         "          move-result-object  v0",
355         "          check-cast          v0, LTest;",
356         "          const-wide/16       v8, 0x0000000000000001  # 1",
357         "          add-long/2addr      v2, v8",
358         "          invoke-virtual      { v5 }, LTest;->newRow()LTest;",
359         "          move-result-object  v1",
360         "          invoke-static       { v2, v3 }, Ljava/lang/Long;->valueOf(J)Ljava/lang/Long;",
361         "          move-result-object  v7",
362         "          invoke-virtual      { v1, v7 }, LTest;->a(Ljava/lang/Object;)LTest;",
363         "          move-result-object  v1",
364         "          const-string        v7, \"add\"",
365         "          invoke-virtual      { v1, v7 }, LTest;->a(Ljava/lang/Object;)LTest;",
366         "          move-result-object  v1",
367         "          iget-object         v7, v0, LTest;->a:Ljava/lang/String;",
368         "          invoke-virtual      { v1, v7 }, LTest;->a(Ljava/lang/Object;)LTest;",
369         "          move-result-object  v1",
370         "          iget                v7, v0, LTest;->b:I",
371         "          invoke-static       { v7 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;",
372         "          move-result-object  v7",
373         "          invoke-virtual      { v1, v7 }, LTest;->add(Ljava/lang/Object;)LTest;",
374         "          move-result-object  v1",
375         "          iget-object         v0, v0, LTest;->a:Ljava/lang/String;",
376         "          invoke-virtual      { v1, v0 }, LTest;->add(Ljava/lang/Object;)LTest;",
377         "          goto                :label_52",
378         "      :label_107",
379         "          iget-object         v0, v4, LTest;->a:LTest;",
380         "          const-string        v1, \"text 1\"",
381         "          const/4             v2, 0x00  # 0",
382         "          invoke-virtual      { v0, v1, v2 }, LTest;->a(Ljava/lang/String;I)LTest;",
383         "          move-result-object  v0",
384         "          const-string        v1, \"text 2\"",
385         "          const-string        v2, \"\"",
386         "          invoke-interface    { v0, v1, v2 }, LTest;->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
387         "          move-result-object  v0",
388         "          invoke-static       { v5, v0 }, LTest;->a(LTest;Ljava/lang/String;)LTest;",
389         "          move-result-object  v0",
390         "          goto                :label_7"
391     );
392     DexCode code = method.getCode().asDexCode();
393     assertEquals(3, code.instructions.length);
394     assertTrue(code.instructions[0] instanceof InvokeVirtual);
395     assertTrue(code.instructions[1] instanceof Const4);
396     assertEquals(0, ((Const4) code.instructions[1]).B);
397     assertTrue(code.instructions[2] instanceof ReturnObject);
398   }
399 
400   @Test
y()401   public void y() {
402     DexEncodedMethod method = oneMethodApplication(
403         "boolean",
404         Lists.newArrayList("Test", "java.lang.Object"),
405         6,
406         "      const-wide/16       v4, 0x0000000000000000L  # 0",
407         "      const/4             v0, 0x01  # 1",
408         "      const/4             v3, 0x00  # 0",
409         "      const/4             v1, 0x00  # 0",
410         "      if-ne               v6, v7, :label_8",
411         "    :label_7",
412         "      return              v0",
413         "    :label_8",
414         "      if-nez              v7, :label_12",
415         "      move                v0, v1",
416         "      goto                :label_7",
417         "    :label_12",
418         "      instance-of         v2, v7, LTest;",
419         "      if-nez              v2, :label_18",
420         "      move                v0, v1",
421         "      goto                :label_7",
422         "    :label_18",
423         "      check-cast          v7, LTest;",
424         "      cmp-long            v2, v4, v4",
425         "      if-nez              v2, :label_50",
426         "      invoke-static       { v3, v3 }, LTest;->a(Ljava/lang/Object;Ljava/lang/Object;)Z",
427         "      move-result         v2",
428         "      if-eqz              v2, :label_50",
429         "      invoke-static       { v3, v3 }, LTest;->a(Ljava/lang/Object;Ljava/lang/Object;)Z",
430         "      move-result         v2",
431         "      if-eqz              v2, :label_50",
432         "      invoke-static       { v1 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;",
433         "      move-result-object  v2",
434         "      invoke-static       { v1 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;",
435         "      move-result-object  v3",
436         "      invoke-static       { v2, v3 }, LTest;->a(Ljava/lang/Object;Ljava/lang/Object;)Z",
437         "      move-result         v2",
438         "      if-nez              v2, :label_7",
439         "    :label_50",
440         "      move                v0, v1",
441         "      goto                :label_7"
442     );
443     DexCode code = method.getCode().asDexCode();
444     // TODO(sgjesse): Maybe this test is too fragile, as it leaves quite a lot of code, so the
445     // expectation might need changing with other optimizations.
446     assertEquals(29, code.instructions.length);
447   }
448 }
449