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 assertIntEquals(int expected, int result)24 public static void assertIntEquals(int expected, int result) { 25 if (expected != result) { 26 throw new Error("Expected: " + expected + ", found: " + result); 27 } 28 } 29 assertLongEquals(long expected, long result)30 public static void assertLongEquals(long expected, long result) { 31 if (expected != result) { 32 throw new Error("Expected: " + expected + ", found: " + result); 33 } 34 } 35 assertEquals(boolean expected, boolean result)36 public static void assertEquals(boolean expected, boolean result) { 37 if (expected != result) { 38 throw new Error("Expected: " + expected + ", found: " + result); 39 } 40 } 41 $noinline$runSmaliTest(String name, Class<T> klass, T input1, T input2)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 $opt$noinline$andToOr(int a, int b)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 $opt$noinline$booleanAndToOr(boolean a, boolean b)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 $opt$noinline$orToAnd(long a, long b)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 $opt$noinline$booleanOrToAnd(boolean a, boolean b)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 $opt$noinline$regressInputsAway(int a, int b)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 $opt$noinline$notXorToXor(int a, int b)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 $opt$noinline$booleanNotXorToXor(boolean a, boolean b)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 $opt$noinline$notMultipleUses(int a, int b)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 main(String[] args)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