1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import java.lang.reflect.Method;
18
19public class Main {
20
21  // A dummy value to defeat inlining of these routines.
22  static boolean doThrow = false;
23
24  public static void assertIntEquals(int expected, int result) {
25    if (expected != result) {
26      throw new Error("Expected: " + expected + ", found: " + result);
27    }
28  }
29
30  public static void assertLongEquals(long expected, long result) {
31    if (expected != result) {
32      throw new Error("Expected: " + expected + ", found: " + result);
33    }
34  }
35
36  public static void assertEquals(boolean expected, boolean result) {
37    if (expected != result) {
38      throw new Error("Expected: " + expected + ", found: " + result);
39    }
40  }
41
42  public static <T> T $noinline$runSmaliTest(String name, Class<T> klass, T input1, T input2) {
43    if (doThrow) { throw new Error(); }
44
45    Class<T> inputKlass = (Class<T>)input1.getClass();
46    try {
47      Class<?> c = Class.forName("SmaliTests");
48      Method m = c.getMethod(name, klass, klass);
49      return inputKlass.cast(m.invoke(null, input1, input2));
50    } catch (Exception ex) {
51      throw new Error(ex);
52    }
53  }
54
55  /**
56   * Test transformation of Not/Not/And into Or/Not.
57   */
58
59  // Note: before the instruction_simplifier pass, Xor's are used instead of
60  // Not's (the simplification happens during the same pass).
61  /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (before)
62  /// CHECK-DAG:       <<P1:i\d+>>          ParameterValue
63  /// CHECK-DAG:       <<P2:i\d+>>          ParameterValue
64  /// CHECK-DAG:       <<CstM1:i\d+>>       IntConstant -1
65  /// CHECK-DAG:       <<Not1:i\d+>>        Xor [<<P1>>,<<CstM1>>]
66  /// CHECK-DAG:       <<Not2:i\d+>>        Xor [<<P2>>,<<CstM1>>]
67  /// CHECK-DAG:       <<And:i\d+>>         And [<<Not1>>,<<Not2>>]
68  /// CHECK-DAG:                            Return [<<And>>]
69
70  /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
71  /// CHECK-DAG:       <<P1:i\d+>>          ParameterValue
72  /// CHECK-DAG:       <<P2:i\d+>>          ParameterValue
73  /// CHECK-DAG:       <<Or:i\d+>>          Or [<<P1>>,<<P2>>]
74  /// CHECK-DAG:       <<Not:i\d+>>         Not [<<Or>>]
75  /// CHECK-DAG:                            Return [<<Not>>]
76
77  /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
78  /// CHECK-DAG:                            Not
79  /// CHECK-NOT:                            Not
80
81  /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
82  /// CHECK-NOT:                            And
83
84  public static int $opt$noinline$andToOr(int a, int b) {
85    if (doThrow) throw new Error();
86    return ~a & ~b;
87  }
88
89  /**
90   * Test transformation of Not/Not/And into Or/Not for boolean negations.
91   * Note that the graph before this instruction simplification pass does not
92   * contain `HBooleanNot` instructions. This is because this transformation
93   * follows the optimization of `HSelect` to `HBooleanNot` occurring in the
94   * same pass.
95   */
96
97  /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_inlining (before)
98  /// CHECK-DAG:       <<P1:z\d+>>          ParameterValue
99  /// CHECK-DAG:       <<P2:z\d+>>          ParameterValue
100  /// CHECK-DAG:       <<Const0:i\d+>>      IntConstant 0
101  /// CHECK-DAG:       <<Const1:i\d+>>      IntConstant 1
102  /// CHECK-DAG:       <<Select1:i\d+>>     Select [<<Const1>>,<<Const0>>,<<P1>>]
103  /// CHECK-DAG:       <<Select2:i\d+>>     Select [<<Const1>>,<<Const0>>,<<P2>>]
104  /// CHECK-DAG:       <<And:i\d+>>         And [<<Select2>>,<<Select1>>]
105  /// CHECK-DAG:                            Return [<<And>>]
106
107  /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_inlining (after)
108  /// CHECK-DAG:       <<Cond1:z\d+>>       ParameterValue
109  /// CHECK-DAG:       <<Cond2:z\d+>>       ParameterValue
110  /// CHECK-DAG:       <<Or:i\d+>>          Or [<<Cond2>>,<<Cond1>>]
111  /// CHECK-DAG:       <<BooleanNot:z\d+>>  BooleanNot [<<Or>>]
112  /// CHECK-DAG:                            Return [<<BooleanNot>>]
113
114  /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_bce (after)
115  /// CHECK-DAG:                            BooleanNot
116  /// CHECK-NOT:                            BooleanNot
117
118  /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_bce (after)
119  /// CHECK-NOT:                            And
120
121  public static boolean $opt$noinline$booleanAndToOr(boolean a, boolean b) {
122    if (doThrow) throw new Error();
123    return !a & !b;
124  }
125
126  /**
127   * Test transformation of Not/Not/Or into And/Not.
128   */
129
130  // See note above.
131  // The second Xor has its arguments reversed for no obvious reason.
132  /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (before)
133  /// CHECK-DAG:       <<P1:j\d+>>          ParameterValue
134  /// CHECK-DAG:       <<P2:j\d+>>          ParameterValue
135  /// CHECK-DAG:       <<CstM1:j\d+>>       LongConstant -1
136  /// CHECK-DAG:       <<Not1:j\d+>>        Xor [<<P1>>,<<CstM1>>]
137  /// CHECK-DAG:       <<Not2:j\d+>>        Xor [<<CstM1>>,<<P2>>]
138  /// CHECK-DAG:       <<Or:j\d+>>          Or [<<Not1>>,<<Not2>>]
139  /// CHECK-DAG:                            Return [<<Or>>]
140
141  /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
142  /// CHECK-DAG:       <<P1:j\d+>>          ParameterValue
143  /// CHECK-DAG:       <<P2:j\d+>>          ParameterValue
144  /// CHECK-DAG:       <<And:j\d+>>         And [<<P1>>,<<P2>>]
145  /// CHECK-DAG:       <<Not:j\d+>>         Not [<<And>>]
146  /// CHECK-DAG:                            Return [<<Not>>]
147
148  /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
149  /// CHECK-DAG:                            Not
150  /// CHECK-NOT:                            Not
151
152  /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
153  /// CHECK-NOT:                            Or
154
155  public static long $opt$noinline$orToAnd(long a, long b) {
156    if (doThrow) throw new Error();
157    return ~a | ~b;
158  }
159
160  /**
161   * Test transformation of Not/Not/Or into Or/And for boolean negations.
162   * Note that the graph before this instruction simplification pass does not
163   * contain `HBooleanNot` instructions. This is because this transformation
164   * follows the optimization of `HSelect` to `HBooleanNot` occurring in the
165   * same pass.
166   */
167
168  /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_inlining (before)
169  /// CHECK-DAG:       <<P1:z\d+>>          ParameterValue
170  /// CHECK-DAG:       <<P2:z\d+>>          ParameterValue
171  /// CHECK-DAG:       <<Const0:i\d+>>      IntConstant 0
172  /// CHECK-DAG:       <<Const1:i\d+>>      IntConstant 1
173  /// CHECK-DAG:       <<Select1:i\d+>>     Select [<<Const1>>,<<Const0>>,<<P1>>]
174  /// CHECK-DAG:       <<Select2:i\d+>>     Select [<<Const1>>,<<Const0>>,<<P2>>]
175  /// CHECK-DAG:       <<Or:i\d+>>          Or [<<Select2>>,<<Select1>>]
176  /// CHECK-DAG:                            Return [<<Or>>]
177
178  /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_inlining (after)
179  /// CHECK-DAG:       <<Cond1:z\d+>>       ParameterValue
180  /// CHECK-DAG:       <<Cond2:z\d+>>       ParameterValue
181  /// CHECK-DAG:       <<And:i\d+>>         And [<<Cond2>>,<<Cond1>>]
182  /// CHECK-DAG:       <<BooleanNot:z\d+>>  BooleanNot [<<And>>]
183  /// CHECK-DAG:                            Return [<<BooleanNot>>]
184
185  /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_bce (after)
186  /// CHECK-DAG:                            BooleanNot
187  /// CHECK-NOT:                            BooleanNot
188
189  /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_bce (after)
190  /// CHECK-NOT:                            Or
191
192  public static boolean $opt$noinline$booleanOrToAnd(boolean a, boolean b) {
193    if (doThrow) throw new Error();
194    return !a | !b;
195  }
196
197  /**
198   * Test that the transformation copes with inputs being separated from the
199   * bitwise operations.
200   * This is a regression test. The initial logic was inserting the new bitwise
201   * operation incorrectly.
202   */
203
204  /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (before)
205  /// CHECK-DAG:       <<P1:i\d+>>          ParameterValue
206  /// CHECK-DAG:       <<P2:i\d+>>          ParameterValue
207  /// CHECK-DAG:       <<Cst1:i\d+>>        IntConstant 1
208  /// CHECK-DAG:       <<CstM1:i\d+>>       IntConstant -1
209  /// CHECK-DAG:       <<AddP1:i\d+>>       Add [<<P1>>,<<Cst1>>]
210  /// CHECK-DAG:       <<Not1:i\d+>>        Xor [<<AddP1>>,<<CstM1>>]
211  /// CHECK-DAG:       <<AddP2:i\d+>>       Add [<<P2>>,<<Cst1>>]
212  /// CHECK-DAG:       <<Not2:i\d+>>        Xor [<<AddP2>>,<<CstM1>>]
213  /// CHECK-DAG:       <<Or:i\d+>>          Or [<<Not1>>,<<Not2>>]
214  /// CHECK-DAG:                            Return [<<Or>>]
215
216  /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
217  /// CHECK-DAG:       <<P1:i\d+>>          ParameterValue
218  /// CHECK-DAG:       <<P2:i\d+>>          ParameterValue
219  /// CHECK-DAG:       <<Cst1:i\d+>>        IntConstant 1
220  /// CHECK-DAG:       <<AddP1:i\d+>>       Add [<<P1>>,<<Cst1>>]
221  /// CHECK-DAG:       <<AddP2:i\d+>>       Add [<<P2>>,<<Cst1>>]
222  /// CHECK-DAG:       <<And:i\d+>>         And [<<AddP1>>,<<AddP2>>]
223  /// CHECK-DAG:       <<Not:i\d+>>         Not [<<And>>]
224  /// CHECK-DAG:                            Return [<<Not>>]
225
226  /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
227  /// CHECK-DAG:                            Not
228  /// CHECK-NOT:                            Not
229
230  /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
231  /// CHECK-NOT:                            Or
232
233  public static int $opt$noinline$regressInputsAway(int a, int b) {
234    if (doThrow) throw new Error();
235    int a1 = a + 1;
236    int not_a1 = ~a1;
237    int b1 = b + 1;
238    int not_b1 = ~b1;
239    return not_a1 | not_b1;
240  }
241
242  /**
243   * Test transformation of Not/Not/Xor into Xor.
244   */
245
246  // See first note above.
247  /// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (before)
248  /// CHECK-DAG:       <<P1:i\d+>>          ParameterValue
249  /// CHECK-DAG:       <<P2:i\d+>>          ParameterValue
250  /// CHECK-DAG:       <<CstM1:i\d+>>       IntConstant -1
251  /// CHECK-DAG:       <<Not1:i\d+>>        Xor [<<P1>>,<<CstM1>>]
252  /// CHECK-DAG:       <<Not2:i\d+>>        Xor [<<P2>>,<<CstM1>>]
253  /// CHECK-DAG:       <<Xor:i\d+>>         Xor [<<Not1>>,<<Not2>>]
254  /// CHECK-DAG:                            Return [<<Xor>>]
255
256  /// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (after)
257  /// CHECK-DAG:       <<P1:i\d+>>          ParameterValue
258  /// CHECK-DAG:       <<P2:i\d+>>          ParameterValue
259  /// CHECK-DAG:       <<Xor:i\d+>>         Xor [<<P1>>,<<P2>>]
260  /// CHECK-DAG:                            Return [<<Xor>>]
261
262  /// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (after)
263  /// CHECK-NOT:                            Not
264
265  public static int $opt$noinline$notXorToXor(int a, int b) {
266    if (doThrow) throw new Error();
267    return ~a ^ ~b;
268  }
269
270  /**
271   * Test transformation of Not/Not/Xor into Xor for boolean negations.
272   * Note that the graph before this instruction simplification pass does not
273   * contain `HBooleanNot` instructions. This is because this transformation
274   * follows the optimization of `HSelect` to `HBooleanNot` occurring in the
275   * same pass.
276   */
277
278  /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_inlining (before)
279  /// CHECK-DAG:       <<P1:z\d+>>          ParameterValue
280  /// CHECK-DAG:       <<P2:z\d+>>          ParameterValue
281  /// CHECK-DAG:       <<Const0:i\d+>>      IntConstant 0
282  /// CHECK-DAG:       <<Const1:i\d+>>      IntConstant 1
283  /// CHECK-DAG:       <<Select1:i\d+>>     Select [<<Const1>>,<<Const0>>,<<P1>>]
284  /// CHECK-DAG:       <<Select2:i\d+>>     Select [<<Const1>>,<<Const0>>,<<P2>>]
285  /// CHECK-DAG:       <<Xor:i\d+>>         Xor [<<Select2>>,<<Select1>>]
286  /// CHECK-DAG:                            Return [<<Xor>>]
287
288  /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_inlining (after)
289  /// CHECK-DAG:       <<Cond1:z\d+>>       ParameterValue
290  /// CHECK-DAG:       <<Cond2:z\d+>>       ParameterValue
291  /// CHECK-DAG:       <<Xor:i\d+>>         Xor [<<Cond2>>,<<Cond1>>]
292  /// CHECK-DAG:                            Return [<<Xor>>]
293
294  /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_bce (after)
295  /// CHECK-NOT:                            BooleanNot
296
297  public static boolean $opt$noinline$booleanNotXorToXor(boolean a, boolean b) {
298    if (doThrow) throw new Error();
299    return !a ^ !b;
300  }
301
302  /**
303   * Check that no transformation is done when one Not has multiple uses.
304   */
305
306  /// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (before)
307  /// CHECK-DAG:       <<P1:i\d+>>          ParameterValue
308  /// CHECK-DAG:       <<P2:i\d+>>          ParameterValue
309  /// CHECK-DAG:       <<CstM1:i\d+>>       IntConstant -1
310  /// CHECK-DAG:       <<One:i\d+>>         IntConstant 1
311  /// CHECK-DAG:       <<Not2:i\d+>>        Xor [<<P2>>,<<CstM1>>]
312  /// CHECK-DAG:       <<And2:i\d+>>        And [<<Not2>>,<<One>>]
313  /// CHECK-DAG:       <<Not1:i\d+>>        Xor [<<P1>>,<<CstM1>>]
314  /// CHECK-DAG:       <<And1:i\d+>>        And [<<Not1>>,<<Not2>>]
315  /// CHECK-DAG:       <<Add:i\d+>>         Add [<<And2>>,<<And1>>]
316  /// CHECK-DAG:                            Return [<<Add>>]
317
318  /// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (after)
319  /// CHECK-DAG:       <<P1:i\d+>>          ParameterValue
320  /// CHECK-DAG:       <<P2:i\d+>>          ParameterValue
321  /// CHECK-DAG:       <<One:i\d+>>         IntConstant 1
322  /// CHECK-DAG:       <<Not2:i\d+>>        Not [<<P2>>]
323  /// CHECK-DAG:       <<And2:i\d+>>        And [<<Not2>>,<<One>>]
324  /// CHECK-DAG:       <<Not1:i\d+>>        Not [<<P1>>]
325  /// CHECK-DAG:       <<And1:i\d+>>        And [<<Not1>>,<<Not2>>]
326  /// CHECK-DAG:       <<Add:i\d+>>         Add [<<And2>>,<<And1>>]
327  /// CHECK-DAG:                            Return [<<Add>>]
328
329  /// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (after)
330  /// CHECK-NOT:                            Or
331
332  public static int $opt$noinline$notMultipleUses(int a, int b) {
333    if (doThrow) throw new Error();
334    int tmp = ~b;
335    return (tmp & 0x1) + (~a & tmp);
336  }
337
338  public static void main(String[] args) {
339    assertIntEquals(~0xff, $opt$noinline$andToOr(0xf, 0xff));
340    assertIntEquals(~0xff, $noinline$runSmaliTest("$opt$noinline$andToOr", int.class, 0xf, 0xff));
341    assertEquals(true, $opt$noinline$booleanAndToOr(false, false));
342    assertEquals(true, $noinline$runSmaliTest("$opt$noinline$booleanAndToOr", boolean.class, false, false));
343    assertLongEquals(~0xf, $opt$noinline$orToAnd(0xf, 0xff));
344    assertLongEquals(~0xf, $noinline$runSmaliTest("$opt$noinline$orToAnd", long.class, 0xfL, 0xffL));
345    assertEquals(false, $opt$noinline$booleanOrToAnd(true, true));
346    assertEquals(false, $noinline$runSmaliTest("$opt$noinline$booleanOrToAnd", boolean.class, true, true));
347    assertIntEquals(-1, $opt$noinline$regressInputsAway(0xf, 0xff));
348    assertIntEquals(-1, $noinline$runSmaliTest("$opt$noinline$regressInputsAway", int.class, 0xf, 0xff));
349    assertIntEquals(0xf0, $opt$noinline$notXorToXor(0xf, 0xff));
350    assertIntEquals(0xf0, $noinline$runSmaliTest("$opt$noinline$notXorToXor", int.class, 0xf, 0xff));
351    assertEquals(true, $opt$noinline$booleanNotXorToXor(true, false));
352    assertEquals(true, $noinline$runSmaliTest("$opt$noinline$booleanNotXorToXor", boolean.class, true, false));
353    assertIntEquals(~0xff, $opt$noinline$notMultipleUses(0xf, 0xff));
354    assertIntEquals(~0xff, $noinline$runSmaliTest("$opt$noinline$notMultipleUses", int.class, 0xf, 0xff));
355  }
356}
357