From 265043b86d014a422d1326257c8df8d37c540c24 Mon Sep 17 00:00:00 2001
From: Eric Liu <eric.c.liu@arm.com>
Date: Mon, 3 Feb 2020 01:47:40 +0000
Subject: [PATCH] AArch64: Merge shift pair and integral narrow into add/sub

This patch implements two types of match rules like C2.

1. Merge shift pair into add/sub.
2. Merge integral narrow into add/sub.

E.g. Below code is generated for `x + (((y << 56) >> 56) << 3)`

        lsl x0, x3, #56
        asr x0, x0, #56
        add x0, x2, x0, lsl #3

After this patch, generated assembly above can be optimized to below:

        add x0, x2, w3, sxtb #3

The test cases in this patch can show more details about those match
rules.

Change-Id: I82502186c8e9745dc07c349c3f66eae573006165
---
 .../aarch64/test/TestProtectedAssembler.java  |   4 +-
 .../asm/aarch64/AArch64Assembler.java         |   2 +-
 .../aarch64/test/AArch64ArrayAddressTest.java |   8 +-
 .../AArch64MergeExtendWithAddSubTest.java     | 238 +++++++++++++
 .../AArch64MergeNarrowWithAddSubTest.java     | 314 ++++++++++++++++++
 .../core/aarch64/AArch64NodeMatchRules.java   | 122 ++++++-
 .../lir/aarch64/AArch64ArithmeticOp.java      |  21 +-
 7 files changed, 695 insertions(+), 14 deletions(-)
 create mode 100644 compiler/src/org.graalvm.compiler.core.aarch64.test/src/org/graalvm/compiler/core/aarch64/test/AArch64MergeExtendWithAddSubTest.java
 create mode 100644 compiler/src/org.graalvm.compiler.core.aarch64.test/src/org/graalvm/compiler/core/aarch64/test/AArch64MergeNarrowWithAddSubTest.java

diff --git a/compiler/src/org.graalvm.compiler.asm.aarch64.test/src/org/graalvm/compiler/asm/aarch64/test/TestProtectedAssembler.java b/compiler/src/org.graalvm.compiler.asm.aarch64.test/src/org/graalvm/compiler/asm/aarch64/test/TestProtectedAssembler.java
index 2664154720318..a8a5a74e1d27e 100644
--- a/compiler/src/org.graalvm.compiler.asm.aarch64.test/src/org/graalvm/compiler/asm/aarch64/test/TestProtectedAssembler.java
+++ b/compiler/src/org.graalvm.compiler.asm.aarch64.test/src/org/graalvm/compiler/asm/aarch64/test/TestProtectedAssembler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -258,7 +258,7 @@ protected void adds(int size, Register dst, Register src1, Register src2, Extend
     }
 
     @Override
