1 /* 2 * Copyright (C) 2020 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 public class RemTest { expectEquals(int expected, int result)18 private static void expectEquals(int expected, int result) { 19 if (expected != result) { 20 throw new Error("Expected: " + expected + ", found: " + result); 21 } 22 } 23 expectEquals(long expected, long result)24 private static void expectEquals(long expected, long result) { 25 if (expected != result) { 26 throw new Error("Expected: " + expected + ", found: " + result); 27 } 28 } 29 main()30 public static void main() { 31 remInt(); 32 remLong(); 33 } 34 remInt()35 private static void remInt() { 36 expectEquals(0, $noinline$IntRemBy18(0)); 37 expectEquals(1, $noinline$IntRemBy18(1)); 38 expectEquals(-1, $noinline$IntRemBy18(-1)); 39 expectEquals(0, $noinline$IntRemBy18(18)); 40 expectEquals(0, $noinline$IntRemBy18(-18)); 41 expectEquals(11, $noinline$IntRemBy18(65)); 42 expectEquals(-11, $noinline$IntRemBy18(-65)); 43 44 expectEquals(0, $noinline$IntALenRemBy18(new int[0])); 45 expectEquals(1, $noinline$IntALenRemBy18(new int[1])); 46 expectEquals(0, $noinline$IntALenRemBy18(new int[18])); 47 expectEquals(11, $noinline$IntALenRemBy18(new int[65])); 48 49 expectEquals(0, $noinline$IntRemByMinus18(0)); 50 expectEquals(1, $noinline$IntRemByMinus18(1)); 51 expectEquals(-1, $noinline$IntRemByMinus18(-1)); 52 expectEquals(0, $noinline$IntRemByMinus18(18)); 53 expectEquals(0, $noinline$IntRemByMinus18(-18)); 54 expectEquals(11, $noinline$IntRemByMinus18(65)); 55 expectEquals(-11, $noinline$IntRemByMinus18(-65)); 56 57 expectEquals(0, $noinline$IntRemBy7(0)); 58 expectEquals(1, $noinline$IntRemBy7(1)); 59 expectEquals(-1, $noinline$IntRemBy7(-1)); 60 expectEquals(0, $noinline$IntRemBy7(7)); 61 expectEquals(0, $noinline$IntRemBy7(-7)); 62 expectEquals(1, $noinline$IntRemBy7(22)); 63 expectEquals(-1, $noinline$IntRemBy7(-22)); 64 65 expectEquals(0, $noinline$IntALenRemBy7(new int[0])); 66 expectEquals(1, $noinline$IntALenRemBy7(new int[1])); 67 expectEquals(0, $noinline$IntALenRemBy7(new int[7])); 68 expectEquals(1, $noinline$IntALenRemBy7(new int[22])); 69 70 expectEquals(0, $noinline$IntRemByMinus7(0)); 71 expectEquals(1, $noinline$IntRemByMinus7(1)); 72 expectEquals(-1, $noinline$IntRemByMinus7(-1)); 73 expectEquals(0, $noinline$IntRemByMinus7(7)); 74 expectEquals(0, $noinline$IntRemByMinus7(-7)); 75 expectEquals(1, $noinline$IntRemByMinus7(22)); 76 expectEquals(-1, $noinline$IntRemByMinus7(-22)); 77 78 expectEquals(0, $noinline$IntRemBy6(0)); 79 expectEquals(1, $noinline$IntRemBy6(1)); 80 expectEquals(-1, $noinline$IntRemBy6(-1)); 81 expectEquals(0, $noinline$IntRemBy6(6)); 82 expectEquals(0, $noinline$IntRemBy6(-6)); 83 expectEquals(1, $noinline$IntRemBy6(19)); 84 expectEquals(-1, $noinline$IntRemBy6(-19)); 85 86 expectEquals(0, $noinline$IntALenRemBy6(new int[0])); 87 expectEquals(1, $noinline$IntALenRemBy6(new int[1])); 88 expectEquals(0, $noinline$IntALenRemBy6(new int[6])); 89 expectEquals(1, $noinline$IntALenRemBy6(new int[19])); 90 91 expectEquals(0, $noinline$IntRemByMinus6(0)); 92 expectEquals(1, $noinline$IntRemByMinus6(1)); 93 expectEquals(-1, $noinline$IntRemByMinus6(-1)); 94 expectEquals(0, $noinline$IntRemByMinus6(6)); 95 expectEquals(0, $noinline$IntRemByMinus6(-6)); 96 expectEquals(1, $noinline$IntRemByMinus6(19)); 97 expectEquals(-1, $noinline$IntRemByMinus6(-19)); 98 } 99 100 // A test case to check that 'lsr' and 'asr' are combined into one 'asr'. 101 // For divisor 18 seen in an MP3 decoding workload there is no need 102 // to correct the result of get_high(dividend * magic). So there are no 103 // instructions between 'lsr' and 'asr'. In such a case they can be combined 104 // into one 'asr'. 105 // 106 /// CHECK-START-ARM64: int RemTest.$noinline$IntRemBy18(int) disassembly (after) 107 /// CHECK: asr x{{\d+}}, x{{\d+}}, #34 108 /// CHECK-NEXT: add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31 109 /// CHECK-NEXT: mov w{{\d+}}, #0x12 110 /// CHECK-NEXT: msub w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} $noinline$IntRemBy18(int v)111 private static int $noinline$IntRemBy18(int v) { 112 int r = v % 18; 113 return r; 114 } 115 116 // A test case to check that a correcting 'add' is not generated for a non-negative 117 // dividend and a positive divisor. 118 // 119 /// CHECK-START-ARM: int RemTest.$noinline$IntALenRemBy18(int[]) disassembly (after) 120 /// CHECK: smull r{{\d+}}, r{{\d+}}, r{{\d+}}, r{{\d+}} 121 /// CHECK-NEXT: lsr{{s?}} r{{\d+}}, #2 122 /// CHECK-NEXT: mov{{s?}} r{{\d+}}, #18 123 /// CHECK-NEXT: mls r{{\d+}}, r{{\d+}}, r{{\d+}}, r{{\d+}} 124 // 125 /// CHECK-START-ARM64: int RemTest.$noinline$IntALenRemBy18(int[]) disassembly (after) 126 /// CHECK: lsr x{{\d+}}, x{{\d+}}, #34 127 /// CHECK-NEXT: mov w{{\d+}}, #0x12 128 /// CHECK-NEXT: msub w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} $noinline$IntALenRemBy18(int[] arr)129 private static int $noinline$IntALenRemBy18(int[] arr) { 130 int r = arr.length % 18; 131 return r; 132 } 133 134 // A test case to check that 'lsr' and 'asr' are combined into one 'asr'. 135 // Divisor -18 has the same property as divisor 18: no need to correct the 136 // result of get_high(dividend * magic). So there are no 137 // instructions between 'lsr' and 'asr'. In such a case they can be combined 138 // into one 'asr'. 139 // 140 /// CHECK-START-ARM64: int RemTest.$noinline$IntRemByMinus18(int) disassembly (after) 141 /// CHECK: asr x{{\d+}}, x{{\d+}}, #34 142 /// CHECK-NEXT: add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31 143 /// CHECK-NEXT: mov w{{\d+}}, #0xffffffee 144 /// CHECK-NEXT: msub w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} $noinline$IntRemByMinus18(int v)145 private static int $noinline$IntRemByMinus18(int v) { 146 int r = v % -18; 147 return r; 148 } 149 150 // A test case to check that 'lsr' and 'add' are combined into one 'adds'. 151 // For divisor 7 seen in the core library the result of get_high(dividend * magic) 152 // must be corrected by the 'add' instruction. 153 // 154 // The test case also checks 'add' and 'add_shift' are optimized into 'adds' and 'cinc'. 155 // 156 /// CHECK-START-ARM64: int RemTest.$noinline$IntRemBy7(int) disassembly (after) 157 /// CHECK: adds x{{\d+}}, x{{\d+}}, x{{\d+}}, lsl #32 158 /// CHECK-NEXT: asr x{{\d+}}, x{{\d+}}, #34 159 /// CHECK-NEXT: cinc w{{\d+}}, w{{\d+}}, mi 160 /// CHECK-NEXT: mov w{{\d+}}, #0x7 161 /// CHECK-NEXT: msub w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} $noinline$IntRemBy7(int v)162 private static int $noinline$IntRemBy7(int v) { 163 int r = v % 7; 164 return r; 165 } 166 167 // A test case to check that a correcting 'add' is not generated for a non-negative 168 // dividend and a positive divisor. 169 // 170 /// CHECK-START-ARM: int RemTest.$noinline$IntALenRemBy7(int[]) disassembly (after) 171 /// CHECK: smull r{{\d+}}, r{{\d+}}, r{{\d+}}, r{{\d+}} 172 /// CHECK-NEXT: add{{s?}} r{{\d+}}, r{{\d+}} 173 /// CHECK-NEXT: lsr{{s?}} r{{\d+}}, #2 174 /// CHECK-NEXT: mov{{s?}} r{{\d+}}, #7 175 /// CHECK-NEXT: mls r{{\d+}}, r{{\d+}}, r{{\d+}}, r{{\d+}} 176 // 177 /// CHECK-START-ARM64: int RemTest.$noinline$IntALenRemBy7(int[]) disassembly (after) 178 /// CHECK: lsr x{{\d+}}, x{{\d+}}, #34 179 /// CHECK-NEXT: mov w{{\d+}}, #0x7 180 /// CHECK-NEXT: msub w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} $noinline$IntALenRemBy7(int[] arr)181 private static int $noinline$IntALenRemBy7(int[] arr) { 182 int r = arr.length % 7; 183 return r; 184 } 185 186 // A test case to check that 'lsr' and 'add' are combined into one 'adds'. 187 // Divisor -7 has the same property as divisor 7: the result of get_high(dividend * magic) 188 // must be corrected. In this case it is a 'sub' instruction. 189 // 190 // The test case also checks 'sub' and 'add_shift' are optimized into 'subs' and 'cinc'. 191 // 192 /// CHECK-START-ARM64: int RemTest.$noinline$IntRemByMinus7(int) disassembly (after) 193 /// CHECK: subs x{{\d+}}, x{{\d+}}, x{{\d+}}, lsl #32 194 /// CHECK-NEXT: asr x{{\d+}}, x{{\d+}}, #34 195 /// CHECK-NEXT: cinc w{{\d+}}, w{{\d+}}, mi 196 /// CHECK-NEXT: mov w{{\d+}}, #0xfffffff9 197 /// CHECK-NEXT: msub w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} $noinline$IntRemByMinus7(int v)198 private static int $noinline$IntRemByMinus7(int v) { 199 int r = v % -7; 200 return r; 201 } 202 203 // A test case to check that 'asr' is used to get the high 32 bits of the result of 204 // 'dividend * magic'. 205 // For divisor 6 seen in the core library there is no need to correct the result of 206 // get_high(dividend * magic). Also there is no 'asr' before the final 'add' instruction 207 // which uses only the high 32 bits of the result. In such a case 'asr' getting the high 208 // 32 bits can be used as well. 209 // 210 /// CHECK-START-ARM64: int RemTest.$noinline$IntRemBy6(int) disassembly (after) 211 /// CHECK: asr x{{\d+}}, x{{\d+}}, #32 212 /// CHECK-NEXT: add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31 213 /// CHECK-NEXT: mov w{{\d+}}, #0x6 214 /// CHECK-NEXT: msub w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} $noinline$IntRemBy6(int v)215 private static int $noinline$IntRemBy6(int v) { 216 int r = v % 6; 217 return r; 218 } 219 220 // A test case to check that a correcting 'add' is not generated for a non-negative 221 // dividend and a positive divisor. 222 // 223 /// CHECK-START-ARM: int RemTest.$noinline$IntALenRemBy6(int[]) disassembly (after) 224 /// CHECK: smull r{{\d+}}, r{{\d+}}, r{{\d+}}, r{{\d+}} 225 /// CHECK-NEXT: mov{{s?}} r{{\d+}}, #6 226 /// CHECK-NEXT: mls r{{\d+}}, r{{\d+}}, r{{\d+}}, r{{\d+}} 227 // 228 /// CHECK-START-ARM64: int RemTest.$noinline$IntALenRemBy6(int[]) disassembly (after) 229 /// CHECK: lsr x{{\d+}}, x{{\d+}}, #32 230 /// CHECK-NEXT: mov w{{\d+}}, #0x6 231 /// CHECK-NEXT: msub w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} $noinline$IntALenRemBy6(int[] arr)232 private static int $noinline$IntALenRemBy6(int[] arr) { 233 int r = arr.length % 6; 234 return r; 235 } 236 237 // A test case to check that 'asr' is used to get the high 32 bits of the result of 238 // 'dividend * magic'. 239 // Divisor -6 has the same property as divisor 6: no need to correct the result of 240 // get_high(dividend * magic) and no 'asr' before the final 'add' instruction 241 // which uses only the high 32 bits of the result. In such a case 'asr' getting the high 242 // 32 bits can be used as well. 243 // 244 /// CHECK-START-ARM64: int RemTest.$noinline$IntRemByMinus6(int) disassembly (after) 245 /// CHECK: asr x{{\d+}}, x{{\d+}}, #32 246 /// CHECK-NEXT: add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31 247 /// CHECK-NEXT: mov w{{\d+}}, #0xfffffffa 248 /// CHECK-NEXT: msub w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} $noinline$IntRemByMinus6(int v)249 private static int $noinline$IntRemByMinus6(int v) { 250 int r = v % -6; 251 return r; 252 } 253 remLong()254 private static void remLong() { 255 expectEquals(0L, $noinline$LongRemBy18(0L)); 256 expectEquals(1L, $noinline$LongRemBy18(1L)); 257 expectEquals(-1L, $noinline$LongRemBy18(-1L)); 258 expectEquals(0L, $noinline$LongRemBy18(18L)); 259 expectEquals(0L, $noinline$LongRemBy18(-18L)); 260 expectEquals(11L, $noinline$LongRemBy18(65L)); 261 expectEquals(-11L, $noinline$LongRemBy18(-65L)); 262 263 expectEquals(0L, $noinline$LongRemByMinus18(0L)); 264 expectEquals(1L, $noinline$LongRemByMinus18(1L)); 265 expectEquals(-1L, $noinline$LongRemByMinus18(-1L)); 266 expectEquals(0L, $noinline$LongRemByMinus18(18L)); 267 expectEquals(0L, $noinline$LongRemByMinus18(-18L)); 268 expectEquals(11L, $noinline$LongRemByMinus18(65L)); 269 expectEquals(-11L, $noinline$LongRemByMinus18(-65L)); 270 271 expectEquals(0L, $noinline$LongRemBy7(0L)); 272 expectEquals(1L, $noinline$LongRemBy7(1L)); 273 expectEquals(-1L, $noinline$LongRemBy7(-1L)); 274 expectEquals(0L, $noinline$LongRemBy7(7L)); 275 expectEquals(0L, $noinline$LongRemBy7(-7L)); 276 expectEquals(1L, $noinline$LongRemBy7(22L)); 277 expectEquals(-1L, $noinline$LongRemBy7(-22L)); 278 279 expectEquals(0L, $noinline$LongRemByMinus7(0L)); 280 expectEquals(1L, $noinline$LongRemByMinus7(1L)); 281 expectEquals(-1L, $noinline$LongRemByMinus7(-1L)); 282 expectEquals(0L, $noinline$LongRemByMinus7(7L)); 283 expectEquals(0L, $noinline$LongRemByMinus7(-7L)); 284 expectEquals(1L, $noinline$LongRemByMinus7(22L)); 285 expectEquals(-1L, $noinline$LongRemByMinus7(-22L)); 286 287 expectEquals(0L, $noinline$LongRemBy6(0L)); 288 expectEquals(1L, $noinline$LongRemBy6(1L)); 289 expectEquals(-1L, $noinline$LongRemBy6(-1L)); 290 expectEquals(0L, $noinline$LongRemBy6(6L)); 291 expectEquals(0L, $noinline$LongRemBy6(-6L)); 292 expectEquals(1L, $noinline$LongRemBy6(19L)); 293 expectEquals(-1L, $noinline$LongRemBy6(-19L)); 294 295 expectEquals(0L, $noinline$LongRemByMinus6(0L)); 296 expectEquals(1L, $noinline$LongRemByMinus6(1L)); 297 expectEquals(-1L, $noinline$LongRemByMinus6(-1L)); 298 expectEquals(0L, $noinline$LongRemByMinus6(6L)); 299 expectEquals(0L, $noinline$LongRemByMinus6(-6L)); 300 expectEquals(1L, $noinline$LongRemByMinus6(19L)); 301 expectEquals(-1L, $noinline$LongRemByMinus6(-19L)); 302 303 expectEquals(0L, $noinline$LongRemBy100(0L)); 304 expectEquals(1L, $noinline$LongRemBy100(1L)); 305 expectEquals(-1L, $noinline$LongRemBy100(-1L)); 306 expectEquals(0L, $noinline$LongRemBy100(100L)); 307 expectEquals(0L, $noinline$LongRemBy100(-100L)); 308 expectEquals(1L, $noinline$LongRemBy100(101L)); 309 expectEquals(-1L, $noinline$LongRemBy100(-101L)); 310 311 expectEquals(0L, $noinline$LongRemByMinus100(0L)); 312 expectEquals(1L, $noinline$LongRemByMinus100(1L)); 313 expectEquals(-1L, $noinline$LongRemByMinus100(-1L)); 314 expectEquals(0L, $noinline$LongRemByMinus100(100L)); 315 expectEquals(0L, $noinline$LongRemByMinus100(-100L)); 316 expectEquals(1L, $noinline$LongRemByMinus100(101L)); 317 expectEquals(-1L, $noinline$LongRemByMinus100(-101L)); 318 } 319 320 // Test cases for Int64 HDiv/HRem to check that optimizations implemented for Int32 are not 321 // used for Int64. The same divisors 18, -18, 7, -7, 6 and -6 are used. 322 323 /// CHECK-START-ARM64: long RemTest.$noinline$LongRemBy18(long) disassembly (after) 324 /// CHECK: smulh x{{\d+}}, x{{\d+}}, x{{\d+}} 325 /// CHECK-NEXT: add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63 326 /// CHECK-NEXT: mov x{{\d+}}, #0x12 327 /// CHECK-NEXT: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}} $noinline$LongRemBy18(long v)328 private static long $noinline$LongRemBy18(long v) { 329 long r = v % 18L; 330 return r; 331 } 332 333 /// CHECK-START-ARM64: long RemTest.$noinline$LongRemByMinus18(long) disassembly (after) 334 /// CHECK: smulh x{{\d+}}, x{{\d+}}, x{{\d+}} 335 /// CHECK-NEXT: add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63 336 /// CHECK-NEXT: mov x{{\d+}}, #0xffffffffffffffee 337 /// CHECK-NEXT: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}} $noinline$LongRemByMinus18(long v)338 private static long $noinline$LongRemByMinus18(long v) { 339 long r = v % -18L; 340 return r; 341 } 342 343 /// CHECK-START-ARM64: long RemTest.$noinline$LongRemBy7(long) disassembly (after) 344 /// CHECK: smulh x{{\d+}}, x{{\d+}}, x{{\d+}} 345 /// CHECK-NEXT: asr x{{\d+}}, x{{\d+}}, #1 346 /// CHECK-NEXT: add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63 347 /// CHECK-NEXT: mov x{{\d+}}, #0x7 348 /// CHECK-NEXT: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}} $noinline$LongRemBy7(long v)349 private static long $noinline$LongRemBy7(long v) { 350 long r = v % 7L; 351 return r; 352 } 353 354 /// CHECK-START-ARM64: long RemTest.$noinline$LongRemByMinus7(long) disassembly (after) 355 /// CHECK: smulh x{{\d+}}, x{{\d+}}, x{{\d+}} 356 /// CHECK-NEXT: asr x{{\d+}}, x{{\d+}}, #1 357 /// CHECK-NEXT: add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63 358 /// CHECK-NEXT: mov x{{\d+}}, #0xfffffffffffffff9 359 /// CHECK-NEXT: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}} $noinline$LongRemByMinus7(long v)360 private static long $noinline$LongRemByMinus7(long v) { 361 long r = v % -7L; 362 return r; 363 } 364 365 /// CHECK-START-ARM64: long RemTest.$noinline$LongRemBy6(long) disassembly (after) 366 /// CHECK: smulh x{{\d+}}, x{{\d+}}, x{{\d+}} 367 /// CHECK-NEXT: add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63 368 /// CHECK-NEXT: mov x{{\d+}}, #0x6 369 /// CHECK-NEXT: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}} $noinline$LongRemBy6(long v)370 private static long $noinline$LongRemBy6(long v) { 371 long r = v % 6L; 372 return r; 373 } 374 375 /// CHECK-START-ARM64: long RemTest.$noinline$LongRemByMinus6(long) disassembly (after) 376 /// CHECK: smulh x{{\d+}}, x{{\d+}}, x{{\d+}} 377 /// CHECK-NEXT: add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63 378 /// CHECK-NEXT: mov x{{\d+}}, #0xfffffffffffffffa 379 /// CHECK-NEXT: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}} $noinline$LongRemByMinus6(long v)380 private static long $noinline$LongRemByMinus6(long v) { 381 long r = v % -6L; 382 return r; 383 } 384 385 // A test to check 'add' and 'add_shift' are optimized into 'adds' and 'cinc'. 386 // 387 /// CHECK-START-ARM64: long RemTest.$noinline$LongRemBy100(long) disassembly (after) 388 /// CHECK: smulh x{{\d+}}, x{{\d+}}, x{{\d+}} 389 /// CHECK-NEXT: adds x{{\d+}}, x{{\d+}}, x{{\d+}} 390 /// CHECK-NEXT: asr x{{\d+}}, x{{\d+}}, #6 391 /// CHECK-NEXT: cinc x{{\d+}}, x{{\d+}}, mi 392 /// CHECK-NEXT: mov x{{\d+}}, #0x64 393 /// CHECK-NEXT: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}} $noinline$LongRemBy100(long v)394 private static long $noinline$LongRemBy100(long v) { 395 long r = v % 100L; 396 return r; 397 } 398 399 // A test to check 'sub' and 'add_shift' are optimized into 'subs' and 'cinc'. 400 // 401 /// CHECK-START-ARM64: long RemTest.$noinline$LongRemByMinus100(long) disassembly (after) 402 /// CHECK: smulh x{{\d+}}, x{{\d+}}, x{{\d+}} 403 /// CHECK-NEXT: subs x{{\d+}}, x{{\d+}}, x{{\d+}} 404 /// CHECK-NEXT: asr x{{\d+}}, x{{\d+}}, #6 405 /// CHECK-NEXT: cinc x{{\d+}}, x{{\d+}}, mi 406 /// CHECK-NEXT: mov x{{\d+}}, #0xffffffffffffff9c 407 /// CHECK-NEXT: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}} $noinline$LongRemByMinus100(long v)408 private static long $noinline$LongRemByMinus100(long v) { 409 long r = v % -100L; 410 return r; 411 } 412 } 413