Home | History | Annotate | Download | only in src
      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 
     17 import java.lang.reflect.Method;
     18 
     19 public 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