-    protected void sub(int size, Register dst, Register src1, Register src2, ExtendType extendType, int shiftAmt) {
+    public void sub(int size, Register dst, Register src1, Register src2, ExtendType extendType, int shiftAmt) {
         super.sub(size, dst, src1, src2, extendType, shiftAmt);
     }
 
diff --git a/compiler/src/org.graalvm.compiler.asm.aarch64/src/org/graalvm/compiler/asm/aarch64/AArch64Assembler.java b/compiler/src/org.graalvm.compiler.asm.aarch64/src/org/graalvm/compiler/asm/aarch64/AArch64Assembler.java
index ed9e7492e9151..20e487e34ae58 100755
--- a/compiler/src/org.graalvm.compiler.asm.aarch64/src/org/graalvm/compiler/asm/aarch64/AArch64Assembler.java
+++ b/compiler/src/org.graalvm.compiler.asm.aarch64/src/org/graalvm/compiler/asm/aarch64/AArch64Assembler.java
@@ -1992,7 +1992,7 @@ protected void adds(int size, Register dst, Register src1, Register src2, Extend
      * @param extendType defines how src2 is extended to the same size as src1.
      * @param shiftAmt must be in range 0 to 4.
      */
-    protected void sub(int size, Register dst, Register src1, Register src2, ExtendType extendType, int shiftAmt) {
+    public void sub(int size, Register dst, Register src1, Register src2, ExtendType extendType, int shiftAmt) {
         assert !dst.equals(zr);
         assert !src1.equals(zr);
         assert !src2.equals(sp);
diff --git a/compiler/src/org.graalvm.compiler.core.aarch64.test/src/org/graalvm/compiler/core/aarch64/test/AArch64ArrayAddressTest.java b/compiler/src/org.graalvm.compiler.core.aarch64.test/src/org/graalvm/compiler/core/aarch64/test/AArch64ArrayAddressTest.java
index e65e13454fbcc..a0f1b6b557c00 100644
--- a/compiler/src/org.graalvm.compiler.core.aarch64.test/src/org/graalvm/compiler/core/aarch64/test/AArch64ArrayAddressTest.java
+++ b/compiler/src/org.graalvm.compiler.core.aarch64.test/src/org/graalvm/compiler/core/aarch64/test/AArch64ArrayAddressTest.java
@@ -1,6 +1,6 @@
 /*
- * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2019, Arm Limited. All rights reserved.
+ * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2020, Arm Limited. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,7 +27,7 @@
 
 import org.graalvm.compiler.api.directives.GraalDirectives;
 import org.graalvm.compiler.lir.LIRInstruction;
-import org.graalvm.compiler.lir.aarch64.AArch64ArithmeticOp.ExtendedAddShiftOp;
+import org.graalvm.compiler.lir.aarch64.AArch64ArithmeticOp.ExtendedAddSubShiftOp;
 import org.junit.Test;
 
 import java.util.ArrayDeque;
@@ -36,7 +36,7 @@
 import java.util.function.Predicate;
 
 public class AArch64ArrayAddressTest extends AArch64MatchRuleTest {
-    private static final Predicate<LIRInstruction> predicate = op -> (op instanceof ExtendedAddShiftOp);
+    private static final Predicate<LIRInstruction> predicate = op -> (op instanceof ExtendedAddSubShiftOp);
 
     public static byte loadByte(byte[] arr, int n) {
         return arr[n];
diff --git a/compiler/src/org.graalvm.compiler.core.aarch64.test/src/org/graalvm/compiler/core/aarch64/test/AArch64MergeExtendWithAddSubTest.java b/compiler/src/org.graalvm.compiler.core.aarch64.test/src/org/graalvm/compiler/core/aarch64/test/AArch64MergeExtendWithAddSubTest.java
new file mode 100644
index 0000000000000..f7b31d7cf9fa5
--- /dev/null
+++ b/compiler/src/org.graalvm.compiler.core.aarch64.test/src/org/graalvm/compiler/core/aarch64/test/AArch64MergeExtendWithAddSubTest.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, Arm Limited. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.core.aarch64.test;
+
+import org.graalvm.compiler.lir.LIRInstruction;
+import org.graalvm.compiler.lir.aarch64.AArch64ArithmeticOp;
+import org.junit.Test;
+
+import java.util.function.Predicate;
+
+public class AArch64MergeExtendWithAddSubTest extends AArch64MatchRuleTest {
+
+    private static final Predicate<LIRInstruction> PRED_EXTEND_ADD_SHIFT = op -> (op instanceof AArch64ArithmeticOp.ExtendedAddSubShiftOp && op.name().equals("ADD"));
+    private static final Predicate<LIRInstruction> PRED_EXTEND_SUB_SHIFT = op -> (op instanceof AArch64ArithmeticOp.ExtendedAddSubShiftOp && op.name().equals("SUB"));
+
+    private static final Long[] LONG_VALUES = {-1L, 0L, 0x1234567812345678L, 0xFFFFFFFFL, 0x12L, 0x1234L, Long.MIN_VALUE, Long.MAX_VALUE};
+    private static final Integer[] INT_VALUES = {-1, 0, 0x1234, 0x12345678, Integer.MIN_VALUE, Integer.MAX_VALUE};
+
+    private <T> void predicateExist(String[] testCases, T[] values, Predicate<LIRInstruction> predicate) {
+        for (String t : testCases) {
+            for (T value : values) {
+                test(t, value, value);
+                checkLIR(t, predicate, 1);
+            }
+        }
+    }
+
+    public long addI2LShift(long x, long y) {
+        int z = (int) y;
+        return x + (((long) z) << 3);
+    }
+
+    public long addB2LShift(long x, long y) {
+        byte z = (byte) y;
+        return x + (((long) z) << 2);
+    }
+
+    public long addC2LShift(long x, long y) {
+        char z = (char) y;
+        return x + (((long) z) << 1);
+    }
+
+    public long addS2LShift(long x, long y) {
+        short z = (short) y;
+        return x + (((long) z) << 4);
+    }
+
+    public long subI2LShift(long x, long y) {
+        int z = (int) y;
+        return x - (((long) z) << 1);
+    }
+
+    public long subB2LShift(long x, long y) {
+        byte z = (byte) y;
+        return x - (((long) z) << 2);
+    }
+
+    public long subC2LShift(long x, long y) {
+        char z = (char) y;
+        return x - (((long) z) << 3);
+    }
+
+    public long subS2LShift(long x, long y) {
+        short z = (short) y;
+        return x - (((long) z) << 4);
+    }
+
+    public long addI2L(long x, long y) {
+        int z = (int) y;
+        return x + z;
+    }
+
+    public long addB2L(long x, long y) {
+        byte z = (byte) y;
+        return x + z;
+    }
+
+    public long addC2L(long x, long y) {
+        char z = (char) y;
+        return x + z;
+    }
+
+    public long addS2L(long x, long y) {
+        short z = (short) y;
+        return x + z;
+    }
+
+    public int addB2S(int x, int y) {
+        short a = (short) x;
+        byte b = (byte) y;
+        return a + b;
+    }
+
+    public int addB2SShift(int x, int y) {
+        short a = (short) x;
+        byte b = (byte) y;
+        return a + (b << 2);
+    }
+
+    public int addB2I(int x, int y) {
+        byte z = (byte) y;
+        return x + z;
+    }
+
+    public int addB2IShift(int x, int y) {
+        byte z = (byte) y;
+        return x + (z << 3);
+    }
+
+    public int addS2I(int x, int y) {
+        short z = (short) y;
+        return x + z;
+    }
+
+    public int addS2IShift(int x, int y) {
+        short z = (short) y;
+        return x + (z << 2);
+    }
+
+    public int addC2I(int x, int y) {
+        char z = (char) y;
+        return x + z;
+    }
+
+    public int addC2IShift(int x, int y) {
+        char z = (char) y;
+        return x + (z << 1);
+    }
+
+    @Test
+    public void mergeSignExtendIntoAdd() {
+        predicateExist(new String[]{"addB2S", "addB2I", "addS2I", "addC2I"}, INT_VALUES, PRED_EXTEND_ADD_SHIFT);
+        predicateExist(new String[]{"addB2L", "addC2L", "addI2L", "addS2L"}, LONG_VALUES, PRED_EXTEND_ADD_SHIFT);
+    }
+
+    @Test
+    public void mergeSignExtendShiftIntoAdd() {
+        predicateExist(new String[]{"addB2SShift", "addB2IShift", "addS2IShift", "addC2IShift"}, INT_VALUES, PRED_EXTEND_ADD_SHIFT);
+        predicateExist(new String[]{"addB2LShift", "addC2LShift", "addI2LShift", "addS2LShift"}, LONG_VALUES, PRED_EXTEND_ADD_SHIFT);
+    }
+
+    public long subI2L(long x, long y) {
+        int z = (int) y;
+        return x - z;
+    }
+
+    public long subB2L(long x, long y) {
+        byte z = (byte) y;
+        return x - z;
+    }
+
+    public long subC2L(long x, long y) {
+        char z = (char) y;
+        return x - z;
+    }
+
+    public long subS2L(long x, long y) {
+        short z = (short) y;
+        return x - z;
+    }
+
+    public int subB2S(int x, int y) {
+        short a = (short) x;
+        byte b = (byte) y;
+        return a - b;
+    }
+
+    public int subB2SShift(int x, int y) {
+        short a = (short) x;
+        byte b = (byte) y;
+        return a - (b << 2);
+    }
+
+    public int subB2I(int x, int y) {
+        byte z = (byte) y;
+        return x - z;
+    }
+
+    public int subB2IShift(int x, int y) {
+        byte z = (byte) y;
+        return x - (z << 3);
+    }
+
+    public int subS2I(int x, int y) {
+        short z = (short) y;
+        return x - z;
+    }
+
+    public int subS2IShift(int x, int y) {
+        short z = (short) y;
+        return x - (z << 2);
+    }
+
+    public int subC2I(int x, int y) {
+        char z = (char) y;
+        return x - z;
+    }
+
+    public int subC2IShift(int x, int y) {
+        char z = (char) y;
+        return x - (z << 1);
+    }
+
+    @Test
+    public void mergeSignExtendShiftIntoSub() {
+        predicateExist(new String[]{"subB2SShift", "subB2IShift", "subS2IShift", "subC2IShift"}, INT_VALUES, PRED_EXTEND_SUB_SHIFT);
+        predicateExist(new String[]{"subB2LShift", "subC2LShift", "subI2LShift", "subS2LShift"}, LONG_VALUES, PRED_EXTEND_SUB_SHIFT);
+    }
+
+    @Test
+    public void mergeSignExtendIntoSub() {
+        predicateExist(new String[]{"subB2S", "subB2I", "subS2I", "subC2I"}, INT_VALUES, PRED_EXTEND_SUB_SHIFT);
+        predicateExist(new String[]{"subB2L", "subC2L", "subI2L", "subS2L"}, LONG_VALUES, PRED_EXTEND_SUB_SHIFT);
+    }
+}
diff --git a/compiler/src/org.graalvm.compiler.core.aarch64.test/src/org/graalvm/compiler/core/aarch64/test/AArch64MergeNarrowWithAddSubTest.java b/compiler/src/org.graalvm.compiler.core.aarch64.test/src/org/graalvm/compiler/core/aarch64/test/AArch64MergeNarrowWithAddSubTest.java
new file mode 100644
index 0000000000000..98ecb46bc4dc6
--- /dev/null
+++ b/compiler/src/org.graalvm.compiler.core.aarch64.test/src/org/graalvm/compiler/core/aarch64/test/AArch64MergeNarrowWithAddSubTest.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, Arm Limited. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.core.aarch64.test;
+
+import org.graalvm.compiler.lir.LIRInstruction;
+import org.graalvm.compiler.lir.aarch64.AArch64ArithmeticOp;
+import org.junit.Test;
+
+import java.util.function.Predicate;
+
+public class AArch64MergeNarrowWithAddSubTest extends AArch64MatchRuleTest {
+
+    private static final Predicate<LIRInstruction> PRED_EXTEND_ADD_SHIFT = op -> (op instanceof AArch64ArithmeticOp.ExtendedAddSubShiftOp && op.name().equals("ADD"));
+    private static final Predicate<LIRInstruction> PRED_EXTEND_SUB_SHIFT = op -> (op instanceof AArch64ArithmeticOp.ExtendedAddSubShiftOp && op.name().equals("SUB"));
+
+    private static final Long[] LONG_VALUES = {-1L, 0L, 0x1234567812345678L, 0xFFFFFFFFL, 0x12L, 0x1234L, Long.MIN_VALUE, Long.MAX_VALUE};
+    private static final Integer[] INT_VALUES = {-1, 0, 0x1234, 0x12345678, Integer.MIN_VALUE, Integer.MAX_VALUE};
+
+    private <T> void predicateExist(String[] testCases, T[] values, Predicate<LIRInstruction> predicate) {
+        for (String t : testCases) {
+            for (T value : values) {
+                test(t, value, value);
+                checkLIR(t, predicate, 1);
+            }
+        }
+    }
+
+    public int addIB(int x, int y) {
+        return x + (y & 0xff);
+    }
+
+    public int addIH(int x, int y) {
+        return x + (y & 0xffff);
+    }
+
+    public long addLB(long x, long y) {
+        return x + (y & 0xff);
+    }
+
+    public long addLH(long x, long y) {
+        return x + (y & 0xffff);
+    }
+
+    public long addLW(long x, long y) {
+        return x + (y & 0xffffffffL);
+    }
+
+    @Test
+    public void mergeDowncastIntoAdd() {
+        predicateExist(new String[]{"addIB", "addIH"}, INT_VALUES, PRED_EXTEND_ADD_SHIFT);
+        predicateExist(new String[]{"addLB", "addLH", "addLW"}, LONG_VALUES, PRED_EXTEND_ADD_SHIFT);
+    }
+
+    public int subIB(int x, int y) {
+        return x - (y & 0xff);
+    }
+
+    public int subIH(int x, int y) {
+        return x - (y & 0xffff);
+    }
+
+    public long subLB(long x, long y) {
+        return x - (y & 0xff);
+    }
+
+    public long subLH(long x, long y) {
+        return x - (y & 0xffff);
+    }
+
+    public long subLW(long x, long y) {
+        return x - (y & 0xffffffffL);
+    }
+
+    @Test
+    public void mergeDowncastIntoSub() {
+        predicateExist(new String[]{"subIB", "subIH"}, INT_VALUES, PRED_EXTEND_SUB_SHIFT);
+        predicateExist(new String[]{"subLB", "subLH", "subLW"}, LONG_VALUES, PRED_EXTEND_SUB_SHIFT);
+    }
+
+    public int addIBShift(int x, int y) {
+        return x + ((y & 0xff) << 3);
+    }
+
+    public int addIHShift(int x, int y) {
+        return x + ((y & 0xffff) << 3);
+    }
+
+    public long addLBShift(long x, long y) {
+        return x + ((y & 0xffL) << 3);
+    }
+
+    public long addLHShift(long x, long y) {
+        return x + ((y & 0xffffL) << 3);
+    }
+
+    public long addLWShift(long x, long y) {
+        return x + ((y & 0xffffffffL) << 3);
+    }
+
+    @Test
+    public void mergeShiftDowncastIntoAdd() {
+        predicateExist(new String[]{"addIBShift", "addIHShift"}, INT_VALUES, PRED_EXTEND_ADD_SHIFT);
+        predicateExist(new String[]{"addLBShift", "addLHShift", "addLWShift"}, LONG_VALUES, PRED_EXTEND_ADD_SHIFT);
+    }
+
+    public int subIBShift(int x, int y) {
+        return x - ((y & 0xff) << 3);
+    }
+
+    public int subIHShift(int x, int y) {
+        return x - ((y & 0xffff) << 3);
+    }
+
+    public long subLBShift(long x, long y) {
+        return x - ((y & 0xffL) << 3);
+    }
+
+    public long subLHShift(long x, long y) {
+        return x - ((y & 0xffffL) << 3);
+    }
+
+    public long subLWShift(long x, long y) {
+        return x - ((y & 0xffffffffL) << 3);
+    }
+
+    @Test
+    public void mergeShiftDowncastIntoSub() {
+        predicateExist(new String[]{"subIBShift", "subIHShift"}, INT_VALUES, PRED_EXTEND_SUB_SHIFT);
+        predicateExist(new String[]{"subLBShift", "subLHShift", "subLWShift"}, LONG_VALUES, PRED_EXTEND_SUB_SHIFT);
+    }
+
+    public int addIntExtractShort(int x, int y) {
+        return x + ((y << 16) >> 16);
+    }
+
+    public int addIntExtractByte(int x, int y) {
+        return x + ((y << 24) >> 24);
+    }
+
+    public long addLongExtractInt(long x, long y) {
+        return x + ((y << 32) >> 32);
+    }
+
+    public long addLongExtractShort(long x, long y) {
+        return x + ((y << 48) >> 48);
+    }
+
+    public long addLongExtractByte(long x, long y) {
+        return x + ((y << 56) >> 56);
+    }
+
+    @Test
+    public void addSignExtractTest() {
+        predicateExist(new String[]{"addIntExtractShort", "addIntExtractByte"}, INT_VALUES, PRED_EXTEND_ADD_SHIFT);
+        predicateExist(new String[]{"addLongExtractInt", "addLongExtractShort", "addLongExtractByte"}, LONG_VALUES, PRED_EXTEND_ADD_SHIFT);
+    }
+
+    public int addIntExtractShortByShift(int x, int y) {
+        return x + (((y << 16) >> 16) << 3);
+    }
+
+    public int addIntExtractByteByShift(int x, int y) {
+        return x + (((y << 24) >> 24) << 3);
+    }
+
+    public long addLongExtractIntByShift(long x, long y) {
+        return x + (((y << 32) >> 32) << 3);
+    }
+
+    public long addLongExtractShortByShift(long x, long y) {
+        return x + (((y << 48) >> 48) << 3);
+    }
+
+    public long addLongExtractByteByShift(long x, long y) {
+        return x + (((y << 56) >> 56) << 3);
+    }
+
+    @Test
+    public void addExtractByShiftTest() {
+        predicateExist(new String[]{"addIntExtractShortByShift", "addIntExtractByteByShift"}, INT_VALUES, PRED_EXTEND_ADD_SHIFT);
+        predicateExist(new String[]{"addLongExtractIntByShift", "addLongExtractShortByShift", "addLongExtractByteByShift"}, LONG_VALUES, PRED_EXTEND_ADD_SHIFT);
+    }
+
+    public int addIntUnsignedExtractByte(int x, int y) {
+        return x + ((y << 24) >>> 24);
+    }
+
+    public long addLongUnsignedExtractByte(long x, long y) {
+        return x + ((y << 56) >>> 56);
+    }
+
+    @Test
+    public void addUnsignedExtractTest() {
+        predicateExist(new String[]{"addIntUnsignedExtractByte"}, INT_VALUES, PRED_EXTEND_ADD_SHIFT);
+        predicateExist(new String[]{"addLongUnsignedExtractByte"}, LONG_VALUES, PRED_EXTEND_ADD_SHIFT);
+    }
+
+    public int addIntUnsignedExtractByteByShift(int x, int y) {
+        return x + (((y << 24) >>> 24) << 2);
+    }
+
+    public long addLongUnsignedExtractByteByShift(long x, long y) {
+        return x + (((y << 56) >>> 56) << 1);
+    }
+
+    @Test
+    public void addUnsignedExtractByShiftTest() {
+        predicateExist(new String[]{"addIntUnsignedExtractByteByShift"}, INT_VALUES, PRED_EXTEND_ADD_SHIFT);
+        predicateExist(new String[]{"addLongUnsignedExtractByteByShift"}, LONG_VALUES, PRED_EXTEND_ADD_SHIFT);
+    }
+
+    public int subIntExtractShort(int x, int y) {
+        return x - ((y << 16) >> 16);
+    }
+
+    public int subIntExtractByte(int x, int y) {
+        return x - ((y << 24) >> 24);
+    }
+
+    public long subLongExtractInt(long x, long y) {
+        return x - ((y << 32) >> 32);
+    }
+
+    public long subLongExtractShort(long x, long y) {
+        return x - ((y << 48) >> 48);
+    }
+
+    public long subLongExtractByte(long x, long y) {
+        return x - ((y << 56) >> 56);
+    }
+
+    @Test
+    public void subExtractTest() {
+        predicateExist(new String[]{"subIntExtractShort", "subIntExtractByte"}, INT_VALUES, PRED_EXTEND_SUB_SHIFT);
+        predicateExist(new String[]{"subLongExtractInt", "subLongExtractShort", "subLongExtractByte"}, LONG_VALUES, PRED_EXTEND_SUB_SHIFT);
+    }
+
+    public int subIntExtractShortByShift(int x, int y) {
+        return x - (((y << 16) >> 16) << 3);
+    }
+
+    public int subIntExtractByteByShift(int x, int y) {
+        return x - (((y << 24) >> 24) << 3);
+    }
+
+    public long subLongExtractIntByShift(long x, long y) {
+        return x - (((y << 32) >> 32) << 3);
+    }
+
+    public long subLongExtractShortByShift(long x, long y) {
+        return x - (((y << 48) >> 48) << 3);
+    }
+
+    public long subLongExtractByteByShift(long x, long y) {
+        return x - (((y << 56) >> 56) << 3);
+    }
+
+    @Test
+    public void subExtractByShiftTest() {
+        predicateExist(new String[]{"subIntExtractShortByShift", "subIntExtractByteByShift"}, INT_VALUES, PRED_EXTEND_SUB_SHIFT);
+        predicateExist(new String[]{"subLongExtractIntByShift", "subLongExtractShortByShift", "subLongExtractByteByShift"}, LONG_VALUES, PRED_EXTEND_SUB_SHIFT);
+    }
+
+    public int subIntUnsignedExtractByte(int x, int y) {
+        return x - ((y << 24) >>> 24);
+    }
+
+    public long subLongUnsignedExtractByte(long x, long y) {
+        return x - ((y << 56) >>> 56);
+    }
+
+    @Test
+    public void subUnsignedExtractTest() {
+        predicateExist(new String[]{"subIntUnsignedExtractByte"}, INT_VALUES, PRED_EXTEND_SUB_SHIFT);
+        predicateExist(new String[]{"subLongUnsignedExtractByte"}, LONG_VALUES, PRED_EXTEND_SUB_SHIFT);
+    }
+
+    public int subIntUnsignedExtractByteByShift(int x, int y) {
+        return x - (((y << 24) >>> 24) << 1);
+    }
+
+    public long subLongUnsignedExtractByteByShift(long x, long y) {
+        return x - (((y << 56) >>> 56) << 2);
+    }
+
+    @Test
+    public void subUnsignedExtractByShiftTest() {
+        predicateExist(new String[]{"subIntUnsignedExtractByteByShift"}, INT_VALUES, PRED_EXTEND_SUB_SHIFT);
+        predicateExist(new String[]{"subLongUnsignedExtractByteByShift"}, LONG_VALUES, PRED_EXTEND_SUB_SHIFT);
+    }
+}
diff --git a/compiler/src/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64NodeMatchRules.java b/compiler/src/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64NodeMatchRules.java
index af24ec66ec868..2d8db5f52a03d 100644
--- a/compiler/src/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64NodeMatchRules.java
+++ b/compiler/src/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64NodeMatchRules.java
@@ -141,6 +141,22 @@ private static ExtendType getZeroExtendType(int fromBits) {
         }
     }
 
+    private static ExtendType getSignExtendType(int fromBits) {
+        switch (fromBits) {
+            case Byte.SIZE:
+                return ExtendType.SXTB;
+            case Short.SIZE:
+                return ExtendType.SXTH;
+            case Integer.SIZE:
+                return ExtendType.SXTW;
+            case Long.SIZE:
+                return ExtendType.SXTX;
+            default:
+                GraalError.shouldNotReachHere("extended from " + fromBits + "bits is not supported!");
+                return null;
+        }
+    }
+
     private AllocatableValue moveSp(AllocatableValue value) {
         return getLIRGeneratorTool().moveSp(value);
     }
@@ -190,7 +206,83 @@ private ComplexMatchResult emitBitTestAndBranch(FixedNode trueSuccessor, FixedNo
     }
 
     private static boolean isNarrowingLongToInt(NarrowNode narrow) {
-        return narrow.getInputBits() == 64 && narrow.getResultBits() == 32;
+        return narrow.getInputBits() == Long.SIZE && narrow.getResultBits() == Integer.SIZE;
+    }
+
+    private ComplexMatchResult emitExtendedAddSubShift(BinaryNode op, ValueNode x, ValueNode y, ExtendType extType, int shiftAmt) {
+        assert op instanceof AddNode || op instanceof SubNode;
+        return builder -> {
+            AllocatableValue src1 = moveSp(gen.asAllocatable(operand(x)));
+            AllocatableValue src2 = moveSp(gen.asAllocatable(operand(y)));
+            Variable result = gen.newVariable(LIRKind.combine(operand(x), operand(y)));
+            AArch64ArithmeticOp arithmeticOp = op instanceof AddNode ? AArch64ArithmeticOp.ADD : AArch64ArithmeticOp.SUB;
+            gen.append(new AArch64ArithmeticOp.ExtendedAddSubShiftOp(arithmeticOp, result, src1, src2, extType, shiftAmt));
+            return result;
+        };
+    }
+
+    @MatchRule("(Add=op x (LeftShift (SignExtend=ext y) Constant=lshift))")
+    @MatchRule("(Sub=op x (LeftShift (SignExtend=ext y) Constant=lshift))")
+    @MatchRule("(Add=op x (LeftShift (ZeroExtend=ext y) Constant=lshift))")
+    @MatchRule("(Sub=op x (LeftShift (ZeroExtend=ext y) Constant=lshift))")
+    public ComplexMatchResult mergeSignExtendByShiftIntoAddSub(BinaryNode op, UnaryNode ext, ValueNode x, ValueNode y, ConstantNode lshift) {
+        assert lshift.getStackKind().isNumericInteger();
+        int shiftAmt = lshift.asJavaConstant().asInt();
+        if (shiftAmt > 4 || shiftAmt < 0) {
+            return null;
+        }
+        ExtendType extType;
+        if (ext instanceof SignExtendNode) {
+            extType = getSignExtendType(((SignExtendNode) ext).getInputBits());
+        } else {
+            extType = getZeroExtendType(((ZeroExtendNode) ext).getInputBits());
+        }
+        return emitExtendedAddSubShift(op, x, y, extType, shiftAmt);
+    }
+
+    @MatchRule("(Add=op x (LeftShift (And y Constant=constant) Constant=lshift))")
+    @MatchRule("(Sub=op x (LeftShift (And y Constant=constant) Constant=lshift))")
+    public ComplexMatchResult mergeShiftDowncastIntoAddSub(BinaryNode op, ValueNode x, ValueNode y, ConstantNode constant, ConstantNode lshift) {
+        assert lshift.getStackKind().isNumericInteger();
+        assert constant.getStackKind().isNumericInteger();
+        int shiftAmt = lshift.asJavaConstant().asInt();
+        long mask = constant.asJavaConstant().asLong();
+        if (shiftAmt > 4 || shiftAmt < 0) {
+            return null;
+        }
+        if (mask != 0xff && mask != 0xffff && mask != 0xffffffffL) {
+            return null;
+        }
+        ExtendType extType = getZeroExtendType(Long.toBinaryString(mask).length());
+        return emitExtendedAddSubShift(op, x, y, extType, shiftAmt);
+    }
+
+    @MatchRule("(Add=op x (RightShift (LeftShift y Constant=shiftConst) Constant=shiftConst))")
+    @MatchRule("(Sub=op x (RightShift (LeftShift y Constant=shiftConst) Constant=shiftConst))")
+    public ComplexMatchResult mergePairShiftIntoAddSub(BinaryNode op, ValueNode x, ValueNode y, ConstantNode shiftConst) {
+        assert shiftConst.getStackKind().isNumericInteger();
+        int shift = shiftConst.asJavaConstant().asInt();
+        if (shift != 16 && shift != 24 && shift != 32 && shift != 48 && shift != 56) {
+            return null;
+        }
+        int extractBits = shift >= 32 ? Long.SIZE - shift : Integer.SIZE - shift;
+        return emitExtendedAddSubShift(op, x, y, getSignExtendType(extractBits), 0);
+    }
+
+    @MatchRule("(Add=op x (LeftShift (RightShift (LeftShift y Constant=shiftConst) Constant=shiftConst) Constant=lshift))")
+    @MatchRule("(Sub=op x (LeftShift (RightShift (LeftShift y Constant=shiftConst) Constant=shiftConst) Constant=lshift))")
+    public ComplexMatchResult mergeShiftedPairShiftIntoAddSub(BinaryNode op, ValueNode x, ValueNode y, ConstantNode shiftConst, ConstantNode lshift) {
+        assert shiftConst.getStackKind().isNumericInteger();
+        int shift = shiftConst.asJavaConstant().asInt();
+        int shiftAmt = lshift.asJavaConstant().asInt();
+        if (shiftAmt > 4 || shiftAmt < 0) {
+            return null;
+        }
+        if (shift != 16 && shift != 24 && shift != 32 && shift != 48 && shift != 56) {
+            return null;
+        }
+        int extractBits = shift >= 32 ? Long.SIZE - shift : Integer.SIZE - shift;
+        return emitExtendedAddSubShift(op, x, y, getSignExtendType(extractBits), shiftAmt);
     }
 
     @MatchRule("(AArch64PointerAdd=addP base ZeroExtend)")
@@ -225,7 +317,7 @@ public ComplexMatchResult extendedPointerAddShift(AArch64PointerAddNode addP) {
                 LIRKind kind = LIRKind.combineDerived(gen.getLIRKind(addP.stamp(NodeView.DEFAULT)),
                                 baseReference, null);
                 Variable result = gen.newVariable(kind);
-                gen.append(new AArch64ArithmeticOp.ExtendedAddShiftOp(result, x, moveSp(y),
+                gen.append(new AArch64ArithmeticOp.ExtendedAddSubShiftOp(AArch64ArithmeticOp.ADD, result, x, moveSp(y),
                                 extendType, shiftNum));
                 return result;
             };
@@ -469,6 +561,32 @@ public ComplexMatchResult elideL2IForBinary(BinaryNode binary, NarrowNode narrow
                         resultKind, op, commutative, operand(src2), operand(src1));
     }
 
+    @MatchRule("(Add=op x (And y Constant=constant))")
+    @MatchRule("(Sub=op x (And y Constant=constant))")
+    public ComplexMatchResult mergeDowncastIntoAddSub(BinaryNode op, ValueNode x, ValueNode y, ConstantNode constant) {
+        assert constant.getStackKind().isNumericInteger();
+        long mask = constant.asJavaConstant().asLong();
+        if (mask != 0xff && mask != 0xffff && mask != 0xffffffffL) {
+            return null;
+        }
+        ExtendType extType = getZeroExtendType(Long.toBinaryString(mask).length());
+        return emitExtendedAddSubShift(op, x, y, extType, 0);
+    }
+
+    @MatchRule("(Add=op x (SignExtend=ext y))")
+    @MatchRule("(Sub=op x (SignExtend=ext y))")
+    @MatchRule("(Add=op x (ZeroExtend=ext y))")
+    @MatchRule("(Sub=op x (ZeroExtend=ext y))")
+    public ComplexMatchResult mergeSignExtendIntoAddSub(BinaryNode op, UnaryNode ext, ValueNode x, ValueNode y) {
+        ExtendType extType;
+        if (ext instanceof SignExtendNode) {
+            extType = getSignExtendType(((SignExtendNode) ext).getInputBits());
+        } else {
+            extType = getZeroExtendType(((ZeroExtendNode) ext).getInputBits());
+        }
+        return emitExtendedAddSubShift(op, x, y, extType, 0);
+    }
+
     @MatchRule("(Negate=unary (Narrow=narrow value))")
     @MatchRule("(Not=unary (Narrow=narrow value))")
     public ComplexMatchResult elideL2IForUnary(UnaryNode unary, NarrowNode narrow) {
diff --git a/compiler/src/org.graalvm.compiler.lir.aarch64/src/org/graalvm/compiler/lir/aarch64/AArch64ArithmeticOp.java b/compiler/src/org.graalvm.compiler.lir.aarch64/src/org/graalvm/compiler/lir/aarch64/AArch64ArithmeticOp.java
index df49a1ec1590e..9f4b0bfc52907 100644
--- a/compiler/src/org.graalvm.compiler.lir.aarch64/src/org/graalvm/compiler/lir/aarch64/AArch64ArithmeticOp.java
+++ b/compiler/src/org.graalvm.compiler.lir.aarch64/src/org/graalvm/compiler/lir/aarch64/AArch64ArithmeticOp.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -461,8 +461,9 @@ public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) {
         }
     }
 
-    public static class ExtendedAddShiftOp extends AArch64LIRInstruction {
-        private static final LIRInstructionClass<ExtendedAddShiftOp> TYPE = LIRInstructionClass.create(ExtendedAddShiftOp.class);
+    public static class ExtendedAddSubShiftOp extends AArch64LIRInstruction {
+        private static final LIRInstructionClass<ExtendedAddSubShiftOp> TYPE = LIRInstructionClass.create(ExtendedAddSubShiftOp.class);
+        @Opcode private final AArch64ArithmeticOp op;
         @Def(REG) protected AllocatableValue result;
         @Use(REG) protected AllocatableValue src1;
         @Use(REG) protected AllocatableValue src2;
@@ -475,8 +476,9 @@ public static class ExtendedAddShiftOp extends AArch64LIRInstruction {
          * @param extendType defines how src2 is extended to the same size as src1.
          * @param shiftAmt must be in range 0 to 4.
          */
-        public ExtendedAddShiftOp(AllocatableValue result, AllocatableValue src1, AllocatableValue src2, AArch64Assembler.ExtendType extendType, int shiftAmt) {
+        public ExtendedAddSubShiftOp(AArch64ArithmeticOp op, AllocatableValue result, AllocatableValue src1, AllocatableValue src2, AArch64Assembler.ExtendType extendType, int shiftAmt) {
             super(TYPE);
+            this.op = op;
             this.result = result;
             this.src1 = src1;
             this.src2 = src2;
@@ -487,7 +489,16 @@ public ExtendedAddShiftOp(AllocatableValue result, AllocatableValue src1, Alloca
         @Override
         public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) {
             int size = result.getPlatformKind().getSizeInBytes() * Byte.SIZE;
-            masm.add(size, asRegister(result), asRegister(src1), asRegister(src2), extendType, shiftAmt);
+            switch (op) {
+                case ADD:
+                    masm.add(size, asRegister(result), asRegister(src1), asRegister(src2), extendType, shiftAmt);
+                    break;
+                case SUB:
+                    masm.sub(size, asRegister(result), asRegister(src1), asRegister(src2), extendType, shiftAmt);
+                    break;
+                default:
+                    throw GraalError.shouldNotReachHere();
+            }
         }
     }