/* * Copyright (C) 2015 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 { // A dummy value to defeat inlining of these routines. static boolean doThrow = false; public static void assertIntEquals(int expected, int result) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); } } public static void assertLongEquals(long expected, long result) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); } } /** * Test basic merging of `MUL+ADD` into `MULADD`. */ /// CHECK-START-ARM64: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm64 (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> Mul [<>,<>] /// CHECK: <> Add [<>,<>] /// CHECK: Return [<>] /// CHECK-START-ARM64: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm64 (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Add /// CHECK: Return [<>] /// CHECK-START-ARM64: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm64 (after) /// CHECK-NOT: Mul /// CHECK-NOT: Add /// CHECK-START-ARM64: int Main.$opt$noinline$mulAdd(int, int, int) disassembly (after) /// CHECK: madd w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} /// CHECK-START-ARM: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> Mul [<>,<>] /// CHECK: <> Add [<>,<>] /// CHECK: Return [<>] /// CHECK-START-ARM: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Add /// CHECK: Return [<>] /// CHECK-START-ARM: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm (after) /// CHECK-NOT: Mul /// CHECK-NOT: Add /// CHECK-START-ARM: int Main.$opt$noinline$mulAdd(int, int, int) disassembly (after) /// CHECK: mla r{{\d+}}, r{{\d+}}, r{{\d+}}, r{{\d+}} public static int $opt$noinline$mulAdd(int acc, int left, int right) { if (doThrow) throw new Error(); return acc + left * right; } /** * Test basic merging of `MUL+SUB` into `MULSUB`. */ /// CHECK-START-ARM64: long Main.$opt$noinline$mulSub(long, long, long) instruction_simplifier_arm64 (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> Mul [<>,<>] /// CHECK: <> Sub [<>,<>] /// CHECK: Return [<>] /// CHECK-START-ARM64: long Main.$opt$noinline$mulSub(long, long, long) instruction_simplifier_arm64 (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Sub /// CHECK: Return [<>] /// CHECK-START-ARM64: long Main.$opt$noinline$mulSub(long, long, long) instruction_simplifier_arm64 (after) /// CHECK-NOT: Mul /// CHECK-NOT: Sub /// CHECK-START-ARM64: long Main.$opt$noinline$mulSub(long, long, long) disassembly (after) /// CHECK: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}} /// CHECK-START-ARM: long Main.$opt$noinline$mulSub(long, long, long) instruction_simplifier_arm (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> Mul [<>,<>] /// CHECK: <> Sub [<>,<>] /// CHECK: Return [<>] /// CHECK-START-ARM: long Main.$opt$noinline$mulSub(long, long, long) instruction_simplifier_arm (after) /// CHECK-NOT: MultiplyAccumulate public static long $opt$noinline$mulSub(long acc, long left, long right) { if (doThrow) throw new Error(); return acc - left * right; } /** * Test that we do not create a multiply-accumulate instruction when there * are other uses of the multiplication that cannot merge it. */ /// CHECK-START-ARM64: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm64 (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> Mul [<>,<>] /// CHECK: <> Add [<>,<>] /// CHECK: <> Or [<>,<>] /// CHECK: Return [<>] /// CHECK-START-ARM64: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm64 (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> Mul [<>,<>] /// CHECK: <> Add [<>,<>] /// CHECK: <> Or [<>,<>] /// CHECK: Return [<>] /// CHECK-START-ARM64: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm64 (after) /// CHECK-NOT: MultiplyAccumulate /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> Mul [<>,<>] /// CHECK: <> Add [<>,<>] /// CHECK: <> Or [<>,<>] /// CHECK: Return [<>] /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> Mul [<>,<>] /// CHECK: <> Add [<>,<>] /// CHECK: <> Or [<>,<>] /// CHECK: Return [<>] /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm (after) /// CHECK-NOT: MultiplyAccumulate public static int $opt$noinline$multipleUses1(int acc, int left, int right) { if (doThrow) throw new Error(); int temp = left * right; return temp | (acc + temp); } /** * Test that we do not create a multiply-accumulate instruction even when all * uses of the multiplication can merge it. */ /// CHECK-START-ARM64: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm64 (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> Mul [<>,<>] /// CHECK: <> Add [<>,<>] /// CHECK: <> Sub [<>,<>] /// CHECK: <> Add [<>,<>] /// CHECK: Return [<>] /// CHECK-START-ARM64: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm64 (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> Mul [<>,<>] /// CHECK: <> Add [<>,<>] /// CHECK: <> Sub [<>,<>] /// CHECK: <> Add [<>,<>] /// CHECK: Return [<>] /// CHECK-START-ARM64: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm64 (after) /// CHECK-NOT: MultiplyAccumulate /// CHECK-START-ARM: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> Mul [<>,<>] /// CHECK: <> Add [<>,<>] /// CHECK: <> Sub [<>,<>] /// CHECK: <> Add [<>,<>] /// CHECK: Return [<>] /// CHECK-START-ARM: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> Mul [<>,<>] /// CHECK: <> Add [<>,<>] /// CHECK: <> Sub [<>,<>] /// CHECK: <> Add [<>,<>] /// CHECK: Return [<>] /// CHECK-START-ARM: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm (after) /// CHECK-NOT: MultiplyAccumulate public static long $opt$noinline$multipleUses2(long acc, long left, long right) { if (doThrow) throw new Error(); long temp = left * right; return (acc + temp) + (acc - temp); } /** * Test the interpretation of `a * (b + 1)` as `a + (a * b)`. */ /// CHECK-START-ARM64: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm64 (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> IntConstant 1 /// CHECK: <> Add [<>,<>] /// CHECK: <> Mul [<>,<>] /// CHECK: Return [<>] /// CHECK-START-ARM64: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm64 (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Add /// CHECK: Return [<>] /// CHECK-START-ARM64: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm64 (after) /// CHECK-NOT: Mul /// CHECK-NOT: Add /// CHECK-START-ARM64: int Main.$opt$noinline$mulPlusOne(int, int) disassembly (after) /// CHECK: madd w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} /// CHECK-START-ARM: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> IntConstant 1 /// CHECK: <> Add [<>,<>] /// CHECK: <> Mul [<>,<>] /// CHECK: Return [<>] /// CHECK-START-ARM: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Add /// CHECK: Return [<>] /// CHECK-START-ARM: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm (after) /// CHECK-NOT: Mul /// CHECK-NOT: Add /// CHECK-START-ARM: int Main.$opt$noinline$mulPlusOne(int, int) disassembly (after) /// CHECK: mla r{{\d+}}, r{{\d+}}, r{{\d+}}, r{{\d+}} public static int $opt$noinline$mulPlusOne(int acc, int var) { if (doThrow) throw new Error(); return acc * (var + 1); } /** * Test the interpretation of `a * (1 - b)` as `a - (a * b)`. */ /// CHECK-START-ARM64: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm64 (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> LongConstant 1 /// CHECK: <> Sub [<>,<>] /// CHECK: <> Mul [<>,<>] /// CHECK: Return [<>] /// CHECK-START-ARM64: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm64 (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Sub /// CHECK: Return [<>] /// CHECK-START-ARM64: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm64 (after) /// CHECK-NOT: Mul /// CHECK-NOT: Sub /// CHECK-START-ARM64: long Main.$opt$noinline$mulMinusOne(long, long) disassembly (after) /// CHECK: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}} /// CHECK-START-ARM: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> LongConstant 1 /// CHECK: <> Sub [<>,<>] /// CHECK: <> Mul [<>,<>] /// CHECK: Return [<>] /// CHECK-START-ARM: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm (after) /// CHECK-NOT: MultiplyAccumulate public static long $opt$noinline$mulMinusOne(long acc, long var) { if (doThrow) throw new Error(); return acc * (1 - var); } /** * Test basic merging of `MUL+NEG` into `MULNEG`. */ /// CHECK-START-ARM64: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm64 (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> Mul [<>,<>] /// CHECK: <> Neg [<>] /// CHECK: Return [<>] /// CHECK-START-ARM64: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm64 (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> IntConstant 0 /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Sub /// CHECK: Return [<>] /// CHECK-START-ARM64: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm64 (after) /// CHECK-NOT: Mul /// CHECK-NOT: Neg /// CHECK-START-ARM64: int Main.$opt$noinline$mulNeg(int, int) disassembly (after) /// CHECK: mneg w{{\d+}}, w{{\d+}}, w{{\d+}} /// CHECK-START-ARM: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> Mul [<>,<>] /// CHECK: <> Neg [<>] /// CHECK: Return [<>] /// CHECK-START-ARM: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> Mul [<>,<>] /// CHECK: <> Neg [<>] /// CHECK: Return [<>] /// CHECK-START-ARM: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm (after) /// CHECK-NOT: MultiplyAccumulate public static int $opt$noinline$mulNeg(int left, int right) { if (doThrow) throw new Error(); return - (left * right); } /** * Test basic merging of `MUL+NEG` into `MULNEG`. */ /// CHECK-START-ARM64: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm64 (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> Mul [<>,<>] /// CHECK: <> Neg [<>] /// CHECK: Return [<>] /// CHECK-START-ARM64: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm64 (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> LongConstant 0 /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Sub /// CHECK: Return [<>] /// CHECK-START-ARM64: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm64 (after) /// CHECK-NOT: Mul /// CHECK-NOT: Neg /// CHECK-START-ARM64: long Main.$opt$noinline$mulNeg(long, long) disassembly (after) /// CHECK: mneg x{{\d+}}, x{{\d+}}, x{{\d+}} /// CHECK-START-ARM: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> Mul [<>,<>] /// CHECK: <> Neg [<>] /// CHECK: Return [<>] /// CHECK-START-ARM: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> Mul [<>,<>] /// CHECK: <> Neg [<>] /// CHECK: Return [<>] /// CHECK-START-ARM: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm (after) /// CHECK-NOT: MultiplyAccumulate public static long $opt$noinline$mulNeg(long left, long right) { if (doThrow) throw new Error(); return - (left * right); } /// CHECK-START-ARM64: void Main.SimdMulAdd(int[], int[]) instruction_simplifier_arm64 (before) /// CHECK-DAG: Phi loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecAdd loop:<> outer_loop:none /// CHECK-START-ARM64: void Main.SimdMulAdd(int[], int[]) instruction_simplifier_arm64 (after) /// CHECK-DAG: Phi loop:<> outer_loop:none /// CHECK-DAG: VecMultiplyAccumulate kind:Add loop:<> outer_loop:none /// CHECK-START-ARM64: void Main.SimdMulAdd(int[], int[]) instruction_simplifier_arm64 (after) /// CHECK-NOT: VecMull /// CHECK-NOT: VecAdd public static void SimdMulAdd(int[] array1, int[] array2) { for (int j = 0; j < 100; j++) { array2[j] += 12345 * array1[j]; } } /// CHECK-START-ARM64: void Main.SimdMulSub(int[], int[]) instruction_simplifier_arm64 (before) /// CHECK-DAG: Phi loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecSub loop:<> outer_loop:none /// CHECK-START-ARM64: void Main.SimdMulSub(int[], int[]) instruction_simplifier_arm64 (after) /// CHECK-DAG: Phi loop:<> outer_loop:none /// CHECK-DAG: VecMultiplyAccumulate kind:Sub loop:<> outer_loop:none /// CHECK-START-ARM64: void Main.SimdMulSub(int[], int[]) instruction_simplifier_arm64 (after) /// CHECK-NOT: VecMull /// CHECK-NOT: VecSub public static void SimdMulSub(int[] array1, int[] array2) { for (int j = 0; j < 100; j++) { array2[j] -= 12345 * array1[j]; } } /// CHECK-START-ARM64: void Main.SimdMulMultipleUses(int[], int[]) instruction_simplifier_arm64 (before) /// CHECK-DAG: Phi loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecSub loop:<> outer_loop:none /// CHECK-START-ARM64: void Main.SimdMulMultipleUses(int[], int[]) instruction_simplifier_arm64 (after) /// CHECK-NOT: VecMultiplyAccumulate public static void SimdMulMultipleUses(int[] array1, int[] array2) { for (int j = 0; j < 100; j++) { int temp = 12345 * array1[j]; array2[j] -= temp; array1[j] = temp; } } public static final int ARRAY_SIZE = 1000; public static void initArray(int[] array) { for (int i = 0; i < ARRAY_SIZE; i++) { array[i] = i; } } public static int calcArraySum(int[] array) { int sum = 0; for (int i = 0; i < ARRAY_SIZE; i++) { sum += array[i]; } return sum; } public static void testSimdMultiplyAccumulate() { int[] array1 = new int[ARRAY_SIZE]; int[] array2 = new int[ARRAY_SIZE]; initArray(array1); initArray(array2); SimdMulSub(array1, array2); assertIntEquals(-60608250, calcArraySum(array2)); initArray(array1); initArray(array2); SimdMulAdd(array1, array2); assertIntEquals(61607250, calcArraySum(array2)); } public static void main(String[] args) { assertIntEquals(7, $opt$noinline$mulAdd(1, 2, 3)); assertLongEquals(-26, $opt$noinline$mulSub(4, 5, 6)); assertIntEquals(79, $opt$noinline$multipleUses1(7, 8, 9)); assertLongEquals(20, $opt$noinline$multipleUses2(10, 11, 12)); assertIntEquals(195, $opt$noinline$mulPlusOne(13, 14)); assertLongEquals(-225, $opt$noinline$mulMinusOne(15, 16)); assertIntEquals(-306, $opt$noinline$mulNeg(17, 18)); assertLongEquals(-380, $opt$noinline$mulNeg(19, 20)); testSimdMultiplyAccumulate(); } }