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