| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| public class Main { |
| private static void expectEquals(long expected, long result) { |
| if (expected != result) { |
| throw new Error("Expected: " + expected + ", found: " + result); |
| } |
| } |
| |
| private static void expectEquals(long[] div_rem_expected, long[] result) { |
| expectEquals(div_rem_expected[0], result[0]); |
| expectEquals(div_rem_expected[1], result[1]); |
| } |
| |
| private static void remInt() { |
| expectEquals(1L, $noinline$IntDivRemBy18(1)); |
| expectEquals(1L << 32 | 2L, $noinline$IntDivRemBy18(20)); |
| |
| expectEquals(1L, $noinline$IntRemDivBy18(1)); |
| expectEquals(1L << 32 | 2L, $noinline$IntRemDivBy18(20)); |
| |
| expectEquals(1L, $noinline$IntDivRemBy18(1, false)); |
| expectEquals(1L << 32 | 2L, $noinline$IntDivRemBy18(20, true)); |
| |
| expectEquals(1L, $noinline$IntDivRemByMinus18(1)); |
| expectEquals(-1L, $noinline$IntDivRemBy18(-1)); |
| expectEquals((-1L << 32) | 2L, $noinline$IntDivRemByMinus18(20)); |
| expectEquals((1L << 32) | (-2L & 0x00000000ffffffff), $noinline$IntDivRemByMinus18(-20)); |
| |
| expectEquals(0L, $noinline$IntDivRemBy5(0)); |
| expectEquals(1L, $noinline$IntDivRemBy5(1)); |
| expectEquals(1L << 32, $noinline$IntDivRemBy5(5)); |
| expectEquals((1L << 32) | 1L, $noinline$IntDivRemBy5(6)); |
| expectEquals((-1L << 32) | 0x00000000ffffffff, $noinline$IntDivRemBy5(-6)); |
| expectEquals(-1L << 32, $noinline$IntDivRemBy5(-5)); |
| expectEquals(0x00000000ffffffff, $noinline$IntDivRemBy5(-1)); |
| |
| expectEquals(0L, $noinline$IntDivRemByMinus5(0)); |
| expectEquals(1L, $noinline$IntDivRemByMinus5(1)); |
| expectEquals(-1L << 32, $noinline$IntDivRemByMinus5(5)); |
| expectEquals((-1L << 32) | 1L, $noinline$IntDivRemByMinus5(6)); |
| expectEquals((1L << 32) | 0x00000000ffffffff, $noinline$IntDivRemByMinus5(-6)); |
| expectEquals(1L << 32, $noinline$IntDivRemByMinus5(-5)); |
| expectEquals(0x00000000ffffffff, $noinline$IntDivRemByMinus5(-1)); |
| |
| expectEquals(0L, $noinline$IntDivRemBy7(0)); |
| expectEquals(1L, $noinline$IntDivRemBy7(1)); |
| expectEquals(1L << 32, $noinline$IntDivRemBy7(7)); |
| expectEquals((1L << 32) | 1L, $noinline$IntDivRemBy7(8)); |
| expectEquals((-1L << 32) | 0x00000000ffffffff, $noinline$IntDivRemBy7(-8)); |
| expectEquals(-1L << 32, $noinline$IntDivRemBy7(-7)); |
| expectEquals(0x00000000ffffffff, $noinline$IntDivRemBy7(-1)); |
| |
| expectEquals(0L, $noinline$IntDivRemByMinus7(0)); |
| expectEquals(1L, $noinline$IntDivRemByMinus7(1)); |
| expectEquals(-1L << 32, $noinline$IntDivRemByMinus7(7)); |
| expectEquals((-1L << 32) | 1L, $noinline$IntDivRemByMinus7(8)); |
| expectEquals((1L << 32) | 0x00000000ffffffff, $noinline$IntDivRemByMinus7(-8)); |
| expectEquals(1L << 32, $noinline$IntDivRemByMinus7(-7)); |
| expectEquals(0x00000000ffffffff, $noinline$IntDivRemByMinus7(-1)); |
| |
| expectEquals(0L, $noinline$IntDivRemByMaxInt(0)); |
| expectEquals(1L, $noinline$IntDivRemByMaxInt(1)); |
| expectEquals(1L << 32, $noinline$IntDivRemByMaxInt(Integer.MAX_VALUE)); |
| expectEquals(Integer.MAX_VALUE - 1, $noinline$IntDivRemByMaxInt(Integer.MAX_VALUE - 1)); |
| expectEquals((-1L << 32) | 0x00000000ffffffff, $noinline$IntDivRemByMaxInt(Integer.MIN_VALUE)); |
| expectEquals(0x00000000ffffffff, $noinline$IntDivRemByMaxInt(-1)); |
| } |
| |
| // A test case to check: |
| // If there is HDiv with the same inputs as HRem, it is reused. |
| // |
| /// CHECK-START: long Main.$noinline$IntDivRemBy18(int) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK: Rem |
| // |
| /// CHECK-START: long Main.$noinline$IntDivRemBy18(int) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Mul |
| /// CHECK-NEXT: Sub |
| // |
| /// CHECK-START-ARM64: long Main.$noinline$IntDivRemBy18(int) disassembly (after) |
| /// CHECK: asr x{{\d+}}, x{{\d+}}, #34 |
| /// CHECK-NEXT: add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31 |
| /// CHECK: msub w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} |
| private static long $noinline$IntDivRemBy18(int v) { |
| int q = v / 18; |
| int r = v % 18; |
| return ((long)q << 32) | r; |
| } |
| |
| // A test case to check: |
| // If there is HDiv with the same inputs as HRem, it is reused. |
| // |
| /// CHECK-START: long Main.$noinline$IntDivRemByMinus18(int) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK: Rem |
| // |
| /// CHECK-START: long Main.$noinline$IntDivRemByMinus18(int) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Mul |
| /// CHECK-NEXT: Sub |
| // |
| /// CHECK-START-ARM64: long Main.$noinline$IntDivRemByMinus18(int) disassembly (after) |
| /// CHECK: asr x{{\d+}}, x{{\d+}}, #34 |
| /// CHECK-NEXT: add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31 |
| /// CHECK: msub w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} |
| private static long $noinline$IntDivRemByMinus18(int v) { |
| int q = v / -18; |
| int r = v % -18; |
| return ((long)q << 32) | r; |
| } |
| |
| // A test case to check: |
| // If there is HDiv with the same inputs as HRem, it is reused. |
| // |
| /// CHECK-START: long Main.$noinline$IntRemDivBy18(int) instruction_simplifier (before) |
| /// CHECK: Rem |
| /// CHECK: Div |
| // |
| /// CHECK-START: long Main.$noinline$IntRemDivBy18(int) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Mul |
| /// CHECK-NEXT: Sub |
| // |
| /// CHECK-START-ARM64: long Main.$noinline$IntRemDivBy18(int) disassembly (after) |
| /// CHECK: asr x{{\d+}}, x{{\d+}}, #34 |
| /// CHECK-NEXT: add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31 |
| /// CHECK: msub w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} |
| private static long $noinline$IntRemDivBy18(int v) { |
| int r = v % 18; |
| int q = v / 18; |
| return ((long)q << 32) | r; |
| } |
| |
| // A test case to check: |
| // If there is HDiv with the same inputs as HRem, it is reused. |
| // |
| /// CHECK-START: long Main.$noinline$IntDivRemBy5(int) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK: Rem |
| // |
| /// CHECK-START: long Main.$noinline$IntDivRemBy5(int) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Shl |
| /// CHECK-NEXT: Add |
| /// CHECK-NEXT: Sub |
| // |
| /// CHECK-START-ARM64: long Main.$noinline$IntDivRemBy5(int) disassembly (after) |
| /// CHECK: asr x{{\d+}}, x{{\d+}}, #33 |
| /// CHECK-NEXT: add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31 |
| /// CHECK: add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsl #2 |
| /// CHECK: sub w{{\d+}}, w{{\d+}}, w{{\d+}} |
| private static long $noinline$IntDivRemBy5(int v) { |
| int q = v / 5; |
| int r = v % 5; |
| return ((long)q << 32) | r; |
| } |
| |
| // A test case to check: |
| // If there is HDiv with the same inputs as HRem, it is reused. |
| // |
| /// CHECK-START: long Main.$noinline$IntDivRemByMinus5(int) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK: Rem |
| // |
| /// CHECK-START: long Main.$noinline$IntDivRemByMinus5(int) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Mul |
| /// CHECK-NEXT: Sub |
| // |
| /// CHECK-START-ARM64: long Main.$noinline$IntDivRemByMinus5(int) disassembly (after) |
| /// CHECK: asr x{{\d+}}, x{{\d+}}, #33 |
| /// CHECK-NEXT: add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31 |
| /// CHECK: msub w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} |
| private static long $noinline$IntDivRemByMinus5(int v) { |
| int q = v / -5; |
| int r = v % -5; |
| return ((long)q << 32) | r; |
| } |
| |
| // A test case to check: |
| // If there is HDiv with the same inputs as HRem, it is reused. |
| // |
| /// CHECK-START: long Main.$noinline$IntDivRemBy7(int) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK: Rem |
| // |
| /// CHECK-START: long Main.$noinline$IntDivRemBy7(int) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Shl |
| /// CHECK-NEXT: Sub |
| /// CHECK-NEXT: Sub |
| // |
| /// CHECK-START-ARM64: long Main.$noinline$IntDivRemBy7(int) instruction_simplifier_arm64 (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: DataProcWithShifterOp |
| /// CHECK-NEXT: Add |
| // |
| /// CHECK-START-ARM64: long Main.$noinline$IntDivRemBy7(int) disassembly (after) |
| /// CHECK: asr x{{\d+}}, x{{\d+}}, #34 |
| /// CHECK-NEXT: cinc w{{\d+}}, w{{\d+}}, mi |
| /// CHECK: sub w{{\d+}}, w{{\d+}}, w{{\d+}}, lsl #3 |
| /// CHECK: add w{{\d+}}, w{{\d+}}, w{{\d+}} |
| // |
| /// CHECK-START-ARM: long Main.$noinline$IntDivRemBy7(int) instruction_simplifier_arm (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: DataProcWithShifterOp |
| /// CHECK-NEXT: Add |
| // |
| /// CHECK-START-ARM: long Main.$noinline$IntDivRemBy7(int) disassembly (after) |
| /// CHECK: asr{{s?}} r{{\d+}}, #2 |
| /// CHECK-NEXT: sub r{{\d+}}, r{{\d+}}, r{{\d+}}, asr #31 |
| /// CHECK: sub r{{\d+}}, r{{\d+}}, r{{\d+}}, lsl #3 |
| /// CHECK: add{{s?}} r{{\d+}}, r{{\d+}}, r{{\d+}} |
| private static long $noinline$IntDivRemBy7(int v) { |
| int q = v / 7; |
| int r = v % 7; |
| return ((long)q << 32) | r; |
| } |
| |
| // A test case to check: |
| // If there is HDiv with the same inputs as HRem, it is reused. |
| // |
| /// CHECK-START: long Main.$noinline$IntDivRemByMinus7(int) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK: Rem |
| // |
| /// CHECK-START: long Main.$noinline$IntDivRemByMinus7(int) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Mul |
| /// CHECK-NEXT: Sub |
| // |
| /// CHECK-START-ARM64: long Main.$noinline$IntDivRemByMinus7(int) disassembly (after) |
| /// CHECK: asr x{{\d+}}, x{{\d+}}, #34 |
| /// CHECK-NEXT: cinc w{{\d+}}, w{{\d+}}, mi |
| /// CHECK: msub w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} |
| private static long $noinline$IntDivRemByMinus7(int v) { |
| int q = v / -7; |
| int r = v % -7; |
| return ((long)q << 32) | r; |
| } |
| |
| // A test case to check: |
| // If there is HDiv with the same inputs as HRem, it is reused. |
| // |
| /// CHECK-START: long Main.$noinline$IntDivRemByMaxInt(int) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK-NEXT: Rem |
| // |
| /// CHECK-START: long Main.$noinline$IntDivRemByMaxInt(int) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Shl |
| /// CHECK-NEXT: Sub |
| /// CHECK-NEXT: Sub |
| // |
| /// CHECK-START-ARM64: long Main.$noinline$IntDivRemByMaxInt(int) instruction_simplifier_arm64 (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: DataProcWithShifterOp |
| /// CHECK-NEXT: Add |
| // |
| /// CHECK-START-ARM64: long Main.$noinline$IntDivRemByMaxInt(int) disassembly (after) |
| /// CHECK: asr x{{\d+}}, x{{\d+}}, #61 |
| /// CHECK-NEXT: add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31 |
| /// CHECK: sub w{{\d+}}, w{{\d+}}, w{{\d+}}, lsl #31 |
| /// CHECK: add w{{\d+}}, w{{\d+}}, w{{\d+}} |
| // |
| /// CHECK-START-ARM: long Main.$noinline$IntDivRemByMaxInt(int) instruction_simplifier_arm (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: DataProcWithShifterOp |
| /// CHECK-NEXT: Add |
| // |
| /// CHECK-START-ARM: long Main.$noinline$IntDivRemByMaxInt(int) disassembly (after) |
| /// CHECK: asr{{s?}} r{{\d+}}, #29 |
| /// CHECK-NEXT: sub r{{\d+}}, r{{\d+}}, r{{\d+}}, asr #31 |
| /// CHECK: sub r{{\d+}}, r{{\d+}}, r{{\d+}}, lsl #31 |
| /// CHECK: add{{s?}} r{{\d+}}, r{{\d+}}, r{{\d+}} |
| private static long $noinline$IntDivRemByMaxInt(int v) { |
| int q = v / Integer.MAX_VALUE; |
| int r = v % Integer.MAX_VALUE; |
| return ((long)q << 32) | r; |
| } |
| |
| // A test case to check: |
| // HDiv with the same inputs as HRem but in another basic block is not reused. |
| // |
| /// CHECK-START: long Main.$noinline$IntDivRemBy18(int, boolean) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK: Rem |
| // |
| /// CHECK-START: long Main.$noinline$IntDivRemBy18(int, boolean) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK: Rem |
| private static long $noinline$IntDivRemBy18(int v, boolean do_division) { |
| long result = 0; |
| if (do_division) { |
| int q = v / 18; |
| result = (long)q << 32; |
| } |
| int r = v % 18; |
| return result | r; |
| } |
| |
| // A test case to check: |
| // If there is HDiv with the same inputs as HRem, it is reused. |
| // |
| /// CHECK-START: long Main.$noinline$IntDivRem(int, int) instruction_simplifier$after_gvn (before) |
| /// CHECK: Div |
| /// CHECK-NEXT: Rem |
| // |
| /// CHECK-START: long Main.$noinline$IntDivRem(int, int) instruction_simplifier$after_gvn (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Mul |
| /// CHECK-NEXT: Sub |
| private static long $noinline$IntDivRem(int v, int s) { |
| int q = v / s; |
| int r = v % s; |
| return ((long)q << 32) | r; |
| } |
| |
| // A test case to check: |
| // If there is HDiv with the same inputs as HRem, it is reused. |
| // |
| /// CHECK-START: long Main.$noinline$IntRemDiv(int, int) instruction_simplifier$after_gvn (before) |
| /// CHECK: Rem |
| /// CHECK-NEXT: Div |
| // |
| /// CHECK-START: long Main.$noinline$IntRemDiv(int, int) instruction_simplifier$after_gvn (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Mul |
| /// CHECK-NEXT: Sub |
| private static long $noinline$IntRemDiv(int v, int s) { |
| int r = v % s; |
| int q = v / s; |
| return ((long)q << 32) | r; |
| } |
| |
| // A test case to check: |
| // HDiv with the same inputs as HRem but in another basic block is not reused. |
| // |
| /// CHECK-START: long Main.$noinline$IntDivRem(int, int, boolean) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK: Rem |
| // |
| /// CHECK-START: long Main.$noinline$IntDivRem(int, int, boolean) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK: Rem |
| private static long $noinline$IntDivRem(int v, int s, boolean do_division) { |
| long result = 0; |
| if (do_division) { |
| int q = v / s; |
| result = (long)q << 32; |
| } |
| int r = v % s; |
| return result | r; |
| } |
| |
| // A test case to check: |
| // If HRem is in a loop, the instruction simplifier postpones its optimization till |
| // loop analysis/optimizations are done. |
| // |
| /// CHECK-START: int Main.$noinline$IntRemBy18InLoop(int) instruction_simplifier (before) |
| /// CHECK: Div loop:B{{\d+}} |
| /// CHECK-NEXT: Rem loop:B{{\d+}} |
| // |
| /// CHECK-START: int Main.$noinline$IntRemBy18InLoop(int) instruction_simplifier (after) |
| /// CHECK: Div loop:B{{\d+}} |
| /// CHECK-NEXT: Rem loop:B{{\d+}} |
| // |
| /// CHECK-START: int Main.$noinline$IntRemBy18InLoop(int) instruction_simplifier$after_loop_opt (before) |
| /// CHECK: Div loop:B{{\d+}} |
| /// CHECK-NEXT: Rem loop:B{{\d+}} |
| // |
| /// CHECK-START: int Main.$noinline$IntRemBy18InLoop(int) instruction_simplifier$after_loop_opt (after) |
| /// CHECK-NOT: Rem |
| /// CHECK: Div loop:B{{\d+}} |
| /// CHECK-NEXT: Mul loop:B{{\d+}} |
| /// CHECK-NEXT: Sub loop:B{{\d+}} |
| private static int $noinline$IntRemBy18InLoop(int v) { |
| int[] values = new int[v]; |
| for (int i = 0; i < values.length; ++i) { |
| int q = i / 18; |
| int r = i % 18; |
| values[i] = q + r; |
| } |
| return values[v - 1]; |
| } |
| |
| // A test case to check: |
| // FP type HRem is not optimized by the instruction simplifier. |
| // |
| /// CHECK-START: float Main.$noinline$FloatRemBy18(float) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK-NEXT: Rem |
| // |
| /// CHECK-START: float Main.$noinline$FloatRemBy18(float) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Rem |
| private static float $noinline$FloatRemBy18(float v) { |
| float q = v / 18.0f; |
| float r = v % 18.0f; |
| return q + r; |
| } |
| |
| // A test case to check: |
| // FP type HRem is not optimized by the instruction simplifier. |
| // |
| /// CHECK-START: double Main.$noinline$DoubleRemBy18(double) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK-NEXT: Rem |
| // |
| /// CHECK-START: double Main.$noinline$DoubleRemBy18(double) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Rem |
| private static double $noinline$DoubleRemBy18(double v) { |
| double q = v / 18.0; |
| double r = v % 18.0; |
| return q + r; |
| } |
| |
| // A test case to check: |
| // HRem with a divisor of power 2 is not optimized by the instruction simplifier because |
| // the case is optimized by the code generator. |
| // |
| /// CHECK-START: int Main.$noinline$IntRemByIntMin(int) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK-NEXT: Rem |
| // |
| /// CHECK-START: int Main.$noinline$IntRemByIntMin(int) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Rem |
| private static int $noinline$IntRemByIntMin(int v) { |
| int q = v / Integer.MIN_VALUE; |
| int r = v % Integer.MIN_VALUE; |
| return q + r; |
| } |
| |
| private static void remLong() { |
| expectEquals(1L, $noinline$LongDivRemBy18(1L)); |
| expectEquals(1L << 32 | 2L, $noinline$LongDivRemBy18(20L)); |
| |
| expectEquals(1L, $noinline$LongRemDivBy18(1L)); |
| expectEquals(1L << 32 | 2L, $noinline$LongRemDivBy18(20L)); |
| |
| expectEquals(1L, $noinline$LongDivRemBy18(1L, false)); |
| expectEquals(1L << 32 | 2L, $noinline$LongDivRemBy18(20L, true)); |
| |
| expectEquals(new long[] {0L, 1L}, $noinline$LongDivRemByMinus18(1)); |
| expectEquals(new long[] {0L, -1L}, $noinline$LongDivRemByMinus18(-1)); |
| expectEquals(new long[] {-1L, 2L}, $noinline$LongDivRemByMinus18(20)); |
| expectEquals(new long[] {1L, -2L}, $noinline$LongDivRemByMinus18(-20)); |
| |
| expectEquals(new long[] {0L, 0L}, $noinline$LongDivRemBy5(0)); |
| expectEquals(new long[] {0L, 1L}, $noinline$LongDivRemBy5(1)); |
| expectEquals(new long[] {1L, 0L}, $noinline$LongDivRemBy5(5)); |
| expectEquals(new long[] {1L, 1L}, $noinline$LongDivRemBy5(6)); |
| expectEquals(new long[] {-1L, -1L}, $noinline$LongDivRemBy5(-6)); |
| expectEquals(new long[] {-1L, 0L}, $noinline$LongDivRemBy5(-5)); |
| expectEquals(new long[] {0L, -1L}, $noinline$LongDivRemBy5(-1)); |
| |
| expectEquals(new long[] {0L, 0L}, $noinline$LongDivRemByMinus5(0)); |
| expectEquals(new long[] {0L, 1L}, $noinline$LongDivRemByMinus5(1)); |
| expectEquals(new long[] {-1L, 0L}, $noinline$LongDivRemByMinus5(5)); |
| expectEquals(new long[] {-1L, 1L}, $noinline$LongDivRemByMinus5(6)); |
| expectEquals(new long[] {1L, -1L}, $noinline$LongDivRemByMinus5(-6)); |
| expectEquals(new long[] {1L, 0L}, $noinline$LongDivRemByMinus5(-5)); |
| expectEquals(new long[] {0L, -1L}, $noinline$LongDivRemByMinus5(-1)); |
| |
| expectEquals(new long[] {0L, 0L}, $noinline$LongDivRemBy7(0)); |
| expectEquals(new long[] {0L, 1L}, $noinline$LongDivRemBy7(1)); |
| expectEquals(new long[] {1L, 0L}, $noinline$LongDivRemBy7(7)); |
| expectEquals(new long[] {1L, 1L}, $noinline$LongDivRemBy7(8)); |
| expectEquals(new long[] {-1L, -1L}, $noinline$LongDivRemBy7(-8)); |
| expectEquals(new long[] {-1L, 0L}, $noinline$LongDivRemBy7(-7)); |
| expectEquals(new long[] {0L, -1L}, $noinline$LongDivRemBy7(-1)); |
| |
| expectEquals(new long[] {0L, 0L}, $noinline$LongDivRemByMinus7(0)); |
| expectEquals(new long[] {0L, 1L}, $noinline$LongDivRemByMinus7(1)); |
| expectEquals(new long[] {-1L, 0L}, $noinline$LongDivRemByMinus7(7)); |
| expectEquals(new long[] {-1L, 1L}, $noinline$LongDivRemByMinus7(8)); |
| expectEquals(new long[] {1L, -1L}, $noinline$LongDivRemByMinus7(-8)); |
| expectEquals(new long[] {1L, 0L}, $noinline$LongDivRemByMinus7(-7)); |
| expectEquals(new long[] {0L, -1L}, $noinline$LongDivRemByMinus7(-1)); |
| |
| expectEquals(new long[] {0L, 0L}, $noinline$LongDivRemByMaxLong(0)); |
| expectEquals(new long[] {0L, 1L}, $noinline$LongDivRemByMaxLong(1)); |
| expectEquals(new long[] {1L, 0L}, $noinline$LongDivRemByMaxLong(Long.MAX_VALUE)); |
| expectEquals(new long[] {0L, Long.MAX_VALUE - 1}, |
| $noinline$LongDivRemByMaxLong(Long.MAX_VALUE - 1)); |
| expectEquals(new long[] {-1L, -1L}, $noinline$LongDivRemByMaxLong(Long.MIN_VALUE)); |
| expectEquals(new long[] {0L, -1L}, $noinline$LongDivRemByMaxLong(-1)); |
| } |
| |
| // A test case to check: |
| // If there is HDiv with the same inputs as HRem, it is reused. |
| // |
| /// CHECK-START: long Main.$noinline$LongDivRemBy18(long) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK: Rem |
| // |
| /// CHECK-START: long Main.$noinline$LongDivRemBy18(long) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Mul |
| /// CHECK-NEXT: Sub |
| private static long $noinline$LongDivRemBy18(long v) { |
| long q = v / 18L; |
| long r = v % 18L; |
| return (q << 32) | r; |
| } |
| |
| // A test case to check: |
| // If there is HDiv with the same inputs as HRem, it is reused. |
| // |
| /// CHECK-START: long[] Main.$noinline$LongDivRemByMinus18(long) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK: Rem |
| // |
| /// CHECK-START: long[] Main.$noinline$LongDivRemByMinus18(long) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Mul |
| /// CHECK-NEXT: Sub |
| // |
| /// CHECK-START-ARM64: long[] Main.$noinline$LongDivRemByMinus18(long) disassembly (after) |
| /// CHECK: smulh x{{\d+}}, x{{\d+}}, x{{\d+}} |
| /// CHECK-NEXT: add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63 |
| /// CHECK: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}} |
| private static long[] $noinline$LongDivRemByMinus18(long v) { |
| long q = v / -18L; |
| long r = v % -18L; |
| return new long[] {q, r}; |
| } |
| |
| // A test case to check: |
| // If there is HDiv with the same inputs as HRem, it is reused. |
| // |
| /// CHECK-START: long Main.$noinline$LongRemDivBy18(long) instruction_simplifier (before) |
| /// CHECK: Rem |
| /// CHECK: Div |
| // |
| /// CHECK-START: long Main.$noinline$LongRemDivBy18(long) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Mul |
| /// CHECK-NEXT: Sub |
| private static long $noinline$LongRemDivBy18(long v) { |
| long r = v % 18L; |
| long q = v / 18L; |
| return (q << 32) | r; |
| } |
| |
| // A test case to check: |
| // If there is HDiv with the same inputs as HRem, it is reused. |
| // |
| /// CHECK-START: long[] Main.$noinline$LongDivRemBy5(long) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK: Rem |
| // |
| /// CHECK-START: long[] Main.$noinline$LongDivRemBy5(long) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Shl |
| /// CHECK-NEXT: Add |
| /// CHECK-NEXT: Sub |
| // |
| /// CHECK-START-ARM64: long[] Main.$noinline$LongDivRemBy5(long) disassembly (after) |
| /// CHECK: asr x{{\d+}}, x{{\d+}}, #1 |
| /// CHECK-NEXT: add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63 |
| /// CHECK: add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsl #2 |
| /// CHECK: sub x{{\d+}}, x{{\d+}}, x{{\d+}} |
| private static long[] $noinline$LongDivRemBy5(long v) { |
| long q = v / 5L; |
| long r = v % 5L; |
| return new long[] {q, r}; |
| } |
| |
| // A test case to check: |
| // If there is HDiv with the same inputs as HRem, it is reused. |
| // |
| /// CHECK-START: long[] Main.$noinline$LongDivRemByMinus5(long) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK: Rem |
| // |
| /// CHECK-START: long[] Main.$noinline$LongDivRemByMinus5(long) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Mul |
| /// CHECK-NEXT: Sub |
| // |
| /// CHECK-START-ARM64: long[] Main.$noinline$LongDivRemByMinus5(long) disassembly (after) |
| /// CHECK: asr x{{\d+}}, x{{\d+}}, #1 |
| /// CHECK-NEXT: add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63 |
| /// CHECK: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}} |
| private static long[] $noinline$LongDivRemByMinus5(long v) { |
| long q = v / -5L; |
| long r = v % -5L; |
| return new long[] {q, r}; |
| } |
| |
| // A test case to check: |
| // If there is HDiv with the same inputs as HRem, it is reused. |
| // |
| /// CHECK-START: long[] Main.$noinline$LongDivRemBy7(long) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK: Rem |
| // |
| /// CHECK-START: long[] Main.$noinline$LongDivRemBy7(long) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Shl |
| /// CHECK-NEXT: Sub |
| /// CHECK-NEXT: Sub |
| // |
| /// CHECK-START-ARM64: long[] Main.$noinline$LongDivRemBy7(long) instruction_simplifier_arm64 (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: DataProcWithShifterOp |
| /// CHECK-NEXT: Add |
| // |
| /// CHECK-START-ARM64: long[] Main.$noinline$LongDivRemBy7(long) disassembly (after) |
| /// CHECK: asr x{{\d+}}, x{{\d+}}, #1 |
| /// CHECK-NEXT: add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63 |
| /// CHECK: sub x{{\d+}}, x{{\d+}}, x{{\d+}}, lsl #3 |
| /// CHECK: add x{{\d+}}, x{{\d+}}, x{{\d+}} |
| // |
| /// CHECK-START-ARM: long[] Main.$noinline$LongDivRemBy7(long) instruction_simplifier_arm (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: DataProcWithShifterOp |
| /// CHECK-NEXT: Add |
| // |
| /// CHECK-START-ARM: long[] Main.$noinline$LongDivRemBy7(long) disassembly (after) |
| /// CHECK: blx lr |
| // CHECK: lsl ip, r{{\d}}, #3 |
| // CHECK-NEXT: orr ip, r{{\d}}, lsr #29 |
| // CHECK-NEXT: sub r{{\d}}, r{{\d}}, r{{\d}}, lsl #3 |
| // CHECK-NEXT: sbc{{s?}} r{{\d}}, r{{\d}}, ip |
| /// CHECK: add{{s?}} r{{\d}}, r{{\d}} |
| /// CHECK-NEXT: adc{{s?}} r{{\d}}, r{{\d}} |
| private static long[] $noinline$LongDivRemBy7(long v) { |
| long q = v / 7L; |
| long r = v % 7L; |
| return new long[] {q, r}; |
| } |
| |
| // A test case to check: |
| // If there is HDiv with the same inputs as HRem, it is reused. |
| // |
| /// CHECK-START: long[] Main.$noinline$LongDivRemByMinus7(long) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK: Rem |
| // |
| /// CHECK-START: long[] Main.$noinline$LongDivRemByMinus7(long) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Mul |
| /// CHECK-NEXT: Sub |
| // |
| /// CHECK-START-ARM64: long[] Main.$noinline$LongDivRemByMinus7(long) disassembly (after) |
| /// CHECK: asr x{{\d+}}, x{{\d+}}, #1 |
| /// CHECK-NEXT: add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63 |
| /// CHECK: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}} |
| private static long[] $noinline$LongDivRemByMinus7(long v) { |
| long q = v / -7L; |
| long r = v % -7L; |
| return new long[] {q, r}; |
| } |
| |
| // A test case to check: |
| // If there is HDiv with the same inputs as HRem, it is reused. |
| // |
| /// CHECK-START: long[] Main.$noinline$LongDivRemByMaxLong(long) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK: Rem |
| // |
| /// CHECK-START: long[] Main.$noinline$LongDivRemByMaxLong(long) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Shl |
| /// CHECK-NEXT: Sub |
| /// CHECK-NEXT: Sub |
| // |
| /// CHECK-START-ARM64: long[] Main.$noinline$LongDivRemByMaxLong(long) instruction_simplifier_arm64 (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: DataProcWithShifterOp |
| /// CHECK-NEXT: Add |
| // |
| /// CHECK-START-ARM64: long[] Main.$noinline$LongDivRemByMaxLong(long) disassembly (after) |
| /// CHECK: asr x{{\d+}}, x{{\d+}}, #61 |
| /// CHECK-NEXT: add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63 |
| /// CHECK: sub x{{\d+}}, x{{\d+}}, x{{\d+}}, lsl #63 |
| /// CHECK: add x{{\d+}}, x{{\d+}}, x{{\d+}} |
| // |
| /// CHECK-START-ARM: long[] Main.$noinline$LongDivRemByMaxLong(long) instruction_simplifier_arm (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: DataProcWithShifterOp |
| /// CHECK-NEXT: Add |
| // |
| /// CHECK-START-ARM: long[] Main.$noinline$LongDivRemByMaxLong(long) disassembly (after) |
| /// CHECK: blx lr |
| // CHECK: sub r{{\d}}, r{{\d}}, r{{\d}}, lsl #31 |
| // CHECK-NEXT: mov{{s?}} r{{\d}}, r{{\d}} |
| /// CHECK: add{{s?}} r{{\d}}, r{{\d}} |
| /// CHECK-NEXT: adc{{s?}} r{{\d}}, r{{\d}} |
| private static long[] $noinline$LongDivRemByMaxLong(long v) { |
| long q = v / Long.MAX_VALUE; |
| long r = v % Long.MAX_VALUE; |
| return new long[] {q, r}; |
| } |
| |
| // A test case to check: |
| // HDiv with the same inputs as HRem but in another basic block is not reused. |
| // |
| /// CHECK-START: long Main.$noinline$LongDivRemBy18(long, boolean) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK: Rem |
| // |
| /// CHECK-START: long Main.$noinline$LongDivRemBy18(long, boolean) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK: Rem |
| private static long $noinline$LongDivRemBy18(long v, boolean do_division) { |
| long result = 0; |
| if (do_division) { |
| long q = v / 18L; |
| result = q << 32; |
| } |
| long r = v % 18L; |
| return result | r; |
| } |
| |
| // A test case to check: |
| // If there is HDiv with the same inputs as HRem, it is reused. |
| // |
| /// CHECK-START: long Main.$noinline$LongDivRem(long, long) instruction_simplifier$after_gvn (before) |
| /// CHECK: Div |
| /// CHECK-NEXT: Rem |
| // |
| /// CHECK-START: long Main.$noinline$LongDivRem(long, long) instruction_simplifier$after_gvn (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Mul |
| /// CHECK-NEXT: Sub |
| private static long $noinline$LongDivRem(long v, long s) { |
| long q = v / s; |
| long r = v % s; |
| return (q << 32) | r; |
| } |
| |
| // A test case to check: |
| // If there is HDiv with the same inputs as HRem, it is reused. |
| // |
| /// CHECK-START: long Main.$noinline$LongRemDiv(long, long) instruction_simplifier$after_gvn (before) |
| /// CHECK: Rem |
| /// CHECK-NEXT: Div |
| // |
| /// CHECK-START: long Main.$noinline$LongRemDiv(long, long) instruction_simplifier$after_gvn (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Mul |
| /// CHECK-NEXT: Sub |
| private static long $noinline$LongRemDiv(long v, long s) { |
| long r = v % s; |
| long q = v / s; |
| return (q << 32) | r; |
| } |
| |
| // A test case to check: |
| // HDiv with the same inputs as HRem but in another basic block is not reused. |
| // |
| /// CHECK-START: long Main.$noinline$LongDivRem(long, long, boolean) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK: Rem |
| // |
| /// CHECK-START: long Main.$noinline$LongDivRem(long, long, boolean) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK: Rem |
| private static long $noinline$LongDivRem(long v, long s, boolean do_division) { |
| long result = 0; |
| if (do_division) { |
| long q = v / s; |
| result = q << 32; |
| } |
| long r = v % s; |
| return result | r; |
| } |
| |
| // A test case to check: |
| // If HRem is in a loop, the instruction simplifier postpones its optimization till |
| // loop analysis/optimizations are done. |
| // |
| /// CHECK-START: long Main.$noinline$LongRemBy18InLoop(long) instruction_simplifier (before) |
| /// CHECK: Div loop:B{{\d+}} |
| /// CHECK-NEXT: Rem loop:B{{\d+}} |
| // |
| /// CHECK-START: long Main.$noinline$LongRemBy18InLoop(long) instruction_simplifier (after) |
| /// CHECK: Div loop:B{{\d+}} |
| /// CHECK-NEXT: Rem loop:B{{\d+}} |
| // |
| /// CHECK-START: long Main.$noinline$LongRemBy18InLoop(long) instruction_simplifier$after_loop_opt (before) |
| /// CHECK: Div loop:B{{\d+}} |
| /// CHECK-NEXT: Rem loop:B{{\d+}} |
| // |
| /// CHECK-START: long Main.$noinline$LongRemBy18InLoop(long) instruction_simplifier$after_loop_opt (after) |
| /// CHECK-NOT: Rem |
| /// CHECK: Div loop:B{{\d+}} |
| /// CHECK-NEXT: Mul loop:B{{\d+}} |
| /// CHECK-NEXT: Sub loop:B{{\d+}} |
| private static long $noinline$LongRemBy18InLoop(long v) { |
| long[] values = new long[(int)v]; |
| for (int i = 0; i < values.length; ++i) { |
| long d = (long)i; |
| long q = d / 18L; |
| long r = d % 18L; |
| values[i] = q + r; |
| } |
| return values[values.length - 1]; |
| } |
| |
| // A test case to check: |
| // HRem with a divisor of power 2 is not optimized by the instruction simplifier because |
| // the case is optimized by the code generator. |
| // |
| /// CHECK-START: long Main.$noinline$LongRemByLongMin(long) instruction_simplifier (before) |
| /// CHECK: Div |
| /// CHECK-NEXT: Rem |
| // |
| /// CHECK-START: long Main.$noinline$LongRemByLongMin(long) instruction_simplifier (after) |
| /// CHECK: Div |
| /// CHECK-NEXT: Rem |
| private static long $noinline$LongRemByLongMin(long v) { |
| long q = v / Long.MIN_VALUE; |
| long r = v % Long.MIN_VALUE; |
| return q + r; |
| } |
| |
| public static void main(String args[]) { |
| remInt(); |
| remLong(); |
| } |
| } |