[llvm] [AArch64] Add CodeGen support for FEAT_CPA (PR #105669)

Rodolfo Wottrich via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 22 07:22:51 PDT 2024


https://github.com/rgwott created https://github.com/llvm/llvm-project/pull/105669

CPA stands for Checked Pointer Arithmetic and is part of the 2023 MTE architecture extensions for A-profile.
The new CPA instructions perform regular pointer arithmetic (such as base register + offset) but check for overflow in the most significant bits of the result, enhancing security by detecting address tampering.

In this patch we intend to capture the semantics of pointer arithmetic when it is not folded into loads/stores, then generate the appropriate CPA instructions. In order to preserve pointer arithmetic semantics through the backend, we add the PTRADD SelectionDAG node type.

The PTRADD node and respective visitPTRADD() function are adapted from the CHERI/Morello LLVM tree.

Mode details about the CPA extension can be found at:

- https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/arm-a-profile-architecture-developments-2023
- https://developer.arm.com/documentation/ddi0602/2023-09/

This PR follows #79569.

>From b26f61578c033eb6be0484e7346970176caafce2 Mon Sep 17 00:00:00 2001
From: Rodolfo Wottrich <rodolfo.wottrich at arm.com>
Date: Thu, 22 Aug 2024 15:17:00 +0100
Subject: [PATCH] [AArch64] Add CodeGen support for FEAT_CPA

CPA stands for Checked Pointer Arithmetic and is part of the 2023 MTE
architecture extensions for A-profile.
The new CPA instructions perform regular pointer arithmetic (such as
base register + offset) but check for overflow in the most significant
bits of the result, enhancing security by detecting address tampering.

In this patch we intend to capture the semantics of pointer arithmetic
when it is not folded into loads/stores, then generate the appropriate
CPA instructions. In order to preserve pointer arithmetic semantics
through the backend, we add the PTRADD SelectionDAG node type.

The PTRADD node and respective visitPTRADD() function are adapted from
the CHERI/Morello LLVM tree.

Mode details about the CPA extension can be found at:

- https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/arm-a-profile-architecture-developments-2023
- https://developer.arm.com/documentation/ddi0602/2023-09/

This PR follows #79569.
---
 llvm/include/llvm/CodeGen/ISDOpcodes.h        |   4 +
 llvm/include/llvm/Target/TargetMachine.h      |   5 +
 .../include/llvm/Target/TargetSelectionDAG.td |   4 +-
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 101 +++-
 llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp |  21 +-
 .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp |  10 +-
 .../SelectionDAG/SelectionDAGBuilder.cpp      |  44 +-
 .../SelectionDAG/SelectionDAGDumper.cpp       |   1 +
 llvm/lib/Target/AArch64/AArch64InstrInfo.td   |  20 +
 .../Target/AArch64/AArch64TargetMachine.cpp   |   4 +
 .../lib/Target/AArch64/AArch64TargetMachine.h |   4 +
 .../GISel/AArch64InstructionSelector.cpp      |   4 +
 llvm/test/CodeGen/AArch64/cpa-globalisel.ll   | 455 ++++++++++++++++++
 llvm/test/CodeGen/AArch64/cpa-selectiondag.ll | 449 +++++++++++++++++
 14 files changed, 1104 insertions(+), 22 deletions(-)
 create mode 100644 llvm/test/CodeGen/AArch64/cpa-globalisel.ll
 create mode 100644 llvm/test/CodeGen/AArch64/cpa-selectiondag.ll

diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h
index 86ff2628975942..305b3349307777 100644
--- a/llvm/include/llvm/CodeGen/ISDOpcodes.h
+++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h
@@ -1452,6 +1452,10 @@ enum NodeType {
   // Outputs: [rv], output chain, glue
   PATCHPOINT,
 
+  // PTRADD represents pointer arithmetic semantics, for those targets which
+  // benefit from that information.
+  PTRADD,
+
 // Vector Predication
 #define BEGIN_REGISTER_VP_SDNODE(VPSDID, ...) VPSDID,
 #include "llvm/IR/VPIntrinsics.def"
diff --git a/llvm/include/llvm/Target/TargetMachine.h b/llvm/include/llvm/Target/TargetMachine.h
index c3e9d41315f617..26425fced52528 100644
--- a/llvm/include/llvm/Target/TargetMachine.h
+++ b/llvm/include/llvm/Target/TargetMachine.h
@@ -434,6 +434,11 @@ class TargetMachine {
       function_ref<void(std::unique_ptr<Module> MPart)> ModuleCallback) {
     return false;
   }
+
+  /// True if target has some particular form of dealing with pointer arithmetic
+  /// semantics. False if pointer arithmetic should not be preserved for passes
+  /// such as instruction selection, and can fallback to regular arithmetic.
+  virtual bool shouldPreservePtrArith(const Function &F) const { return false; }
 };
 
 /// This class describes a target machine that is implemented with the LLVM
diff --git a/llvm/include/llvm/Target/TargetSelectionDAG.td b/llvm/include/llvm/Target/TargetSelectionDAG.td
index 172deffbd31771..aeb27ccf921a4b 100644
--- a/llvm/include/llvm/Target/TargetSelectionDAG.td
+++ b/llvm/include/llvm/Target/TargetSelectionDAG.td
@@ -109,7 +109,7 @@ def SDTOther  : SDTypeProfile<1, 0, [SDTCisVT<0, OtherVT>]>; // for 'vt'.
 def SDTUNDEF  : SDTypeProfile<1, 0, []>;                     // for 'undef'.
 def SDTUnaryOp  : SDTypeProfile<1, 1, []>;                   // for bitconvert.
 
-def SDTPtrAddOp : SDTypeProfile<1, 2, [     // ptradd
+def SDTPtrAddOp : SDTypeProfile<1, 2, [  // ptradd
   SDTCisSameAs<0, 1>, SDTCisInt<2>, SDTCisPtrTy<1>
 ]>;
 def SDTIntBinOp : SDTypeProfile<1, 2, [     // add, and, or, xor, udiv, etc.
@@ -390,7 +390,7 @@ def tblockaddress: SDNode<"ISD::TargetBlockAddress",  SDTPtrLeaf, [],
 
 def add        : SDNode<"ISD::ADD"       , SDTIntBinOp   ,
                         [SDNPCommutative, SDNPAssociative]>;
-def ptradd     : SDNode<"ISD::ADD"       , SDTPtrAddOp, []>;
+def ptradd     : SDNode<"ISD::PTRADD"    , SDTPtrAddOp, []>;
 def sub        : SDNode<"ISD::SUB"       , SDTIntBinOp>;
 def mul        : SDNode<"ISD::MUL"       , SDTIntBinOp,
                         [SDNPCommutative, SDNPAssociative]>;
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 11935cbc309f01..16a16e1d702c29 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -416,7 +416,9 @@ namespace {
     SDValue visitMERGE_VALUES(SDNode *N);
     SDValue visitADD(SDNode *N);
     SDValue visitADDLike(SDNode *N);
-    SDValue visitADDLikeCommutative(SDValue N0, SDValue N1, SDNode *LocReference);
+    SDValue visitADDLikeCommutative(SDValue N0, SDValue N1,
+                                    SDNode *LocReference);
+    SDValue visitPTRADD(SDNode *N);
     SDValue visitSUB(SDNode *N);
     SDValue visitADDSAT(SDNode *N);
     SDValue visitSUBSAT(SDNode *N);
@@ -1082,7 +1084,7 @@ bool DAGCombiner::reassociationCanBreakAddressingModePattern(unsigned Opc,
   // (load/store (add, (add, x, y), offset2)) ->
   // (load/store (add, (add, x, offset2), y)).
 
-  if (N0.getOpcode() != ISD::ADD)
+  if (N0.getOpcode() != ISD::ADD && N0.getOpcode() != ISD::PTRADD)
     return false;
 
   // Check for vscale addressing modes.
@@ -1833,6 +1835,7 @@ SDValue DAGCombiner::visit(SDNode *N) {
   case ISD::TokenFactor:        return visitTokenFactor(N);
   case ISD::MERGE_VALUES:       return visitMERGE_VALUES(N);
   case ISD::ADD:                return visitADD(N);
+  case ISD::PTRADD:             return visitPTRADD(N);
   case ISD::SUB:                return visitSUB(N);
   case ISD::SADDSAT:
   case ISD::UADDSAT:            return visitADDSAT(N);
@@ -2349,7 +2352,7 @@ static bool canFoldInAddressingMode(SDNode *N, SDNode *Use, SelectionDAG &DAG,
   }
 
   TargetLowering::AddrMode AM;
-  if (N->getOpcode() == ISD::ADD) {
+  if (N->getOpcode() == ISD::ADD || N->getOpcode() == ISD::PTRADD) {
     AM.HasBaseReg = true;
     ConstantSDNode *Offset = dyn_cast<ConstantSDNode>(N->getOperand(1));
     if (Offset)
@@ -2578,6 +2581,98 @@ SDValue DAGCombiner::foldSubToAvg(SDNode *N, const SDLoc &DL) {
   return SDValue();
 }
 
+/// Try to fold a pointer arithmetic node.
+/// This needs to be done separately from normal addition, because pointer
+/// addition is not commutative.
+/// This function was adapted from DAGCombiner::visitPTRADD() from the Morello
+/// project, which is based on CHERI.
+SDValue DAGCombiner::visitPTRADD(SDNode *N) {
+  SDValue N0 = N->getOperand(0);
+  SDValue N1 = N->getOperand(1);
+  EVT PtrVT = N0.getValueType();
+  EVT IntVT = N1.getValueType();
+  SDLoc DL(N);
+
+  // fold (ptradd undef, y) -> undef
+  if (N0.isUndef())
+    return N0;
+
+  // fold (ptradd x, undef) -> undef
+  if (N1.isUndef())
+    return DAG.getUNDEF(PtrVT);
+
+  // fold (ptradd x, 0) -> x
+  if (isNullConstant(N1))
+    return N0;
+
+  if (N0.getOpcode() == ISD::PTRADD &&
+      !reassociationCanBreakAddressingModePattern(ISD::PTRADD, DL, N, N0, N1)) {
+    SDValue X = N0.getOperand(0);
+    SDValue Y = N0.getOperand(1);
+    SDValue Z = N1;
+    bool N0OneUse = N0.hasOneUse();
+    bool YIsConstant = DAG.isConstantIntBuildVectorOrConstantInt(Y);
+    bool ZIsConstant = DAG.isConstantIntBuildVectorOrConstantInt(Z);
+
+    // (ptradd (ptradd x, y), z) -> (ptradd (ptradd x, z), y) if:
+    //   * (ptradd x, y) has one use; and
+    //   * y is a constant; and
+    //   * z is not a constant.
+    // Serves to expose constant y for subsequent folding.
+    if (N0OneUse && YIsConstant && !ZIsConstant) {
+      SDValue Add = DAG.getNode(ISD::PTRADD, DL, IntVT, {X, Z});
+
+      // Calling visit() can replace the Add node with ISD::DELETED_NODE if
+      // there aren't any users, so keep a handle around whilst we visit it.
+      HandleSDNode ADDHandle(Add);
+
+      SDValue VisitedAdd = visit(Add.getNode());
+      if (VisitedAdd) {
+        // If visit() returns the same node, it means the SDNode was RAUW'd, and
+        // therefore we have to load the new value to perform the checks whether
+        // the reassociation fold is profitable.
+        if (VisitedAdd.getNode() == Add.getNode())
+          Add = ADDHandle.getValue();
+        else
+          Add = VisitedAdd;
+      }
+
+      return DAG.getMemBasePlusOffset(Add, Y, DL, SDNodeFlags());
+    }
+
+    bool ZOneUse = Z.hasOneUse();
+
+    // (ptradd (ptradd x, y), z) -> (ptradd x, (add y, z)) if:
+    //   * x is a null pointer; or
+    //   * y is a constant and z has one use; or
+    //   * y is a constant and (ptradd x, y) has one use; or
+    //   * (ptradd x, y) and z have one use and z is not a constant.
+    if (isNullConstant(X) || (YIsConstant && ZOneUse) ||
+        (YIsConstant && N0OneUse) || (N0OneUse && ZOneUse && !ZIsConstant)) {
+      SDValue Add = DAG.getNode(ISD::ADD, DL, IntVT, {Y, Z});
+
+      // Calling visit() can replace the Add node with ISD::DELETED_NODE if
+      // there aren't any users, so keep a handle around whilst we visit it.
+      HandleSDNode ADDHandle(Add);
+
+      SDValue VisitedAdd = visit(Add.getNode());
+      if (VisitedAdd) {
+        // If visit() returns the same node, it means the SDNode was RAUW'd, and
+        // therefore we have to load the new value to perform the checks whether
+        // the reassociation fold is profitable.
+        if (VisitedAdd.getNode() == Add.getNode())
+          Add = ADDHandle.getValue();
+        else
+          Add = VisitedAdd;
+      }
+
+      return DAG.getMemBasePlusOffset(X, Add, DL, SDNodeFlags());
+    }
+  }
+
+  return SDValue();
+}
+
 /// Try to fold a 'not' shifted sign-bit with add/sub with constant operand into
 /// a shift and add with a different constant.
 static SDValue foldAddSubOfSignBit(SDNode *N, const SDLoc &DL,
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
index 74e3a898569bea..28e0bdbb549c66 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
@@ -4069,8 +4069,14 @@ bool SelectionDAGLegalize::ExpandNode(SDNode *Node) {
     else
       Index = DAG.getNode(ISD::MUL, dl, Index.getValueType(), Index,
                           DAG.getConstant(EntrySize, dl, Index.getValueType()));
-    SDValue Addr = DAG.getNode(ISD::ADD, dl, Index.getValueType(),
-                               Index, Table);
+    SDValue Addr;
+    if (!DAG.getTarget().shouldPreservePtrArith(
+            DAG.getMachineFunction().getFunction())) {
+      Addr = DAG.getNode(ISD::ADD, dl, Index.getValueType(), Index, Table);
+    } else {
+      // PTRADD always takes the pointer first, so the operands are commuted
+      Addr = DAG.getNode(ISD::PTRADD, dl, Index.getValueType(), Table, Index);
+    }
 
     EVT MemVT = EVT::getIntegerVT(*DAG.getContext(), EntrySize * 8);
     SDValue LD = DAG.getExtLoad(
@@ -4081,8 +4087,15 @@ bool SelectionDAGLegalize::ExpandNode(SDNode *Node) {
       // For PIC, the sequence is:
       // BRIND(load(Jumptable + index) + RelocBase)
       // RelocBase can be JumpTable, GOT or some sort of global base.
-      Addr = DAG.getNode(ISD::ADD, dl, PTy, Addr,
-                          TLI.getPICJumpTableRelocBase(Table, DAG));
+      if (!DAG.getTarget().shouldPreservePtrArith(
+              DAG.getMachineFunction().getFunction())) {
+        Addr = DAG.getNode(ISD::ADD, dl, PTy, Addr,
+                           TLI.getPICJumpTableRelocBase(Table, DAG));
+      } else {
+        // PTRADD always takes the pointer first, so the operands are commuted
+        Addr = DAG.getNode(ISD::PTRADD, dl, PTy,
+                           TLI.getPICJumpTableRelocBase(Table, DAG), Addr);
+      }
     }
 
     Tmp1 = TLI.expandIndirectJTBranch(dl, LD.getValue(1), Addr, JTI, DAG);
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 27675dce70c260..dd746234e6ad83 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -5387,7 +5387,8 @@ bool SelectionDAG::isADDLike(SDValue Op, bool NoWrap) const {
 
 bool SelectionDAG::isBaseWithConstantOffset(SDValue Op) const {
   return Op.getNumOperands() == 2 && isa<ConstantSDNode>(Op.getOperand(1)) &&
-         (Op.getOpcode() == ISD::ADD || isADDLike(Op));
+         (Op.getOpcode() == ISD::ADD || Op.getOpcode() == ISD::PTRADD ||
+          isADDLike(Op));
 }
 
 bool SelectionDAG::isKnownNeverNaN(SDValue Op, bool SNaN, unsigned Depth) const {
@@ -7785,7 +7786,12 @@ SDValue SelectionDAG::getMemBasePlusOffset(SDValue Ptr, SDValue Offset,
                                            const SDNodeFlags Flags) {
   assert(Offset.getValueType().isInteger());
   EVT BasePtrVT = Ptr.getValueType();
-  return getNode(ISD::ADD, DL, BasePtrVT, Ptr, Offset, Flags);
+  if (!this->getTarget().shouldPreservePtrArith(
+          this->getMachineFunction().getFunction())) {
+    return getNode(ISD::ADD, DL, BasePtrVT, Ptr, Offset, Flags);
+  } else {
+    return getNode(ISD::PTRADD, DL, BasePtrVT, Ptr, Offset, Flags);
+  }
 }
 
 /// Returns true if memcpy source is constant data.
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 60dcb118542785..f6e797dee395a4 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -4293,6 +4293,12 @@ void SelectionDAGBuilder::visitGetElementPtr(const User &I) {
   SDLoc dl = getCurSDLoc();
   auto &TLI = DAG.getTargetLoweringInfo();
   GEPNoWrapFlags NW = cast<GEPOperator>(I).getNoWrapFlags();
+  unsigned int AddOpcode = ISD::PTRADD;
+
+  if (!DAG.getTarget().shouldPreservePtrArith(
+          DAG.getMachineFunction().getFunction())) {
+    AddOpcode = ISD::ADD;
+  }
 
   // Normalize Vector GEP - all scalar operands should be converted to the
   // splat vector.
@@ -4324,7 +4330,7 @@ void SelectionDAGBuilder::visitGetElementPtr(const User &I) {
             (int64_t(Offset) >= 0 && NW.hasNoUnsignedSignedWrap()))
           Flags.setNoUnsignedWrap(true);
 
-        N = DAG.getNode(ISD::ADD, dl, N.getValueType(), N,
+        N = DAG.getNode(AddOpcode, dl, N.getValueType(), N,
                         DAG.getConstant(Offset, dl, N.getValueType()), Flags);
       }
     } else {
@@ -4368,7 +4374,7 @@ void SelectionDAGBuilder::visitGetElementPtr(const User &I) {
 
         OffsVal = DAG.getSExtOrTrunc(OffsVal, dl, N.getValueType());
 
-        N = DAG.getNode(ISD::ADD, dl, N.getValueType(), N, OffsVal, Flags);
+        N = DAG.getNode(AddOpcode, dl, N.getValueType(), N, OffsVal, Flags);
         continue;
       }
 
@@ -4411,8 +4417,7 @@ void SelectionDAGBuilder::visitGetElementPtr(const User &I) {
         }
       }
 
-      N = DAG.getNode(ISD::ADD, dl,
-                      N.getValueType(), N, IdxN);
+      N = DAG.getNode(AddOpcode, dl, N.getValueType(), N, IdxN);
     }
   }
 
@@ -4473,8 +4478,15 @@ void SelectionDAGBuilder::visitAlloca(const AllocaInst &I) {
   // an address inside an alloca.
   SDNodeFlags Flags;
   Flags.setNoUnsignedWrap(true);
-  AllocSize = DAG.getNode(ISD::ADD, dl, AllocSize.getValueType(), AllocSize,
-                          DAG.getConstant(StackAlignMask, dl, IntPtr), Flags);
+  if (DAG.getTarget().shouldPreservePtrArith(
+          DAG.getMachineFunction().getFunction())) {
+    AllocSize = DAG.getNode(ISD::PTRADD, dl, AllocSize.getValueType(),
+                            DAG.getConstant(StackAlignMask, dl, IntPtr),
+                            AllocSize, Flags);
+  } else {
+    AllocSize = DAG.getNode(ISD::ADD, dl, AllocSize.getValueType(), AllocSize,
+                            DAG.getConstant(StackAlignMask, dl, IntPtr), Flags);
+  }
 
   // Mask out the low bits for alignment purposes.
   AllocSize = DAG.getNode(ISD::AND, dl, AllocSize.getValueType(), AllocSize,
@@ -9071,8 +9083,13 @@ bool SelectionDAGBuilder::visitMemPCpyCall(const CallInst &I) {
   Size = DAG.getSExtOrTrunc(Size, sdl, Dst.getValueType());
 
   // Adjust return pointer to point just past the last dst byte.
-  SDValue DstPlusSize = DAG.getNode(ISD::ADD, sdl, Dst.getValueType(),
-                                    Dst, Size);
+  unsigned int AddOpcode = ISD::PTRADD;
+  if (!DAG.getTarget().shouldPreservePtrArith(
+          DAG.getMachineFunction().getFunction())) {
+    AddOpcode = ISD::ADD;
+  }
+  SDValue DstPlusSize =
+      DAG.getNode(AddOpcode, sdl, Dst.getValueType(), Dst, Size);
   setValue(&I, DstPlusSize);
   return true;
 }
@@ -11169,9 +11186,14 @@ TargetLowering::LowerCallTo(TargetLowering::CallLoweringInfo &CLI) const {
     MachineFunction &MF = CLI.DAG.getMachineFunction();
     Align HiddenSRetAlign = MF.getFrameInfo().getObjectAlign(DemoteStackIdx);
     for (unsigned i = 0; i < NumValues; ++i) {
-      SDValue Add = CLI.DAG.getNode(ISD::ADD, CLI.DL, PtrVT, DemoteStackSlot,
-                                    CLI.DAG.getConstant(Offsets[i], CLI.DL,
-                                                        PtrVT), Flags);
+      unsigned int AddOpcode = ISD::PTRADD;
+      if (!CLI.DAG.getTarget().shouldPreservePtrArith(
+              CLI.DAG.getMachineFunction().getFunction())) {
+        AddOpcode = ISD::ADD;
+      }
+      SDValue Add = CLI.DAG.getNode(
+          AddOpcode, CLI.DL, PtrVT, DemoteStackSlot,
+          CLI.DAG.getConstant(Offsets[i], CLI.DL, PtrVT), Flags);
       SDValue L = CLI.DAG.getLoad(
           RetTys[i], CLI.DL, CLI.Chain, Add,
           MachinePointerInfo::getFixedStack(CLI.DAG.getMachineFunction(),
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
index 001f782f209fdb..5e727df0fcab48 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
@@ -256,6 +256,7 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const {
 
   // Binary operators
   case ISD::ADD:                        return "add";
+  case ISD::PTRADD:                     return "ptradd";
   case ISD::SUB:                        return "sub";
   case ISD::MUL:                        return "mul";
   case ISD::MULHU:                      return "mulhu";
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index ec225a5b234a26..0fc31cf9120838 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -10223,6 +10223,26 @@ let Predicates = [HasCPA] in {
   // Scalar multiply-add/subtract
   def MADDPT : MulAccumCPA<0, "maddpt">;
   def MSUBPT : MulAccumCPA<1, "msubpt">;
+
+  // Rules to use CPA instructions in pointer arithmetic patterns which are not
+  // folded into loads/stores. The AddedComplexity serves to help supersede
+  // other simpler (non-CPA) patterns and make sure CPA is used instead.
+  let AddedComplexity = 20 in {
+    def : Pat<(ptradd GPR64sp:$Rn, GPR64sp:$Rm),
+              (ADDPT_shift GPR64sp:$Rn, GPR64sp:$Rm, (i32 0))>;
+    def : Pat<(ptradd GPR64sp:$Rn, (shl GPR64sp:$Rm, (i64 imm0_7:$imm))),
+              (ADDPT_shift GPR64sp:$Rn, GPR64sp:$Rm,
+                           (i32 (trunc_imm imm0_7:$imm)))>;
+    def : Pat<(ptradd GPR64sp:$Rn, (ineg GPR64sp:$Rm)),
+              (SUBPT_shift GPR64sp:$Rn, GPR64sp:$Rm, (i32 0))>;
+    def : Pat<(ptradd GPR64sp:$Rn, (ineg (shl GPR64sp:$Rm, (i64 imm0_7:$imm)))),
+              (SUBPT_shift GPR64sp:$Rn, GPR64sp:$Rm,
+                           (i32 (trunc_imm imm0_7:$imm)))>;
+    def : Pat<(ptradd GPR64:$Ra, (mul GPR64:$Rn, GPR64:$Rm)),
+              (MADDPT GPR64:$Rn, GPR64:$Rm, GPR64:$Ra)>;
+    def : Pat<(ptradd GPR64:$Ra, (mul GPR64:$Rn, (ineg GPR64:$Rm))),
+              (MSUBPT GPR64:$Rn, GPR64:$Rm, GPR64:$Ra)>;
+  }
 }
 
 def round_v4fp32_to_v4bf16 :
diff --git a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp
index bd5684a287381a..3dfc90380ccb86 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp
@@ -920,3 +920,7 @@ bool AArch64TargetMachine::parseMachineFunctionInfo(
   MF.getInfo<AArch64FunctionInfo>()->initializeBaseYamlFields(YamlMFI);
   return false;
 }
+
+bool AArch64TargetMachine::shouldPreservePtrArith(const Function &F) const {
+  return getSubtargetImpl(F)->hasCPA();
+}
\ No newline at end of file
diff --git a/llvm/lib/Target/AArch64/AArch64TargetMachine.h b/llvm/lib/Target/AArch64/AArch64TargetMachine.h
index 1a470ca87127ce..c161223fe7fc10 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetMachine.h
+++ b/llvm/lib/Target/AArch64/AArch64TargetMachine.h
@@ -69,6 +69,10 @@ class AArch64TargetMachine : public LLVMTargetMachine {
     return true;
   }
 
+  /// In AArch64, true if FEAT_CPA is present. Allows pointer arithmetic
+  /// semantics to be preserved for instruction selection.
+  bool shouldPreservePtrArith(const Function &F) const override;
+
 private:
   bool isLittle;
 };
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
index e9e6b6cb68d0d1..158a4b2d0d1577 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
@@ -2091,6 +2091,10 @@ bool AArch64InstructionSelector::preISelLower(MachineInstr &I) {
     return Changed;
   }
   case TargetOpcode::G_PTR_ADD:
+    // If Checked Pointer Arithmetic (FEAT_CPA) is present, preserve the pointer
+    // arithmetic semantics instead of falling back to regular arithmetic.
+    if (TM.shouldPreservePtrArith(MF.getFunction()))
+      return false;
     return convertPtrAddToAdd(I, MRI);
   case TargetOpcode::G_LOAD: {
     // For scalar loads of pointers, we try to convert the dest type from p0
diff --git a/llvm/test/CodeGen/AArch64/cpa-globalisel.ll b/llvm/test/CodeGen/AArch64/cpa-globalisel.ll
new file mode 100644
index 00000000000000..ffbe4b358ca5a4
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/cpa-globalisel.ll
@@ -0,0 +1,455 @@
+; RUN: llc -mtriple=aarch64 -verify-machineinstrs --mattr=+cpa -O0 -global-isel=1 -global-isel-abort=1 %s -o - 2>&1 | FileCheck %s --check-prefixes=CHECK-CPA-O0
+; RUN: llc -mtriple=aarch64 -verify-machineinstrs --mattr=+cpa -O3 -global-isel=1 -global-isel-abort=1 %s -o - 2>&1 | FileCheck %s --check-prefixes=CHECK-CPA-O3
+; RUN: llc -mtriple=aarch64 -verify-machineinstrs --mattr=-cpa -O0 -global-isel=1 -global-isel-abort=1 %s -o - 2>&1 | FileCheck %s --check-prefixes=CHECK-NOCPA-O0
+; RUN: llc -mtriple=aarch64 -verify-machineinstrs --mattr=-cpa -O3 -global-isel=1 -global-isel-abort=1 %s -o - 2>&1 | FileCheck %s --check-prefixes=CHECK-NOCPA-O3
+
+%struct.my_type = type { i64, i64 }
+%struct.my_type2 = type { i64, i64, i64, i64, i64, i64 }
+
+ at array = external dso_local global [10 x %struct.my_type], align 8
+ at array2 = external dso_local global [10 x %struct.my_type2], align 8
+
+define void @addpt1(i64 %index, i64 %arg) {
+; CHECK-CPA-O0-LABEL:    addpt1:
+; CHECK-CPA-O0:          addpt	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, lsl #4
+; CHECK-CPA-O0:          str	x{{[0-9]+}}, [[[REG1]], #8]
+;
+; CHECK-CPA-O3-LABEL:    addpt1:
+; CHECK-CPA-O3:          addpt	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, lsl #4
+; CHECK-CPA-O3:          str	x{{[0-9]+}}, [[[REG1]], #8]
+;
+; CHECK-NOCPA-O0-LABEL:  addpt1:
+; CHECK-NOCPA-O0:        add	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, lsl #4
+; CHECK-NOCPA-O0:        str	x{{[0-9]+}}, [[[REG1]], #8]
+;
+; CHECK-NOCPA-O3-LABEL:  addpt1:
+; CHECK-NOCPA-O3:        add	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, lsl #4
+; CHECK-NOCPA-O3:        str	x{{[0-9]+}}, [[[REG1]], #8]
+entry:
+  %e2 = getelementptr inbounds %struct.my_type, ptr @array, i64 %index, i32 1
+  store i64 %arg, ptr %e2, align 8
+  ret void
+}
+
+define void @maddpt1(i32 %pos, ptr %val) {
+; CHECK-CPA-O0-LABEL:    maddpt1:
+; CHECK-CPA-O0:          maddpt	x0, x{{[0-9]+}}, x{{[0-9]+}}, x{{[0-9]+}}
+; CHECK-CPA-O0:          b	memcpy
+;
+; CHECK-CPA-O3-LABEL:    maddpt1:
+; CHECK-CPA-O3:          maddpt	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, x{{[0-9]+}}
+; CHECK-CPA-O3:          str	q{{[0-9]+}}, [[[REG1]]]
+; CHECK-CPA-O3:          str	q{{[0-9]+}}, [[[REG1]], #16]
+; CHECK-CPA-O3:          str	q{{[0-9]+}}, [[[REG1]], #32]
+;
+; CHECK-NOCPA-O0-LABEL:  maddpt1:
+; CHECK-NOCPA-O0:        smaddl	x0, w{{[0-9]+}}, w{{[0-9]+}}, x{{[0-9]+}}
+; CHECK-NOCPA-O0:        b	memcpy
+;
+; CHECK-NOCPA-O3-LABEL:  maddpt1:
+; CHECK-NOCPA-O3:        smaddl	[[REG1:x[0-9]+]], w{{[0-9]+}}, w{{[0-9]+}}, x{{[0-9]+}}
+; CHECK-NOCPA-O3:        str	q{{[0-9]+}}, [[[REG1]]]
+; CHECK-NOCPA-O3:        str	q{{[0-9]+}}, [[[REG1]], #16]
+; CHECK-NOCPA-O3:        str	q{{[0-9]+}}, [[[REG1]], #32]
+entry:
+  %idxprom = sext i32 %pos to i64
+  %arrayidx = getelementptr inbounds [10 x %struct.my_type2], ptr @array2, i64 0, i64 %idxprom
+  tail call void @llvm.memcpy.p0.p0.i64(ptr align 8 dereferenceable(48) %arrayidx, ptr align 8 dereferenceable(48) %val, i64 48, i1 false)
+  ret void
+}
+
+define void @msubpt1(i32 %index, i32 %elem) {
+; CHECK-CPA-O0-LABEL:    msubpt1:
+; CHECK-CPA-O0:          addpt	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}
+; CHECK-CPA-O0:          msubpt	x0, x{{[0-9]+}}, x{{[0-9]+}}, [[REG1]]
+; CHECK-CPA-O0:          addpt	x1, x{{[0-9]+}}, x{{[0-9]+}}
+; CHECK-CPA-O0:          b	memcpy
+;
+; CHECK-CPA-O3-LABEL:    msubpt1:
+; CHECK-CPA-O3:          msubpt	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, x{{[0-9]+}}
+; CHECK-CPA-O3:          str	q{{[0-9]+}}, [[[REG1]], #192]
+; CHECK-CPA-O3:          str	q{{[0-9]+}}, [[[REG1]], #208]
+; CHECK-CPA-O3:          str	q{{[0-9]+}}, [[[REG1]], #224]
+;
+; CHECK-NOCPA-O0-LABEL:  msubpt1:
+; CHECK-NOCPA-O0:        mneg	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}
+; CHECK-NOCPA-O0:        add	x0, x{{[0-9]+}}, [[REG1]]
+; CHECK-NOCPA-O0:        b	memcpy
+;
+; CHECK-NOCPA-O3-LABEL:  msubpt1:
+; CHECK-NOCPA-O3:        mneg	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}
+; CHECK-NOCPA-O3:        add	[[REG2:x[0-9]+]], x{{[0-9]+}}, [[REG1]]
+; CHECK-NOCPA-O3:        str	q{{[0-9]+}}, [[[REG1]], #192]
+; CHECK-NOCPA-O3:        str	q{{[0-9]+}}, [[[REG1]], #208]
+; CHECK-NOCPA-O3:        str	q{{[0-9]+}}, [[[REG1]], #224]
+entry:
+  %idx.ext = sext i32 %index to i64
+  %idx.neg = sub nsw i64 0, %idx.ext
+  %add.ptr = getelementptr inbounds %struct.my_type2, ptr getelementptr inbounds ([10 x %struct.my_type2], ptr @array2, i64 0, i64 6), i64 %idx.neg
+  tail call void @llvm.memcpy.p0.p0.i64(ptr align 8 dereferenceable(48) %add.ptr, ptr align 8 dereferenceable(48) getelementptr inbounds ([10 x %struct.my_type2], ptr @array2, i64 0, i64 2), i64 48, i1 false), !tbaa.struct !6
+  ret void
+}
+
+define void @subpt1(i32 %index, i32 %elem) {
+; CHECK-CPA-O0-LABEL:    subpt1:
+; CHECK-CPA-O0:          addpt	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}
+; CHECK-CPA-O0:          str	q{{[0-9]+}}, [[[REG1]], x{{[0-9]+}}, lsl #4]
+;
+; CHECK-CPA-O3-LABEL:    subpt1:
+; CHECK-CPA-O3:          addpt	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, lsl #4
+; CHECK-CPA-O3:          str	q{{[0-9]+}}, [[[REG1]], #64]
+;
+; CHECK-NOCPA-O0-LABEL:  subpt1:
+; CHECK-NOCPA-O0:        add	[[REG1:x[0-9]+]], x{{[0-9]+}}, #96
+; CHECK-NOCPA-O0:        str	q{{[0-9]+}}, [[[REG1]], x{{[0-9]+}}, lsl #4]
+;
+; CHECK-NOCPA-O3-LABEL:  subpt1:
+; CHECK-NOCPA-O3:        add	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, lsl #4
+; CHECK-NOCPA-O3:        str	q{{[0-9]+}}, [[[REG1]], #64]
+entry:
+  %conv = sext i32 %index to i64
+  %mul.neg = mul nsw i64 %conv, -16
+  %add.ptr = getelementptr inbounds %struct.my_type, ptr getelementptr inbounds ([10 x %struct.my_type], ptr @array, i64 0, i64 6), i64 %mul.neg
+  tail call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) %add.ptr, ptr noundef nonnull align 8 dereferenceable(16) getelementptr inbounds ([10 x %struct.my_type], ptr @array, i64 0, i64 2), i64 16, i1 false), !tbaa.struct !6
+  ret void
+}
+
+define void @subpt2(i32 %index, i32 %elem) {
+; CHECK-CPA-O0-LABEL:    subpt2:
+; CHECK-CPA-O0:          addpt	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}
+; CHECK-CPA-O0:          str	q{{[0-9]+}}, [[[REG1]], x{{[0-9]+}}, lsl #4]
+;
+; CHECK-CPA-O3-LABEL:    subpt2:
+; CHECK-CPA-O3:          addpt	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, lsl #4
+; CHECK-CPA-O3:          str	q{{[0-9]+}}, [[[REG1]], #64]
+;
+; CHECK-NOCPA-O0-LABEL:  subpt2:
+; CHECK-NOCPA-O0:        add	[[REG1:x[0-9]+]], x{{[0-9]+}}, #96
+; CHECK-NOCPA-O0:        str	q{{[0-9]+}}, [[[REG1]], x{{[0-9]+}}, lsl #4]
+;
+; CHECK-NOCPA-O3-LABEL:  subpt2:
+; CHECK-NOCPA-O3:        add	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, lsl #4
+; CHECK-NOCPA-O3:        str	q{{[0-9]+}}, [[[REG1]], #64]
+entry:
+  %idx.ext = sext i32 %index to i64
+  %idx.neg = sub nsw i64 0, %idx.ext
+  %add.ptr = getelementptr inbounds %struct.my_type, ptr getelementptr inbounds ([10 x %struct.my_type], ptr @array, i64 0, i64 6), i64 %idx.neg
+  tail call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) %add.ptr, ptr noundef nonnull align 8 dereferenceable(16) getelementptr inbounds ([10 x %struct.my_type], ptr @array, i64 0, i64 2), i64 16, i1 false), !tbaa.struct !11
+  ret void
+}
+
+define ptr @subpt3(ptr %ptr, i32 %index) {
+; CHECK-CPA-O0-LABEL:    subpt3:
+; CHECK-CPA-O0:          mov	[[REG1:x[0-9]+]], #-8
+; CHECK-CPA-O0:          addpt	x0, x{{[0-9]+}}, [[REG1]]
+; CHECK-CPA-O0:          ret
+;
+; CHECK-CPA-O3-LABEL:    subpt3:
+; CHECK-CPA-O3:          mov	[[REG1:x[0-9]+]], #-8
+; CHECK-CPA-O3:          addpt	x0, x{{[0-9]+}}, [[REG1]]
+; CHECK-CPA-O3:          ret
+;
+; CHECK-NOCPA-O0-LABEL:  subpt3:
+; CHECK-NOCPA-O0:        subs	x0, x{{[0-9]+}}, #8
+; CHECK-NOCPA-O0:        ret
+;
+; CHECK-NOCPA-O3-LABEL:  subpt3:
+; CHECK-NOCPA-O3:        sub	x0, x{{[0-9]+}}, #8
+; CHECK-NOCPA-O3:        ret
+entry:
+  %incdec.ptr.i.i.i = getelementptr inbounds i64, ptr %ptr, i64 -1
+  ret ptr %incdec.ptr.i.i.i
+}
+
+define i64 @subi64(i64 %ptr, i32 %index) {
+; CHECK-CPA-O0-LABEL:    subi64:
+; CHECK-CPA-O0:          subs x0, x0, #1
+; CHECK-CPA-O0:          ret
+;
+; CHECK-CPA-O3-LABEL:    subi64:
+; CHECK-CPA-O3:          sub x0, x0, #1
+; CHECK-CPA-O3:          ret
+;
+; CHECK-NOCPA-O0-LABEL:  subi64:
+; CHECK-NOCPA-O0:        subs x0, x0, #1
+; CHECK-NOCPA-O0:        ret
+;
+; CHECK-NOCPA-O3-LABEL:  subi64:
+; CHECK-NOCPA-O3:        sub x0, x0, #1
+; CHECK-NOCPA-O3:        ret
+entry:
+  %incdec.ptr.i.i.i = add i64 %ptr, -1
+  ret i64 %incdec.ptr.i.i.i
+}
+
+define i32 @subi32(i32 %ptr, i32 %index) {
+; CHECK-CPA-O0-LABEL:    subi32:
+; CHECK-CPA-O0:          subs w0, w0, #1
+; CHECK-CPA-O0:          ret
+;
+; CHECK-CPA-O3-LABEL:    subi32:
+; CHECK-CPA-O3:          sub w0, w0, #1
+; CHECK-CPA-O3:          ret
+;
+; CHECK-NOCPA-O0-LABEL:  subi32:
+; CHECK-NOCPA-O0:        subs w0, w0, #1
+; CHECK-NOCPA-O0:        ret
+;
+; CHECK-NOCPA-O3-LABEL:  subi32:
+; CHECK-NOCPA-O3:        sub w0, w0, #1
+; CHECK-NOCPA-O3:        ret
+entry:
+  %incdec.ptr.i.i.i = add i32 %ptr, -1
+  ret i32 %incdec.ptr.i.i.i
+}
+
+define i16 @subi16(i16 %ptr, i32 %index) {
+; CHECK-CPA-O0-LABEL:    subi16:
+; CHECK-CPA-O0:          subs w0, w0, #1
+; CHECK-CPA-O0:          ret
+;
+; CHECK-CPA-O3-LABEL:    subi16:
+; CHECK-CPA-O3:          sub w0, w0, #1
+; CHECK-CPA-O3:          ret
+;
+; CHECK-NOCPA-O0-LABEL:  subi16:
+; CHECK-NOCPA-O0:        subs w0, w0, #1
+; CHECK-NOCPA-O0:        ret
+;
+; CHECK-NOCPA-O3-LABEL:  subi16:
+; CHECK-NOCPA-O3:        sub w0, w0, #1
+; CHECK-NOCPA-O3:        ret
+entry:
+  %incdec.ptr.i.i.i = add i16 %ptr, -1
+  ret i16 %incdec.ptr.i.i.i
+}
+
+define i64 @addi64(i64 %ptr, i32 %index) {
+; CHECK-CPA-O0-LABEL:    addi64:
+; CHECK-CPA-O0:          add x0, x0, #1
+; CHECK-CPA-O0:          ret
+;
+; CHECK-CPA-O3-LABEL:    addi64:
+; CHECK-CPA-O3:          add x0, x0, #1
+; CHECK-CPA-O3:          ret
+;
+; CHECK-NOCPA-O0-LABEL:  addi64:
+; CHECK-NOCPA-O0:        add x0, x0, #1
+; CHECK-NOCPA-O0:        ret
+;
+; CHECK-NOCPA-O3-LABEL:  addi64:
+; CHECK-NOCPA-O3:        add x0, x0, #1
+; CHECK-NOCPA-O3:        ret
+entry:
+  %incdec.ptr.i.i.i = add i64 %ptr, 1
+  ret i64 %incdec.ptr.i.i.i
+}
+
+define i32 @addi32(i32 %ptr, i32 %index) {
+; CHECK-CPA-O0-LABEL:    addi32:
+; CHECK-CPA-O0:          add w0, w0, #1
+; CHECK-CPA-O0:          ret
+;
+; CHECK-CPA-O3-LABEL:    addi32:
+; CHECK-CPA-O3:          add w0, w0, #1
+; CHECK-CPA-O3:          ret
+;
+; CHECK-NOCPA-O0-LABEL:  addi32:
+; CHECK-NOCPA-O0:        add w0, w0, #1
+; CHECK-NOCPA-O0:        ret
+;
+; CHECK-NOCPA-O3-LABEL:  addi32:
+; CHECK-NOCPA-O3:        add w0, w0, #1
+; CHECK-NOCPA-O3:        ret
+entry:
+  %incdec.ptr.i.i.i = add i32 %ptr, 1
+  ret i32 %incdec.ptr.i.i.i
+}
+
+define i16 @addi16(i16 %ptr, i32 %index) {
+; CHECK-CPA-O0-LABEL:    addi16:
+; CHECK-CPA-O0:          add w0, w0, #1
+; CHECK-CPA-O0:          ret
+;
+; CHECK-CPA-O3-LABEL:    addi16:
+; CHECK-CPA-O3:          add w0, w0, #1
+; CHECK-CPA-O3:          ret
+;
+; CHECK-NOCPA-O0-LABEL:  addi16:
+; CHECK-NOCPA-O0:        add w0, w0, #1
+; CHECK-NOCPA-O0:        ret
+;
+; CHECK-NOCPA-O3-LABEL:  addi16:
+; CHECK-NOCPA-O3:        add w0, w0, #1
+; CHECK-NOCPA-O3:        ret
+entry:
+  %incdec.ptr.i.i.i = add i16 %ptr, 1
+  ret i16 %incdec.ptr.i.i.i
+}
+
+define i64 @arith1(i64 noundef %0, i64 noundef %1, i64 noundef %2) {
+; CHECK-CPA-O0-LABEL:    arith1:
+; CHECK-CPA-O0:          mul	x9, x9, x10
+; CHECK-CPA-O0:          add	x0, x8, x9
+; CHECK-CPA-O0:          add	sp, sp, #32
+; CHECK-CPA-O0:          ret
+;
+; CHECK-CPA-O3-LABEL:    arith1:
+; CHECK-CPA-O3:          madd	x0, x1, x2, x0
+; CHECK-CPA-O3:          add	sp, sp, #32
+; CHECK-CPA-O3:          ret
+;
+; CHECK-NOCPA-O0-LABEL:  arith1:
+; CHECK-NOCPA-O0:        mul	x9, x9, x10
+; CHECK-NOCPA-O0:        add	x0, x8, x9
+; CHECK-NOCPA-O0:        add	sp, sp, #32
+; CHECK-NOCPA-O0:        ret
+;
+; CHECK-NOCPA-O3-LABEL:  arith1:
+; CHECK-NOCPA-O3:        madd	x0, x1, x2, x0
+; CHECK-NOCPA-O3:        add	sp, sp, #32
+; CHECK-NOCPA-O3:        ret
+entry:
+  %4 = alloca i64, align 8
+  %5 = alloca i64, align 8
+  %6 = alloca i64, align 8
+  store i64 %0, ptr %4, align 8
+  store i64 %1, ptr %5, align 8
+  store i64 %2, ptr %6, align 8
+  %7 = load i64, ptr %4, align 8
+  %8 = load i64, ptr %5, align 8
+  %9 = load i64, ptr %6, align 8
+  %10 = mul nsw i64 %8, %9
+  %11 = add nsw i64 %7, %10
+  ret i64 %11
+}
+
+define i64 @arith2(ptr noundef %0, i64 noundef %1, i64 noundef %2, i32 noundef %3) {
+; CHECK-CPA-O0-LABEL:    arith2:
+; CHECK-CPA-O0:          maddpt	x8, x8, x9, x10
+; CHECK-CPA-O0:          ldr	x8, [x8, #24]
+; CHECK-CPA-O0:          ldr	x10, [sp, #16]
+; CHECK-CPA-O0:          ldr	x9, [sp, #8]
+; CHECK-CPA-O0:          mul	x10, x10, x9
+; CHECK-CPA-O0:          add	x8, x8, x10
+; CHECK-CPA-O0:          subs	x0, x8, x9
+; CHECK-CPA-O0:          add	sp, sp, #32
+; CHECK-CPA-O0:          ret
+;
+; CHECK-CPA-O3-LABEL:    arith2:
+; CHECK-CPA-O3:          maddpt	x8, x9, x10, x0
+; CHECK-CPA-O3:          ldr	x8, [x8, #24]
+; CHECK-CPA-O3:          madd	x8, x1, x2, x8
+; CHECK-CPA-O3:          sub	x0, x8, x2
+; CHECK-CPA-O3:          add	sp, sp, #32
+; CHECK-CPA-O3:          ret
+;
+; CHECK-NOCPA-O0-LABEL:  arith2:
+; CHECK-NOCPA-O0:        mul	x9, x9, x10
+; CHECK-NOCPA-O0:        add	x8, x8, x9
+; CHECK-NOCPA-O0:        ldr	x8, [x8, #24]
+; CHECK-NOCPA-O0:        ldr	x10, [sp, #16]
+; CHECK-NOCPA-O0:        ldr	x9, [sp, #8]
+; CHECK-NOCPA-O0:        mul	x10, x10, x9
+; CHECK-NOCPA-O0:        add	x8, x8, x10
+; CHECK-NOCPA-O0:        subs	x0, x8, x9
+; CHECK-NOCPA-O0:        add	sp, sp, #32
+; CHECK-NOCPA-O0:        ret
+;
+; CHECK-NOCPA-O3-LABEL:  arith2:
+; CHECK-NOCPA-O3:        madd	x8, x8, x9, x0
+; CHECK-NOCPA-O3:        ldr	x8, [x8, #24]
+; CHECK-NOCPA-O3:        madd	x8, x1, x2, x8
+; CHECK-NOCPA-O3:        sub	x0, x8, x2
+; CHECK-NOCPA-O3:        add	sp, sp, #32
+; CHECK-NOCPA-O3:        ret
+entry:
+  %5 = alloca ptr, align 8
+  %6 = alloca i64, align 8
+  %7 = alloca i64, align 8
+  %8 = alloca i32, align 4
+  store ptr %0, ptr %5, align 8
+  store i64 %1, ptr %6, align 8
+  store i64 %2, ptr %7, align 8
+  store i32 %3, ptr %8, align 4
+  %9 = load ptr, ptr %5, align 8
+  %10 = load i32, ptr %8, align 4
+  %11 = sext i32 %10 to i64
+  %12 = getelementptr inbounds %struct.my_type2, ptr %9, i64 %11
+  %13 = getelementptr inbounds %struct.my_type2, ptr %12, i32 0, i32 3
+  %14 = load i64, ptr %13, align 8
+  %15 = load i64, ptr %6, align 8
+  %16 = load i64, ptr %7, align 8
+  %17 = mul nsw i64 %15, %16
+  %18 = add nsw i64 %14, %17
+  %19 = sub nsw i64 %18, %16
+  ret i64 %19
+}
+
+ at a = hidden global [2 x [1 x [2 x i8]]] [[1 x [2 x i8]] [[2 x i8] c"\01\01"], [1 x [2 x i8]] [[2 x i8] c"\01\01"]], align 1
+ at b = hidden global i16 0, align 2
+
+define hidden void @multidim() {
+; CHECK-CPA-O0-LABEL:    multidim:
+; CHECK-CPA-O0:          adrp	x8, b
+; CHECK-CPA-O0:          ldrh	w9, [x8, :lo12:b]
+; CHECK-CPA-O0:          mov	w10, w9
+; CHECK-CPA-O0:          ldrh	w8, [x8, :lo12:b]
+; CHECK-CPA-O0:          add	w9, w8, #1
+; CHECK-CPA-O0:          mov	w8, w9
+; CHECK-CPA-O0:          sxtw	x9, w8
+; CHECK-CPA-O0:          mov	w8, #2                          // =0x2
+; CHECK-CPA-O0:          mov	w11, w8
+; CHECK-CPA-O0:          adrp	x8, a
+; CHECK-CPA-O0:          add	x8, x8, :lo12:a
+; CHECK-CPA-O0:          addpt	x8, x8, x11
+; CHECK-CPA-O0:          addpt	x8, x8, x10, lsl #1
+; CHECK-CPA-O0:          addpt	x8, x8, x9
+; CHECK-CPA-O0:          ldrb	w8, [x8]
+;
+; CHECK-CPA-O3-LABEL:    multidim:
+; CHECK-CPA-O3:          ret
+;
+; CHECK-NOCPA-O0-LABEL:  multidim:
+; CHECK-NOCPA-O0:        adrp	x8, b
+; CHECK-NOCPA-O0:        ldrh	w9, [x8, :lo12:b]
+; CHECK-NOCPA-O0:        mov	w10, w9
+; CHECK-NOCPA-O0:        ldrh	w8, [x8, :lo12:b]
+; CHECK-NOCPA-O0:        add	w9, w8, #1
+; CHECK-NOCPA-O0:        adrp	x8, a
+; CHECK-NOCPA-O0:        add	x8, x8, :lo12:a
+; CHECK-NOCPA-O0:        add	x8, x8, #2
+; CHECK-NOCPA-O0:        add	x8, x8, x10, lsl #1
+; CHECK-NOCPA-O0:        add	x8, x8, w9, sxtw
+; CHECK-NOCPA-O0:        ldrb	w8, [x8]
+;
+; CHECK-NOCPA-O3-LABEL:  multidim:
+; CHECK-NOCPA-O3:        ret
+entry:
+  %0 = load i16, ptr @b, align 2
+  %idxprom = zext i16 %0 to i64
+  %arrayidx = getelementptr inbounds [1 x [2 x i8]], ptr getelementptr inbounds ([2 x [1 x [2 x i8]]], ptr @a, i64 0, i64 1), i64 0, i64 %idxprom
+  %1 = load i16, ptr @b, align 2
+  %conv = zext i16 %1 to i32
+  %add = add nsw i32 %conv, 1
+  %idxprom1 = sext i32 %add to i64
+  %arrayidx2 = getelementptr inbounds [2 x i8], ptr %arrayidx, i64 0, i64 %idxprom1
+  %2 = load i8, ptr %arrayidx2, align 1
+  %tobool = icmp ne i8 %2, 0
+  br i1 %tobool, label %if.then, label %if.end
+
+if.then:
+  br label %if.end
+
+if.end:
+  ret void
+}
+
+declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)
+
+!6 = !{i64 0, i64 8, !7, i64 8, i64 8, !7, i64 16, i64 8, !7, i64 24, i64 8, !7, i64 32, i64 8, !7, i64 40, i64 8, !7}
+!7 = !{!8, !8, i64 0}
+!8 = !{!"long", !9, i64 0}
+!9 = !{!"omnipotent char", !10, i64 0}
+!10 = !{!"Simple C++ TBAA"}
+!11 = !{i64 0, i64 8, !7, i64 8, i64 8, !7}
diff --git a/llvm/test/CodeGen/AArch64/cpa-selectiondag.ll b/llvm/test/CodeGen/AArch64/cpa-selectiondag.ll
new file mode 100644
index 00000000000000..ea9c9615943d83
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/cpa-selectiondag.ll
@@ -0,0 +1,449 @@
+; RUN: llc -mtriple=aarch64 -verify-machineinstrs --mattr=+cpa -O0 -global-isel=0 -fast-isel=0 %s -o - 2>&1 | FileCheck %s --check-prefixes=CHECK-CPA-O0
+; RUN: llc -mtriple=aarch64 -verify-machineinstrs --mattr=+cpa -O3 -global-isel=0 -fast-isel=0 %s -o - 2>&1 | FileCheck %s --check-prefixes=CHECK-CPA-O3
+; RUN: llc -mtriple=aarch64 -verify-machineinstrs --mattr=-cpa -O0 -global-isel=0 -fast-isel=0 %s -o - 2>&1 | FileCheck %s --check-prefixes=CHECK-NOCPA-O0
+; RUN: llc -mtriple=aarch64 -verify-machineinstrs --mattr=-cpa -O3 -global-isel=0 -fast-isel=0 %s -o - 2>&1 | FileCheck %s --check-prefixes=CHECK-NOCPA-O3
+
+%struct.my_type = type { i64, i64 }
+%struct.my_type2 = type { i64, i64, i64, i64, i64, i64 }
+
+ at array = external dso_local global [10 x %struct.my_type], align 8
+ at array2 = external dso_local global [10 x %struct.my_type2], align 8
+
+define void @addpt1(i64 %index, i64 %arg) {
+; CHECK-CPA-O0-LABEL:    addpt1:
+; CHECK-CPA-O0:          addpt	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, lsl #4
+; CHECK-CPA-O0:          str	x{{[0-9]+}}, [[[REG1]], #8]
+;
+; CHECK-CPA-O3-LABEL:    addpt1:
+; CHECK-CPA-O3:          addpt	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, lsl #4
+; CHECK-CPA-O3:          str	x{{[0-9]+}}, [[[REG1]], #8]
+;
+; CHECK-NOCPA-O0-LABEL:  addpt1:
+; CHECK-NOCPA-O0:        add	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, lsl #4
+; CHECK-NOCPA-O0:        str	x{{[0-9]+}}, [[[REG1]], #8]
+
+; CHECK-NOCPA-O3-LABEL:  addpt1:
+; CHECK-NOCPA-O3:        add	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, lsl #4
+; CHECK-NOCPA-O3:        str	x{{[0-9]+}}, [[[REG1]], #8]
+entry:
+  %e2 = getelementptr inbounds %struct.my_type, ptr @array, i64 %index, i32 1
+  store i64 %arg, ptr %e2, align 8
+  ret void
+}
+
+define void @maddpt1(i32 %pos, ptr %val) {
+; CHECK-CPA-O0-LABEL:    maddpt1:
+; CHECK-CPA-O0:          maddpt	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, x{{[0-9]+}}
+; CHECK-CPA-O0:          str	q{{[0-9]+}}, [[[REG1]], #32]
+; CHECK-CPA-O0:          str	q{{[0-9]+}}, [[[REG1]], #16]
+; CHECK-CPA-O0:          str	q{{[0-9]+}}, [[[REG1]]]
+;
+; CHECK-CPA-O3-LABEL:    maddpt1:
+; CHECK-CPA-O3:          maddpt	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, x{{[0-9]+}}
+; CHECK-CPA-O3:          stp	q{{[0-9]+}}, q{{[0-9]+}}, [[[REG1]], #16]
+; CHECK-CPA-O3:          str	q{{[0-9]+}}, [[[REG1]]]
+;
+; CHECK-NOCPA-O0-LABEL:  maddpt1:
+; CHECK-NOCPA-O0:        smaddl	[[REG1:x[0-9]+]], w{{[0-9]+}}, w{{[0-9]+}}, x{{[0-9]+}}
+; CHECK-NOCPA-O0:        str	q{{[0-9]+}}, [[[REG1]], #32]
+; CHECK-NOCPA-O0:        str	q{{[0-9]+}}, [[[REG1]], #16]
+; CHECK-NOCPA-O0:        str	q{{[0-9]+}}, [[[REG1]]]
+;
+; CHECK-NOCPA-O3-LABEL:  maddpt1:
+; CHECK-NOCPA-O3:        smaddl	[[REG1:x[0-9]+]], w{{[0-9]+}}, w{{[0-9]+}}, x{{[0-9]+}}
+; CHECK-NOCPA-O3:        stp	q{{[0-9]+}}, q{{[0-9]+}}, [[[REG1]], #16]
+; CHECK-NOCPA-O3:        str	q{{[0-9]+}}, [[[REG1]]]
+entry:
+  %idxprom = sext i32 %pos to i64
+  %arrayidx = getelementptr inbounds [10 x %struct.my_type2], ptr @array2, i64 0, i64 %idxprom
+  tail call void @llvm.memcpy.p0.p0.i64(ptr align 8 dereferenceable(48) %arrayidx, ptr align 8 dereferenceable(48) %val, i64 48, i1 false)
+  ret void
+}
+
+define void @msubpt1(i32 %index, i32 %elem) {
+; CHECK-CPA-O0-LABEL:    msubpt1:
+; CHECK-CPA-O0:          addpt	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, lsl #4
+; CHECK-CPA-O0:          addpt	[[REG2:x[0-9]+]], [[REG1]], x{{[0-9]+}}
+; CHECK-CPA-O0:          addpt	[[REG3:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}
+; CHECK-CPA-O0:          ldr	q{{[0-9]+}}, [[[REG3]], #16]
+; CHECK-CPA-O0:          str	q{{[0-9]+}}, [[[REG1]], #288]
+; CHECK-CPA-O0:          str	q{{[0-9]+}}, [[[REG2]], #32]
+;
+; CHECK-CPA-O3-LABEL:    msubpt1:
+; CHECK-CPA-O3:          addpt	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, lsl #4
+; CHECK-CPA-O3:          addpt	[[REG2:x[0-9]+]], [[REG1]], x{{[0-9]+}}
+; CHECK-CPA-O3:          str	q{{[0-9]+}}, [[[REG1]], #288]
+; CHECK-CPA-O3:          stp	q{{[0-9]+}}, q{{[0-9]+}}, [[[REG2]], #16]
+;
+; CHECK-NOCPA-O0-LABEL:  msubpt1:
+; CHECK-NOCPA-O0:        mneg	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}
+; CHECK-NOCPA-O0:        add	[[REG2:x[0-9]+]], x{{[0-9]+}}, [[REG1]]
+; CHECK-NOCPA-O0:        str	q{{[0-9]+}}, [[[REG2]], #320]
+; CHECK-NOCPA-O0:        str	q{{[0-9]+}}, [[[REG2]], #304]
+; CHECK-NOCPA-O0:        str	q{{[0-9]+}}, [[[REG2]], #288]
+;
+; CHECK-NOCPA-O3-LABEL:  msubpt1:
+; CHECK-NOCPA-O3:        mneg	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}
+; CHECK-NOCPA-O3:        add	[[REG2:x[0-9]+]], x{{[0-9]+}}, [[REG1]]
+; CHECK-NOCPA-O3:        stp	q{{[0-9]+}}, q{{[0-9]+}}, [[[REG2]], #304]
+; CHECK-NOCPA-O3:        str	q{{[0-9]+}}, [[[REG2]], #288]
+entry:
+  %idx.ext = sext i32 %index to i64
+  %idx.neg = sub nsw i64 0, %idx.ext
+  %add.ptr = getelementptr inbounds %struct.my_type2, ptr getelementptr inbounds ([10 x %struct.my_type2], ptr @array2, i64 0, i64 6), i64 %idx.neg
+  tail call void @llvm.memcpy.p0.p0.i64(ptr align 8 dereferenceable(48) %add.ptr, ptr align 8 dereferenceable(48) getelementptr inbounds ([10 x %struct.my_type2], ptr @array2, i64 0, i64 2), i64 48, i1 false), !tbaa.struct !6
+  ret void
+}
+
+define void @subpt1(i32 %index, i32 %elem) {
+; CHECK-CPA-O0-LABEL:    subpt1:
+; CHECK-CPA-O0:          subpt	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}
+; CHECK-CPA-O0:          str	q{{[0-9]+}}, [[[REG1]], #96]
+;
+; CHECK-CPA-O3-LABEL:    subpt1:
+; CHECK-CPA-O3:          subpt	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}
+; CHECK-CPA-O3:          str	q{{[0-9]+}}, [[[REG1]], #96]
+;
+; CHECK-NOCPA-O0-LABEL:  subpt1:
+; CHECK-NOCPA-O0:        subs	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, lsl #8
+; CHECK-NOCPA-O0:        str	q{{[0-9]+}}, [[[REG1]], #96]
+;
+; CHECK-NOCPA-O3-LABEL:  subpt1:
+; CHECK-NOCPA-O3:        sub	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, lsl #8
+; CHECK-NOCPA-O3:        str	q{{[0-9]+}}, [[[REG1]], #96]
+entry:
+  %conv = sext i32 %index to i64
+  %mul.neg = mul nsw i64 %conv, -16
+  %add.ptr = getelementptr inbounds %struct.my_type, ptr getelementptr inbounds ([10 x %struct.my_type], ptr @array, i64 0, i64 6), i64 %mul.neg
+  tail call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) %add.ptr, ptr noundef nonnull align 8 dereferenceable(16) getelementptr inbounds ([10 x %struct.my_type], ptr @array, i64 0, i64 2), i64 16, i1 false), !tbaa.struct !6
+  ret void
+}
+
+define void @subpt2(i32 %index, i32 %elem) {
+; CHECK-CPA-O0-LABEL:    subpt2:
+; CHECK-CPA-O0:          addpt	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, lsl #4
+; CHECK-CPA-O0:          str	q{{[0-9]+}}, [[[REG1]], #96]
+;
+; CHECK-CPA-O3-LABEL:    subpt2:
+; CHECK-CPA-O3:          addpt	[[REG1:x[0-9]+]], x{{[0-9]+}}, x{{[0-9]+}}, lsl #4
+; CHECK-CPA-O3:          str	q{{[0-9]+}}, [[[REG1]], #96]
+;
+; CHECK-NOCPA-O0-LABEL:  subpt2:
+; CHECK-NOCPA-O0:        subs	[[REG1:x[0-9]+]], x{{[0-9]+}}, w{{[0-9]+}}, sxtw #4
+; CHECK-NOCPA-O0:        str	q{{[0-9]+}}, [[[REG1]], #96]
+;
+; CHECK-NOCPA-O3-LABEL:  subpt2:
+; CHECK-NOCPA-O3:        sub	[[REG1:x[0-9]+]], x{{[0-9]+}}, w{{[0-9]+}}, sxtw #4
+; CHECK-NOCPA-O3:        str	q{{[0-9]+}}, [[[REG1]], #96]
+entry:
+  %idx.ext = sext i32 %index to i64
+  %idx.neg = sub nsw i64 0, %idx.ext
+  %add.ptr = getelementptr inbounds %struct.my_type, ptr getelementptr inbounds ([10 x %struct.my_type], ptr @array, i64 0, i64 6), i64 %idx.neg
+  tail call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) %add.ptr, ptr noundef nonnull align 8 dereferenceable(16) getelementptr inbounds ([10 x %struct.my_type], ptr @array, i64 0, i64 2), i64 16, i1 false), !tbaa.struct !11
+  ret void
+}
+
+define ptr @subpt3(ptr %ptr, i32 %index) {
+; CHECK-CPA-O0-LABEL:    subpt3:
+; CHECK-CPA-O0:          mov	[[REG1:x[0-9]+]], #-8
+; CHECK-CPA-O0:          addpt	x0, x{{[0-9]+}}, [[REG1]]
+; CHECK-CPA-O0:          ret
+;
+; CHECK-CPA-O3-LABEL:    subpt3:
+; CHECK-CPA-O3:          mov	[[REG1:x[0-9]+]], #-8
+; CHECK-CPA-O3:          addpt	x0, x{{[0-9]+}}, [[REG1]]
+; CHECK-CPA-O3:          ret
+;
+; CHECK-NOCPA-O0-LABEL:  subpt3:
+; CHECK-NOCPA-O0:        subs	x0, x{{[0-9]+}}, #8
+; CHECK-NOCPA-O0:        ret
+;
+; CHECK-NOCPA-O3-LABEL:  subpt3:
+; CHECK-NOCPA-O3:        sub	x0, x{{[0-9]+}}, #8
+; CHECK-NOCPA-O3:        ret
+entry:
+  %incdec.ptr.i.i.i = getelementptr inbounds i64, ptr %ptr, i64 -1
+  ret ptr %incdec.ptr.i.i.i
+}
+
+define i64 @subi64(i64 %ptr, i32 %index) {
+; CHECK-CPA-O0-LABEL:    subi64:
+; CHECK-CPA-O0:          subs x0, x0, #1
+; CHECK-CPA-O0:          ret
+;
+; CHECK-CPA-O3-LABEL:    subi64:
+; CHECK-CPA-O3:          sub x0, x0, #1
+; CHECK-CPA-O3:          ret
+;
+; CHECK-NOCPA-O0-LABEL:  subi64:
+; CHECK-NOCPA-O0:        subs x0, x0, #1
+; CHECK-NOCPA-O0:        ret
+;
+; CHECK-NOCPA-O3-LABEL:  subi64:
+; CHECK-NOCPA-O3:        sub x0, x0, #1
+; CHECK-NOCPA-O3:        ret
+entry:
+  %incdec.ptr.i.i.i = add i64 %ptr, -1
+  ret i64 %incdec.ptr.i.i.i
+}
+
+define i32 @subi32(i32 %ptr, i32 %index) {
+; CHECK-CPA-O0-LABEL:    subi32:
+; CHECK-CPA-O0:          subs w0, w0, #1
+; CHECK-CPA-O0:          ret
+;
+; CHECK-CPA-O3-LABEL:    subi32:
+; CHECK-CPA-O3:          sub w0, w0, #1
+; CHECK-CPA-O3:          ret
+;
+; CHECK-NOCPA-O0-LABEL:  subi32:
+; CHECK-NOCPA-O0:        subs w0, w0, #1
+; CHECK-NOCPA-O0:        ret
+;
+; CHECK-NOCPA-O3-LABEL:  subi32:
+; CHECK-NOCPA-O3:        sub w0, w0, #1
+; CHECK-NOCPA-O3:        ret
+entry:
+  %incdec.ptr.i.i.i = add i32 %ptr, -1
+  ret i32 %incdec.ptr.i.i.i
+}
+
+define i16 @subi16(i16 %ptr, i32 %index) {
+; CHECK-CPA-O0-LABEL:    subi16:
+; CHECK-CPA-O0:          subs w0, w0, #1
+; CHECK-CPA-O0:          ret
+;
+; CHECK-CPA-O3-LABEL:    subi16:
+; CHECK-CPA-O3:          sub w0, w0, #1
+; CHECK-CPA-O3:          ret
+;
+; CHECK-NOCPA-O0-LABEL:  subi16:
+; CHECK-NOCPA-O0:        subs w0, w0, #1
+; CHECK-NOCPA-O0:        ret
+;
+; CHECK-NOCPA-O3-LABEL:  subi16:
+; CHECK-NOCPA-O3:        sub w0, w0, #1
+; CHECK-NOCPA-O3:        ret
+entry:
+  %incdec.ptr.i.i.i = add i16 %ptr, -1
+  ret i16 %incdec.ptr.i.i.i
+}
+
+define i64 @addi64(i64 %ptr, i32 %index) {
+; CHECK-CPA-O0-LABEL:    addi64:
+; CHECK-CPA-O0:          add x0, x0, #1
+; CHECK-CPA-O0:          ret
+;
+; CHECK-CPA-O3-LABEL:    addi64:
+; CHECK-CPA-O3:          add x0, x0, #1
+; CHECK-CPA-O3:          ret
+;
+; CHECK-NOCPA-O0-LABEL:  addi64:
+; CHECK-NOCPA-O0:        add x0, x0, #1
+; CHECK-NOCPA-O0:        ret
+;
+; CHECK-NOCPA-O3-LABEL:  addi64:
+; CHECK-NOCPA-O3:        add x0, x0, #1
+; CHECK-NOCPA-O3:        ret
+entry:
+  %incdec.ptr.i.i.i = add i64 %ptr, 1
+  ret i64 %incdec.ptr.i.i.i
+}
+
+define i32 @addi32(i32 %ptr, i32 %index) {
+; CHECK-CPA-O0-LABEL:    addi32:
+; CHECK-CPA-O0:          add w0, w0, #1
+; CHECK-CPA-O0:          ret
+;
+; CHECK-CPA-O3-LABEL:    addi32:
+; CHECK-CPA-O3:          add w0, w0, #1
+; CHECK-CPA-O3:          ret
+;
+; CHECK-NOCPA-O0-LABEL:  addi32:
+; CHECK-NOCPA-O0:        add w0, w0, #1
+; CHECK-NOCPA-O0:        ret
+;
+; CHECK-NOCPA-O3-LABEL:  addi32:
+; CHECK-NOCPA-O3:        add w0, w0, #1
+; CHECK-NOCPA-O3:        ret
+entry:
+  %incdec.ptr.i.i.i = add i32 %ptr, 1
+  ret i32 %incdec.ptr.i.i.i
+}
+
+define i16 @addi16(i16 %ptr, i32 %index) {
+; CHECK-CPA-O0-LABEL:    addi16:
+; CHECK-CPA-O0:          add w0, w0, #1
+; CHECK-CPA-O0:          ret
+;
+; CHECK-CPA-O3-LABEL:    addi16:
+; CHECK-CPA-O3:          add w0, w0, #1
+; CHECK-CPA-O3:          ret
+;
+; CHECK-NOCPA-O0-LABEL:  addi16:
+; CHECK-NOCPA-O0:        add w0, w0, #1
+; CHECK-NOCPA-O0:        ret
+;
+; CHECK-NOCPA-O3-LABEL:  addi16:
+; CHECK-NOCPA-O3:        add w0, w0, #1
+; CHECK-NOCPA-O3:        ret
+entry:
+  %incdec.ptr.i.i.i = add i16 %ptr, 1
+  ret i16 %incdec.ptr.i.i.i
+}
+
+define i64 @arith1(i64 noundef %0, i64 noundef %1, i64 noundef %2) {
+; CHECK-CPA-O0-LABEL:    arith1:
+; CHECK-CPA-O0:          mul	x9, x9, x10
+; CHECK-CPA-O0:          add	x0, x8, x9
+; CHECK-CPA-O0:          add	sp, sp, #32
+; CHECK-CPA-O0:          ret
+;
+; CHECK-CPA-O3-LABEL:    arith1:
+; CHECK-CPA-O3:          madd	x8, x1, x2, x0
+; CHECK-CPA-O3:          mov	x0, x8
+; CHECK-CPA-O3:          add	sp, sp, #32
+; CHECK-CPA-O3:          ret
+;
+; CHECK-NOCPA-O0-LABEL:  arith1:
+; CHECK-NOCPA-O0:        mul	x9, x9, x10
+; CHECK-NOCPA-O0:        add	x0, x8, x9
+; CHECK-NOCPA-O0:        add	sp, sp, #32
+; CHECK-NOCPA-O0:        ret
+;
+; CHECK-NOCPA-O3-LABEL:  arith1:
+; CHECK-NOCPA-O3:        madd	x8, x1, x2, x0
+; CHECK-NOCPA-O3:        mov	x0, x8
+; CHECK-NOCPA-O3:        add	sp, sp, #32
+; CHECK-NOCPA-O3:        ret
+entry:
+  %4 = alloca i64, align 8
+  %5 = alloca i64, align 8
+  %6 = alloca i64, align 8
+  store i64 %0, ptr %4, align 8
+  store i64 %1, ptr %5, align 8
+  store i64 %2, ptr %6, align 8
+  %7 = load i64, ptr %4, align 8
+  %8 = load i64, ptr %5, align 8
+  %9 = load i64, ptr %6, align 8
+  %10 = mul nsw i64 %8, %9
+  %11 = add nsw i64 %7, %10
+  ret i64 %11
+}
+
+define i64 @arith2(ptr noundef %0, i64 noundef %1, i64 noundef %2, i32 noundef %3) {
+; CHECK-CPA-O0-LABEL:    arith2:
+; CHECK-CPA-O0:          add	x9, x9, x9, lsl #1
+; CHECK-CPA-O0:          addpt	x8, x8, x9, lsl #4
+; CHECK-CPA-O0:          ldr	x8, [x8, #24]
+; CHECK-CPA-O0:          mul	x10, x10, x9
+; CHECK-CPA-O0:          add	x8, x8, x10
+; CHECK-CPA-O0:          subs	x0, x8, x9
+; CHECK-CPA-O0:          add	sp, sp, #32
+; CHECK-CPA-O0:          ret
+;
+; CHECK-CPA-O3-LABEL:    arith2:
+; CHECK-CPA-O3:          mov	w9, #48                         // =0x30
+; CHECK-CPA-O3:          maddpt	x8, x8, x9, x0
+; CHECK-CPA-O3:          ldr	x8, [x8, #24]
+; CHECK-CPA-O3:          madd	x8, x1, x2, x8
+; CHECK-CPA-O3:          sub	x0, x8, x2
+; CHECK-CPA-O3:          ret
+;
+; CHECK-NOCPA-O0-LABEL:  arith2:
+; CHECK-NOCPA-O0:        smaddl	x8, w8, w9, x10
+; CHECK-NOCPA-O0:        mul	x10, x10, x9
+; CHECK-NOCPA-O0:        add	x8, x8, x10
+; CHECK-NOCPA-O0:        subs	x0, x8, x9
+; CHECK-NOCPA-O0:        add	sp, sp, #32
+; CHECK-NOCPA-O0:        ret
+;
+; CHECK-NOCPA-O3-LABEL:  arith2:
+; CHECK-NOCPA-O3:        smaddl	x8, w3, w8, x0
+; CHECK-NOCPA-O3:        ldr	x8, [x8, #24]
+; CHECK-NOCPA-O3:        madd	x8, x1, x2, x8
+; CHECK-NOCPA-O3:        sub	x0, x8, x2
+; CHECK-NOCPA-O3:        add	sp, sp, #32
+; CHECK-NOCPA-O3:        ret
+entry:
+  %5 = alloca ptr, align 8
+  %6 = alloca i64, align 8
+  %7 = alloca i64, align 8
+  %8 = alloca i32, align 4
+  store ptr %0, ptr %5, align 8
+  store i64 %1, ptr %6, align 8
+  store i64 %2, ptr %7, align 8
+  store i32 %3, ptr %8, align 4
+  %9 = load ptr, ptr %5, align 8
+  %10 = load i32, ptr %8, align 4
+  %11 = sext i32 %10 to i64
+  %12 = getelementptr inbounds %struct.my_type2, ptr %9, i64 %11
+  %13 = getelementptr inbounds %struct.my_type2, ptr %12, i32 0, i32 3
+  %14 = load i64, ptr %13, align 8
+  %15 = load i64, ptr %6, align 8
+  %16 = load i64, ptr %7, align 8
+  %17 = mul nsw i64 %15, %16
+  %18 = add nsw i64 %14, %17
+  %19 = sub nsw i64 %18, %16
+  ret i64 %19
+}
+
+ at a = hidden global [2 x [1 x [2 x i8]]] [[1 x [2 x i8]] [[2 x i8] c"\01\01"], [1 x [2 x i8]] [[2 x i8] c"\01\01"]], align 1
+ at b = hidden global i16 0, align 2
+
+define hidden void @multidim() {
+; CHECK-CPA-O0-LABEL:    multidim:
+; CHECK-CPA-O0:          adrp	x8, b
+; CHECK-CPA-O0:          ldrh	w10, [x8, :lo12:b]
+; CHECK-CPA-O0:          adrp	x8, a
+; CHECK-CPA-O0:          add	x8, x8, :lo12:a
+; CHECK-CPA-O0:          add	w9, w10, #1
+; CHECK-CPA-O0:          mov	w9, w9
+; CHECK-CPA-O0:          add	x9, x9, w10, uxtw #1
+; CHECK-CPA-O0:          addpt	x8, x8, x9
+; CHECK-CPA-O0:          ldrb	w8, [x8, #2]
+;
+; CHECK-CPA-O3-LABEL:    multidim:
+; CHECK-CPA-O3:          ret
+;
+; CHECK-NOCPA-O0-LABEL:  multidim:
+; CHECK-NOCPA-O0:        adrp	x8, b
+; CHECK-NOCPA-O0:        ldrh	w9, [x8, :lo12:b]
+; CHECK-NOCPA-O0:        adrp	x8, a
+; CHECK-NOCPA-O0:        add	x8, x8, :lo12:a
+; CHECK-NOCPA-O0:        add	x8, x8, w9, uxtw #1
+; CHECK-NOCPA-O0:        add	w9, w9, #1
+; CHECK-NOCPA-O0:        mov	w9, w9
+; CHECK-NOCPA-O0:        add	x8, x8, x9
+; CHECK-NOCPA-O0:        ldrb	w8, [x8, #2]
+;
+; CHECK-NOCPA-O3-LABEL:  multidim:
+; CHECK-NOCPA-O3:        ret
+entry:
+  %0 = load i16, ptr @b, align 2
+  %idxprom = zext i16 %0 to i64
+  %arrayidx = getelementptr inbounds [1 x [2 x i8]], ptr getelementptr inbounds ([2 x [1 x [2 x i8]]], ptr @a, i64 0, i64 1), i64 0, i64 %idxprom
+  %1 = load i16, ptr @b, align 2
+  %conv = zext i16 %1 to i32
+  %add = add nsw i32 %conv, 1
+  %idxprom1 = sext i32 %add to i64
+  %arrayidx2 = getelementptr inbounds [2 x i8], ptr %arrayidx, i64 0, i64 %idxprom1
+  %2 = load i8, ptr %arrayidx2, align 1
+  %tobool = icmp ne i8 %2, 0
+  br i1 %tobool, label %if.then, label %if.end
+
+if.then:
+  br label %if.end
+
+if.end:
+  ret void
+}
+
+declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)
+
+!6 = !{i64 0, i64 8, !7, i64 8, i64 8, !7, i64 16, i64 8, !7, i64 24, i64 8, !7, i64 32, i64 8, !7, i64 40, i64 8, !7}
+!7 = !{!8, !8, i64 0}
+!8 = !{!"long", !9, i64 0}
+!9 = !{!"omnipotent char", !10, i64 0}
+!10 = !{!"Simple C++ TBAA"}
+!11 = !{i64 0, i64 8, !7, i64 8, i64 8, !7}



More information about the llvm-commits mailing list