[llvm] [AArch64][PAC] Lower auth/resign into checked sequence. (PR #79024)

Ahmed Bougacha via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 22 09:59:14 PST 2024


https://github.com/ahmedbougacha created https://github.com/llvm/llvm-project/pull/79024

    This introduces 3 hardening modes in the authentication step of
    auth/resign lowering:
    - unchecked, which uses the AUT instructions as-is
    - poison, which detects authentication failure (using an XPAC+CMP
      sequence), explicitly yielding the XPAC result rather than the
      AUT result, to avoid leaking
    - trap, which additionally traps on authentication failure,
      using BRK #0xC470 + key (IA C470, IB C471, DA C472, DB C473.)

    Not all modes are necessarily useful in all contexts, and there
    are more performant alternative lowerings in specific contexts
    (e.g., when I/D TBI enablement is a target ABI guarantee.)

    This is controlled by the `ptrauth-auth-traps` function attributes,
    and can be overridden using `-aarch64-ptrauth-auth-checks=`.

A tiny additional commit then introduces the FPAC subtarget feature and uses it to improve the above isel:

    [AArch64][PAC] Don't emit auth/resign checks when targeting FPAC.

    When the FPAC feature is present, we can rely on its faulting
    behavior to avoid emitting the expensive authentication failure
    check sequence ourvelves.  In which case we emit the same
    sequence as a plain unchecked auth/resign.

>From 224c009f923076197d8b1d8649bdcd29a2c05f2f Mon Sep 17 00:00:00 2001
From: Ahmed Bougacha <ahmed at bougacha.org>
Date: Thu, 21 Dec 2023 11:36:42 -0800
Subject: [PATCH 1/3] [AArch64][PAC] Lower auth/resign into checked sequence.

This introduces 3 hardening modes in the authentication step of
auth/resign lowering:
- unchecked, which uses the AUT instructions as-is
- poison, which detects authentication failure (using an XPAC+CMP
  sequence), explicitly yielding the XPAC result rather than the
  AUT result, to avoid leaking
- trap, which additionally traps on authentication failure,
  using BRK #0xC470 + key (IA C470, IB C471, DA C472, DB C473.)

Not all modes are necessarily useful in all contexts, and there
are more performant alternative lowerings in specific contexts
(e.g., when I/D TBI enablement is a target ABI guarantee.)

This is controlled by the `ptrauth-auth-traps` function attributes,
and can be overridden using `-aarch64-ptrauth-auth-checks=`.
---
 llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp | 272 +++++++
 .../Target/AArch64/AArch64ISelDAGToDAG.cpp    | 102 +++
 llvm/lib/Target/AArch64/AArch64InstrInfo.td   |  30 +
 .../AArch64/GISel/AArch64GlobalISelUtils.cpp  |  29 +
 .../AArch64/GISel/AArch64GlobalISelUtils.h    |   6 +
 .../GISel/AArch64InstructionSelector.cpp      |  58 ++
 ...trauth-intrinsic-auth-resign-with-blend.ll | 261 ++++++
 .../AArch64/ptrauth-intrinsic-auth-resign.ll  | 764 ++++++++++++++++++
 8 files changed, 1522 insertions(+)
 create mode 100644 llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-resign-with-blend.ll
 create mode 100644 llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-resign.ll

diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
index 7d2ff146a340bd8..a2afbdb9e68820d 100644
--- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
+++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
@@ -67,6 +67,16 @@
 
 using namespace llvm;
 
+enum PtrauthCheckMode { Default, Unchecked, Poison, Trap };
+static cl::opt<PtrauthCheckMode>
+PtrauthAuthChecks("aarch64-ptrauth-auth-checks", cl::Hidden,
+                  cl::values(
+                    clEnumValN(Unchecked, "none", "don't test for failure"),
+                    clEnumValN(Poison, "poison", "poison on failure"),
+                    clEnumValN(Trap, "trap", "trap on failure")),
+                  cl::desc("Check pointer authentication auth/resign failures"),
+                  cl::init(Default));
+
 #define DEBUG_TYPE "asm-printer"
 
 namespace {
@@ -124,6 +134,12 @@ class AArch64AsmPrinter : public AsmPrinter {
 
   void emitSled(const MachineInstr &MI, SledKind Kind);
 
+  // Emit the sequence for AUT or AUTPAC.
+  void emitPtrauthAuthResign(const MachineInstr *MI);
+  // Emit the sequence to compute a discriminator into x17, or reuse AddrDisc.
+  unsigned emitPtrauthDiscriminator(uint16_t Disc, unsigned AddrDisc,
+                                    unsigned &InstsEmitted);
+
   /// tblgen'erated driver function for lowering simple MI->MC
   /// pseudo instructions.
   bool emitPseudoExpansionLowering(MCStreamer &OutStreamer,
@@ -1417,6 +1433,257 @@ void AArch64AsmPrinter::emitFMov0(const MachineInstr &MI) {
   }
 }
 
+unsigned AArch64AsmPrinter::emitPtrauthDiscriminator(uint16_t Disc,
+                                                     unsigned AddrDisc,
+                                                     unsigned &InstsEmitted) {
+  // If there is no constant discriminator, there's no blend involved:
+  // just use the address discriminator register as-is (XZR or not).
+  if (!Disc)
+    return AddrDisc;
+
+  // If there's only a constant discriminator, MOV it into x17.
+  if (AddrDisc == AArch64::XZR) {
+    EmitToStreamer(*OutStreamer,
+      MCInstBuilder(AArch64::MOVZXi)
+        .addReg(AArch64::X17)
+        .addImm(Disc)
+        .addImm(/*shift=*/0));
+    ++InstsEmitted;
+    return AArch64::X17;
+  }
+
+  // If there are both, emit a blend into x17.
+  EmitToStreamer(*OutStreamer,
+    MCInstBuilder(AArch64::ORRXrs)
+      .addReg(AArch64::X17)
+      .addReg(AArch64::XZR)
+      .addReg(AddrDisc)
+      .addImm(0));
+  ++InstsEmitted;
+  EmitToStreamer(*OutStreamer,
+    MCInstBuilder(AArch64::MOVKXi)
+      .addReg(AArch64::X17)
+      .addReg(AArch64::X17)
+      .addImm(Disc)
+      .addImm(/*shift=*/48));
+  ++InstsEmitted;
+  return AArch64::X17;
+}
+
+void AArch64AsmPrinter::emitPtrauthAuthResign(const MachineInstr *MI) {
+  unsigned InstsEmitted = 0;
+  const bool IsAUTPAC = MI->getOpcode() == AArch64::AUTPAC;
+
+  // We can expand AUT/AUTPAC into 3 possible sequences:
+  // - unchecked:
+  //      autia x16, x0
+  //      pacib x16, x1 ; if AUTPAC
+  //
+  // - checked and clearing:
+  //      mov x17, x0
+  //      movk x17, #disc, lsl #48
+  //      autia x16, x17
+  //      mov x17, x16
+  //      xpaci x17
+  //      cmp x16, x17
+  //      b.eq Lsuccess
+  //      mov x16, x17
+  //      b Lend
+  //     Lsuccess:
+  //      mov x17, x1
+  //      movk x17, #disc, lsl #48
+  //      pacib x16, x17
+  //     Lend:
+  //   Where we only emit the AUT if we started with an AUT.
+  //
+  // - checked and trapping:
+  //      mov x17, x0
+  //      movk x17, #disc, lsl #48
+  //      autia x16, x0
+  //      mov x17, x16
+  //      xpaci x17
+  //      cmp x16, x17
+  //      b.eq Lsuccess
+  //      brk #<0xc470 + aut key>
+  //     Lsuccess:
+  //      mov x17, x1
+  //      movk x17, #disc, lsl #48
+  //      pacib x16, x17 ; if AUTPAC
+  //   Where the b.eq skips over the trap if the PAC is valid.
+  //
+  // This sequence is expensive, but we need more information to be able to
+  // do better.
+  //
+  // We can't TBZ the poison bit because EnhancedPAC2 XORs the PAC bits
+  // on failure.
+  // We can't TST the PAC bits because we don't always know how the address
+  // space is setup for the target environment (and the bottom PAC bit is
+  // based on that).
+  // Either way, we also don't always know whether TBI is enabled or not for
+  // the specific target environment.
+
+  // By default, auth/resign sequences check for auth failures.
+  bool ShouldCheck = true;
+  // In the checked sequence, we only trap if explicitly requested.
+  bool ShouldTrap = MF->getFunction().hasFnAttribute("ptrauth-auth-traps");
+
+  // However, command-line flags can override this, for experimentation.
+  switch (PtrauthAuthChecks) {
+  case PtrauthCheckMode::Default: break;
+  case PtrauthCheckMode::Unchecked:
+    ShouldCheck = ShouldTrap = false;
+    break;
+  case PtrauthCheckMode::Poison:
+    ShouldCheck = true;
+    ShouldTrap = false;
+    break;
+  case PtrauthCheckMode::Trap:
+    ShouldCheck = ShouldTrap = true;
+    break;
+  }
+
+  auto AUTKey = (AArch64PACKey::ID)MI->getOperand(0).getImm();
+  uint64_t AUTDisc = MI->getOperand(1).getImm();
+  unsigned AUTAddrDisc = MI->getOperand(2).getReg();
+
+  unsigned XPACOpc = getXPACOpcodeForKey(AUTKey);
+
+  // Compute aut discriminator into x17
+  assert(isUInt<16>(AUTDisc));
+  unsigned AUTDiscReg = emitPtrauthDiscriminator(AUTDisc, AUTAddrDisc, InstsEmitted);
+  bool AUTZero = AUTDiscReg == AArch64::XZR;
+  unsigned AUTOpc = getAUTOpcodeForKey(AUTKey, AUTZero);
+
+  //  autiza x16      ; if  AUTZero
+  //  autia x16, x17  ; if !AUTZero
+  MCInst AUTInst;
+  AUTInst.setOpcode(AUTOpc);
+  AUTInst.addOperand(MCOperand::createReg(AArch64::X16));
+  AUTInst.addOperand(MCOperand::createReg(AArch64::X16));
+  if (!AUTZero)
+    AUTInst.addOperand(MCOperand::createReg(AUTDiscReg));
+  EmitToStreamer(*OutStreamer, AUTInst);
+  ++InstsEmitted;
+
+  // Unchecked or checked-but-non-trapping AUT is just an "AUT": we're done.
+  if (!IsAUTPAC && (!ShouldCheck || !ShouldTrap)) {
+    assert(STI->getInstrInfo()->getInstSizeInBytes(*MI) >= InstsEmitted * 4);
+    return;
+  }
+
+  MCSymbol *EndSym = nullptr;
+
+  // Checked sequences do an additional strip-and-compare.
+  if (ShouldCheck) {
+    MCSymbol *SuccessSym = createTempSymbol("auth_success_");
+
+    // XPAC has tied src/dst: use x17 as a temporary copy.
+    //  mov x17, x16
+    EmitToStreamer(*OutStreamer,
+      MCInstBuilder(AArch64::ORRXrs)
+        .addReg(AArch64::X17)
+        .addReg(AArch64::XZR)
+        .addReg(AArch64::X16)
+        .addImm(0));
+    ++InstsEmitted;
+
+    //  xpaci x17
+    EmitToStreamer(*OutStreamer,
+      MCInstBuilder(XPACOpc)
+        .addReg(AArch64::X17)
+        .addReg(AArch64::X17));
+    ++InstsEmitted;
+
+    //  cmp x16, x17
+    EmitToStreamer(*OutStreamer,
+      MCInstBuilder(AArch64::SUBSXrs)
+        .addReg(AArch64::XZR)
+        .addReg(AArch64::X16)
+        .addReg(AArch64::X17)
+        .addImm(0));
+    ++InstsEmitted;
+
+    //  b.eq Lsuccess
+    EmitToStreamer(*OutStreamer,
+      MCInstBuilder(AArch64::Bcc)
+        .addImm(AArch64CC::EQ)
+        .addExpr(MCSymbolRefExpr::create(SuccessSym, OutContext)));
+    ++InstsEmitted;
+
+    if (ShouldTrap) {
+      // Trapping sequences do a 'brk'.
+      //  brk #<0xc470 + aut key>
+      EmitToStreamer(*OutStreamer,
+        MCInstBuilder(AArch64::BRK)
+          .addImm(0xc470 | AUTKey));
+      ++InstsEmitted;
+    } else {
+      // Non-trapping checked sequences return the stripped result in x16,
+      // skipping over the PAC if there is one.
+
+      // FIXME: can we simply return the AUT result, already in x16? without..
+      //        ..traps this is usable as an oracle anyway, based on high bits
+      //  mov x17, x16
+      EmitToStreamer(*OutStreamer,
+        MCInstBuilder(AArch64::ORRXrs)
+          .addReg(AArch64::X16)
+          .addReg(AArch64::XZR)
+          .addReg(AArch64::X17)
+          .addImm(0));
+      ++InstsEmitted;
+
+      if (IsAUTPAC) {
+        EndSym = createTempSymbol("resign_end_");
+
+        //  b Lend
+        EmitToStreamer(*OutStreamer,
+          MCInstBuilder(AArch64::B)
+            .addExpr(MCSymbolRefExpr::create(EndSym, OutContext)));
+        ++InstsEmitted;
+      }
+    }
+
+    // If the auth check succeeds, we can continue.
+    // Lsuccess:
+    OutStreamer->emitLabel(SuccessSym);
+  }
+
+  // We already emitted unchecked and checked-but-non-trapping AUTs.
+  // That left us with trapping AUTs, and AUTPACs.
+  // Trapping AUTs don't need PAC: we're done.
+  if (!IsAUTPAC) {
+    assert(STI->getInstrInfo()->getInstSizeInBytes(*MI) >= InstsEmitted * 4);
+    return;
+  }
+
+  auto PACKey = (AArch64PACKey::ID)MI->getOperand(3).getImm();
+  uint64_t PACDisc = MI->getOperand(4).getImm();
+  unsigned PACAddrDisc = MI->getOperand(5).getReg();
+
+  // Compute pac discriminator into x17
+  assert(isUInt<16>(PACDisc));
+  unsigned PACDiscReg =
+      emitPtrauthDiscriminator(PACDisc, PACAddrDisc, InstsEmitted);
+  bool PACZero = PACDiscReg == AArch64::XZR;
+  unsigned PACOpc = getPACOpcodeForKey(PACKey, PACZero);
+
+  //  pacizb x16      ; if  PACZero
+  //  pacib x16, x17  ; if !PACZero
+  MCInst PACInst;
+  PACInst.setOpcode(PACOpc);
+  PACInst.addOperand(MCOperand::createReg(AArch64::X16));
+  PACInst.addOperand(MCOperand::createReg(AArch64::X16));
+  if (!PACZero)
+    PACInst.addOperand(MCOperand::createReg(PACDiscReg));
+  EmitToStreamer(*OutStreamer, PACInst);
+  ++InstsEmitted;
+
+  assert(STI->getInstrInfo()->getInstSizeInBytes(*MI) >= InstsEmitted * 4);
+  //  Lend:
+  if (EndSym)
+    OutStreamer->emitLabel(EndSym);
+}
+
 // Simple pseudo-instructions have their lowering (with expansion to real
 // instructions) auto-generated.
 #include "AArch64GenMCPseudoLowering.inc"
@@ -1552,6 +1819,11 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) {
     return;
   }
 
+  case AArch64::AUT:
+  case AArch64::AUTPAC:
+    emitPtrauthAuthResign(MI);
+    return;
+
   // Tail calls use pseudo instructions so they have the proper code-gen
   // attributes (isCall, isReturn, etc.). We lower them to the real
   // instruction here.
diff --git a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
index 1437a2b5acbcf6f..fa3637acf1648d5 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
@@ -363,6 +363,9 @@ class AArch64DAGToDAGISel : public SelectionDAGISel {
 
   bool tryIndexedLoad(SDNode *N);
 
+  void SelectPtrauthAuth(SDNode *N);
+  void SelectPtrauthResign(SDNode *N);
+
   bool trySelectStackSlotTagP(SDNode *N);
   void SelectTagP(SDNode *N);
 
@@ -1460,6 +1463,96 @@ void AArch64DAGToDAGISel::SelectTable(SDNode *N, unsigned NumVecs, unsigned Opc,
   ReplaceNode(N, CurDAG->getMachineNode(Opc, dl, VT, Ops));
 }
 
+static std::tuple<SDValue, SDValue>
+extractPtrauthBlendDiscriminators(SDValue Disc, SelectionDAG *DAG) {
+  SDLoc DL(Disc);
+  SDValue AddrDisc;
+  SDValue ConstDisc;
+
+  // If this is a blend, remember the constant and address discriminators.
+  // Otherwise, it's either a constant discriminator, or a non-blended
+  // address discriminator.
+  if (Disc->getOpcode() == ISD::INTRINSIC_WO_CHAIN &&
+      Disc->getConstantOperandVal(0) == Intrinsic::ptrauth_blend) {
+    AddrDisc = Disc->getOperand(1);
+    ConstDisc = Disc->getOperand(2);
+  } else {
+    ConstDisc = Disc;
+  }
+
+  // If the constant discriminator (either the blend RHS, or the entire
+  // discriminator value) isn't a 16-bit constant, bail out, and let the
+  // discriminator be computed separately.
+  auto *ConstDiscN = dyn_cast<ConstantSDNode>(ConstDisc);
+  if (!ConstDiscN || !isUInt<16>(ConstDiscN->getZExtValue()))
+    return std::make_tuple(DAG->getTargetConstant(0, DL, MVT::i64), Disc);
+
+  // If there's no address discriminator, use XZR directly.
+  if (!AddrDisc)
+    AddrDisc = DAG->getRegister(AArch64::XZR, MVT::i64);
+
+  return std::make_tuple(
+      DAG->getTargetConstant(ConstDiscN->getZExtValue(), DL, MVT::i64),
+      AddrDisc);
+}
+
+void AArch64DAGToDAGISel::SelectPtrauthAuth(SDNode *N) {
+  SDLoc DL(N);
+  // IntrinsicID is operand #0
+  SDValue Val = N->getOperand(1);
+  SDValue AUTKey = N->getOperand(2);
+  SDValue AUTDisc = N->getOperand(3);
+
+  unsigned AUTKeyC = cast<ConstantSDNode>(AUTKey)->getZExtValue();
+  AUTKey = CurDAG->getTargetConstant(AUTKeyC, DL, MVT::i64);
+
+  SDValue AUTAddrDisc, AUTConstDisc;
+  std::tie(AUTConstDisc, AUTAddrDisc) =
+      extractPtrauthBlendDiscriminators(AUTDisc, CurDAG);
+
+  SDValue X16Copy = CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL,
+                                         AArch64::X16, Val, SDValue());
+  SDValue Ops[] = {AUTKey, AUTConstDisc, AUTAddrDisc, X16Copy.getValue(1)};
+
+  SDNode *AUT = CurDAG->getMachineNode(AArch64::AUT, DL, MVT::i64, Ops);
+  ReplaceNode(N, AUT);
+  return;
+}
+
+void AArch64DAGToDAGISel::SelectPtrauthResign(SDNode *N) {
+  SDLoc DL(N);
+  // IntrinsicID is operand #0
+  SDValue Val = N->getOperand(1);
+  SDValue AUTKey = N->getOperand(2);
+  SDValue AUTDisc = N->getOperand(3);
+  SDValue PACKey = N->getOperand(4);
+  SDValue PACDisc = N->getOperand(5);
+
+  unsigned AUTKeyC = cast<ConstantSDNode>(AUTKey)->getZExtValue();
+  unsigned PACKeyC = cast<ConstantSDNode>(PACKey)->getZExtValue();
+
+  AUTKey = CurDAG->getTargetConstant(AUTKeyC, DL, MVT::i64);
+  PACKey = CurDAG->getTargetConstant(PACKeyC, DL, MVT::i64);
+
+  SDValue AUTAddrDisc, AUTConstDisc;
+  std::tie(AUTConstDisc, AUTAddrDisc) =
+      extractPtrauthBlendDiscriminators(AUTDisc, CurDAG);
+
+  SDValue PACAddrDisc, PACConstDisc;
+  std::tie(PACConstDisc, PACAddrDisc) =
+      extractPtrauthBlendDiscriminators(PACDisc, CurDAG);
+
+  SDValue X16Copy = CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL,
+                                         AArch64::X16, Val, SDValue());
+
+  SDValue Ops[] = {AUTKey,       AUTConstDisc, AUTAddrDisc,        PACKey,
+                   PACConstDisc, PACAddrDisc,  X16Copy.getValue(1)};
+
+  SDNode *AUTPAC = CurDAG->getMachineNode(AArch64::AUTPAC, DL, MVT::i64, Ops);
+  ReplaceNode(N, AUTPAC);
+  return;
+}
+
 bool AArch64DAGToDAGISel::tryIndexedLoad(SDNode *N) {
   LoadSDNode *LD = cast<LoadSDNode>(N);
   if (LD->isUnindexed())
@@ -5233,6 +5326,15 @@ void AArch64DAGToDAGISel::Select(SDNode *Node) {
     case Intrinsic::aarch64_tagp:
       SelectTagP(Node);
       return;
+
+    case Intrinsic::ptrauth_auth:
+      SelectPtrauthAuth(Node);
+      return;
+
+    case Intrinsic::ptrauth_resign:
+      SelectPtrauthResign(Node);
+      return;
+
     case Intrinsic::aarch64_neon_tbl2:
       SelectTable(Node, 2,
                   VT == MVT::v8i8 ? AArch64::TBLv8i8Two : AArch64::TBLv16i8Two,
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index ddbe840079a573b..964bb431a652a25 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -1648,6 +1648,36 @@ let Predicates = [HasPAuth] in {
   defm LDRAA  : AuthLoad<0, "ldraa", simm10Scaled>;
   defm LDRAB  : AuthLoad<1, "ldrab", simm10Scaled>;
 
+  // AUT pseudo.
+  // This directly manipulates x16/x17, which are the only registers the OS
+  // guarantees are safe to use for sensitive operations.
+  def AUT : Pseudo<(outs), (ins i32imm:$Key, i64imm:$Disc, GPR64noip:$AddrDisc),
+                   []>, Sched<[WriteI, ReadI]> {
+    let isCodeGenOnly = 1;
+    let hasSideEffects = 1;
+    let mayStore = 0;
+    let mayLoad = 0;
+    let Size = 32;
+    let Defs = [X16,X17,NZCV];
+    let Uses = [X16];
+  }
+
+  // AUT and re-PAC a value, using different keys/data.
+  // This directly manipulates x16/x17, which are the only registers the OS
+  // guarantees are safe to use for sensitive operations.
+  def AUTPAC
+      : Pseudo<(outs),
+               (ins i32imm:$AUTKey, i64imm:$AUTDisc, GPR64noip:$AUTAddrDisc,
+                    i32imm:$PACKey, i64imm:$PACDisc, GPR64noip:$PACAddrDisc),
+               []>, Sched<[WriteI, ReadI]> {
+    let isCodeGenOnly = 1;
+    let hasSideEffects = 1;
+    let mayStore = 0;
+    let mayLoad = 0;
+    let Size = 48;
+    let Defs = [X16,X17,NZCV];
+    let Uses = [X16];
+  }
 }
 
 // v9.5-A pointer authentication extensions
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64GlobalISelUtils.cpp b/llvm/lib/Target/AArch64/GISel/AArch64GlobalISelUtils.cpp
index 92db89cc0915b86..80d52506e8fcb05 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64GlobalISelUtils.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64GlobalISelUtils.cpp
@@ -96,6 +96,35 @@ bool AArch64GISelUtils::tryEmitBZero(MachineInstr &MI,
   return true;
 }
 
+std::tuple<uint16_t, Register>
+AArch64GISelUtils::extractPtrauthBlendDiscriminators(Register Disc,
+                                                     MachineRegisterInfo &MRI) {
+  Register AddrDisc = Disc;
+  uint16_t ConstDisc = 0;
+
+  if (auto ConstDiscVal = getIConstantVRegVal(Disc, MRI)) {
+    if (isUInt<16>(ConstDiscVal->getZExtValue())) {
+      ConstDisc = ConstDiscVal->getZExtValue();
+      AddrDisc = AArch64::XZR;
+    }
+    return std::make_tuple(ConstDisc, AddrDisc);
+  }
+
+  auto *DiscMI = MRI.getVRegDef(Disc);
+  if (!DiscMI || DiscMI->getOpcode() != TargetOpcode::G_INTRINSIC ||
+      DiscMI->getOperand(1).getIntrinsicID() != Intrinsic::ptrauth_blend)
+    return std::make_tuple(ConstDisc, AddrDisc);
+
+  if (auto ConstDiscVal =
+      getIConstantVRegVal(DiscMI->getOperand(3).getReg(), MRI)) {
+    if (isUInt<16>(ConstDiscVal->getZExtValue())) {
+      ConstDisc = ConstDiscVal->getZExtValue();
+      AddrDisc = DiscMI->getOperand(2).getReg();
+    }
+  }
+  return std::make_tuple(ConstDisc, AddrDisc);
+}
+
 void AArch64GISelUtils::changeFCMPPredToAArch64CC(
     const CmpInst::Predicate P, AArch64CC::CondCode &CondCode,
     AArch64CC::CondCode &CondCode2) {
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64GlobalISelUtils.h b/llvm/lib/Target/AArch64/GISel/AArch64GlobalISelUtils.h
index 791db7efaf0bee3..9ef833f0fc0ca1e 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64GlobalISelUtils.h
+++ b/llvm/lib/Target/AArch64/GISel/AArch64GlobalISelUtils.h
@@ -53,6 +53,12 @@ bool isCMN(const MachineInstr *MaybeSub, const CmpInst::Predicate &Pred,
 /// \returns true if \p MI was replaced with a G_BZERO.
 bool tryEmitBZero(MachineInstr &MI, MachineIRBuilder &MIRBuilder, bool MinSize);
 
+/// Analyze a ptrauth discriminator value to try to find the constant integer
+/// and address parts, cracking a ptrauth_blend intrinsic if there is one.
+/// \returns integer/address disc. parts, with NoRegister if no address disc.
+std::tuple<uint16_t, Register>
+extractPtrauthBlendDiscriminators(Register Disc, MachineRegisterInfo &MRI);
+
 /// Find the AArch64 condition codes necessary to represent \p P for a scalar
 /// floating point comparison.
 ///
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
index 8344e79f78e1eb6..ed5293959070670 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
@@ -6716,6 +6716,64 @@ bool AArch64InstructionSelector::selectIntrinsic(MachineInstr &I,
     I.eraseFromParent();
     return true;
   }
+  case Intrinsic::ptrauth_resign: {
+    Register DstReg = I.getOperand(0).getReg();
+    Register ValReg = I.getOperand(2).getReg();
+    uint64_t AUTKey = I.getOperand(3).getImm();
+    Register AUTDisc = I.getOperand(4).getReg();
+    uint64_t PACKey = I.getOperand(5).getImm();
+    Register PACDisc = I.getOperand(6).getReg();
+
+    Register AUTAddrDisc = AUTDisc;
+    uint16_t AUTConstDiscC = 0;
+    std::tie(AUTConstDiscC, AUTAddrDisc) =
+      extractPtrauthBlendDiscriminators(AUTDisc, MRI);
+
+    Register PACAddrDisc = PACDisc;
+    uint16_t PACConstDiscC = 0;
+    std::tie(PACConstDiscC, PACAddrDisc) =
+      extractPtrauthBlendDiscriminators(PACDisc, MRI);
+
+    MIB.buildCopy({AArch64::X16}, {ValReg});
+    MIB.buildInstr(TargetOpcode::IMPLICIT_DEF, {AArch64::X17}, {});
+    MIB.buildInstr(AArch64::AUTPAC)
+        .addImm(AUTKey)
+        .addImm(AUTConstDiscC)
+        .addUse(AUTAddrDisc)
+        .addImm(PACKey)
+        .addImm(PACConstDiscC)
+        .addUse(PACAddrDisc)
+        .constrainAllUses(TII, TRI, RBI);
+    MIB.buildCopy({DstReg}, Register(AArch64::X16));
+
+    RBI.constrainGenericRegister(DstReg, AArch64::GPR64RegClass, MRI);
+    I.eraseFromParent();
+    return true;
+  }
+  case Intrinsic::ptrauth_auth: {
+    Register DstReg = I.getOperand(0).getReg();
+    Register ValReg = I.getOperand(2).getReg();
+    uint64_t AUTKey = I.getOperand(3).getImm();
+    Register AUTDisc = I.getOperand(4).getReg();
+
+    Register AUTAddrDisc = AUTDisc;
+    uint16_t AUTConstDiscC = 0;
+    std::tie(AUTConstDiscC, AUTAddrDisc) =
+      extractPtrauthBlendDiscriminators(AUTDisc, MRI);
+
+    MIB.buildCopy({AArch64::X16}, {ValReg});
+    MIB.buildInstr(TargetOpcode::IMPLICIT_DEF, {AArch64::X17}, {});
+    MIB.buildInstr(AArch64::AUT)
+        .addImm(AUTKey)
+        .addImm(AUTConstDiscC)
+        .addUse(AUTAddrDisc)
+        .constrainAllUses(TII, TRI, RBI);
+    MIB.buildCopy({DstReg}, Register(AArch64::X16));
+
+    RBI.constrainGenericRegister(DstReg, AArch64::GPR64RegClass, MRI);
+    I.eraseFromParent();
+    return true;
+  }
   case Intrinsic::frameaddress:
   case Intrinsic::returnaddress: {
     MachineFunction &MF = *I.getParent()->getParent();
diff --git a/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-resign-with-blend.ll b/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-resign-with-blend.ll
new file mode 100644
index 000000000000000..5de08f587477f80
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-resign-with-blend.ll
@@ -0,0 +1,261 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel=0                    -verify-machineinstrs \
+; RUN:   -aarch64-ptrauth-auth-checks=none | FileCheck %s --check-prefixes=UNCHECKED,SDAG-UNCHECKED
+; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel -global-isel-abort=1 -verify-machineinstrs \
+; RUN:   -aarch64-ptrauth-auth-checks=none | FileCheck %s --check-prefixes=UNCHECKED,GISEL-UNCHECKED
+
+; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel=0                    -verify-machineinstrs \
+; RUN:                                     | FileCheck %s --check-prefixes=CHECKED,SDAG-CHECKED
+; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel -global-isel-abort=1 -verify-machineinstrs \
+; RUN:                                     | FileCheck %s --check-prefixes=CHECKED,GISEL-CHECKED
+
+; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel=0                    -verify-machineinstrs \
+; RUN:   -aarch64-ptrauth-auth-checks=trap | FileCheck %s --check-prefixes=TRAP,SDAG-TRAP
+; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel -global-isel-abort=1 -verify-machineinstrs \
+; RUN:   -aarch64-ptrauth-auth-checks=trap | FileCheck %s --check-prefixes=TRAP,GISEL-TRAP
+
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
+
+define i64 @test_auth_blend(i64 %arg, i64 %arg1) {
+; UNCHECKED-LABEL: test_auth_blend:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    mov x17, x1
+; UNCHECKED-NEXT:    movk x17, #65535, lsl #48
+; UNCHECKED-NEXT:    autda x16, x17
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_auth_blend:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    mov x17, x1
+; CHECKED-NEXT:    movk x17, #65535, lsl #48
+; CHECKED-NEXT:    autda x16, x17
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_auth_blend:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    mov x17, x1
+; TRAP-NEXT:    movk x17, #65535, lsl #48
+; TRAP-NEXT:    autda x16, x17
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpacd x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_0
+; TRAP-NEXT:    brk #0xc472
+; TRAP-NEXT:  Lauth_success_0:
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp0 = call i64 @llvm.ptrauth.blend(i64 %arg1, i64 65535)
+  %tmp1 = call i64 @llvm.ptrauth.auth(i64 %arg, i32 2, i64 %tmp0)
+  ret i64 %tmp1
+}
+
+define i64 @test_resign_blend(i64 %arg, i64 %arg1, i64 %arg2) {
+; UNCHECKED-LABEL: test_resign_blend:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    mov x17, x1
+; UNCHECKED-NEXT:    movk x17, #12345, lsl #48
+; UNCHECKED-NEXT:    autda x16, x17
+; UNCHECKED-NEXT:    mov x17, x2
+; UNCHECKED-NEXT:    movk x17, #56789, lsl #48
+; UNCHECKED-NEXT:    pacdb x16, x17
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_blend:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    mov x17, x1
+; CHECKED-NEXT:    movk x17, #12345, lsl #48
+; CHECKED-NEXT:    autda x16, x17
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    xpacd x17
+; CHECKED-NEXT:    cmp x16, x17
+; CHECKED-NEXT:    b.eq Lauth_success_0
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    b Lresign_end_0
+; CHECKED-NEXT:  Lauth_success_0:
+; CHECKED-NEXT:    mov x17, x2
+; CHECKED-NEXT:    movk x17, #56789, lsl #48
+; CHECKED-NEXT:    pacdb x16, x17
+; CHECKED-NEXT:  Lresign_end_0:
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_blend:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    mov x17, x1
+; TRAP-NEXT:    movk x17, #12345, lsl #48
+; TRAP-NEXT:    autda x16, x17
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpacd x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_1
+; TRAP-NEXT:    brk #0xc472
+; TRAP-NEXT:  Lauth_success_1:
+; TRAP-NEXT:    mov x17, x2
+; TRAP-NEXT:    movk x17, #56789, lsl #48
+; TRAP-NEXT:    pacdb x16, x17
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp0 = call i64 @llvm.ptrauth.blend(i64 %arg1, i64 12345)
+  %tmp1 = call i64 @llvm.ptrauth.blend(i64 %arg2, i64 56789)
+  %tmp2 = call i64 @llvm.ptrauth.resign(i64 %arg, i32 2, i64 %tmp0, i32 3, i64 %tmp1)
+  ret i64 %tmp2
+}
+
+define i64 @test_resign_blend_and_const(i64 %arg, i64 %arg1) {
+; UNCHECKED-LABEL: test_resign_blend_and_const:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    mov x17, x1
+; UNCHECKED-NEXT:    movk x17, #12345, lsl #48
+; UNCHECKED-NEXT:    autda x16, x17
+; UNCHECKED-NEXT:    mov x17, #56789 ; =0xddd5
+; UNCHECKED-NEXT:    pacdb x16, x17
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_blend_and_const:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    mov x17, x1
+; CHECKED-NEXT:    movk x17, #12345, lsl #48
+; CHECKED-NEXT:    autda x16, x17
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    xpacd x17
+; CHECKED-NEXT:    cmp x16, x17
+; CHECKED-NEXT:    b.eq Lauth_success_1
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    b Lresign_end_1
+; CHECKED-NEXT:  Lauth_success_1:
+; CHECKED-NEXT:    mov x17, #56789 ; =0xddd5
+; CHECKED-NEXT:    pacdb x16, x17
+; CHECKED-NEXT:  Lresign_end_1:
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_blend_and_const:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    mov x17, x1
+; TRAP-NEXT:    movk x17, #12345, lsl #48
+; TRAP-NEXT:    autda x16, x17
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpacd x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_2
+; TRAP-NEXT:    brk #0xc472
+; TRAP-NEXT:  Lauth_success_2:
+; TRAP-NEXT:    mov x17, #56789 ; =0xddd5
+; TRAP-NEXT:    pacdb x16, x17
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp0 = call i64 @llvm.ptrauth.blend(i64 %arg1, i64 12345)
+  %tmp1 = call i64 @llvm.ptrauth.resign(i64 %arg, i32 2, i64 %tmp0, i32 3, i64 56789)
+  ret i64 %tmp1
+}
+
+define i64 @test_resign_blend_and_addr(i64 %arg, i64 %arg1, i64 %arg2) {
+; UNCHECKED-LABEL: test_resign_blend_and_addr:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    mov x17, x1
+; UNCHECKED-NEXT:    movk x17, #12345, lsl #48
+; UNCHECKED-NEXT:    autda x16, x17
+; UNCHECKED-NEXT:    pacdb x16, x2
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_blend_and_addr:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    mov x17, x1
+; CHECKED-NEXT:    movk x17, #12345, lsl #48
+; CHECKED-NEXT:    autda x16, x17
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    xpacd x17
+; CHECKED-NEXT:    cmp x16, x17
+; CHECKED-NEXT:    b.eq Lauth_success_2
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    b Lresign_end_2
+; CHECKED-NEXT:  Lauth_success_2:
+; CHECKED-NEXT:    pacdb x16, x2
+; CHECKED-NEXT:  Lresign_end_2:
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_blend_and_addr:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    mov x17, x1
+; TRAP-NEXT:    movk x17, #12345, lsl #48
+; TRAP-NEXT:    autda x16, x17
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpacd x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_3
+; TRAP-NEXT:    brk #0xc472
+; TRAP-NEXT:  Lauth_success_3:
+; TRAP-NEXT:    pacdb x16, x2
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp0 = call i64 @llvm.ptrauth.blend(i64 %arg1, i64 12345)
+  %tmp1 = call i64 @llvm.ptrauth.resign(i64 %arg, i32 2, i64 %tmp0, i32 3, i64 %arg2)
+  ret i64 %tmp1
+}
+
+define i64 @test_auth_too_large_discriminator(i64 %arg, i64 %arg1) {
+; UNCHECKED-LABEL: test_auth_too_large_discriminator:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov w8, #65536 ; =0x10000
+; UNCHECKED-NEXT:    bfi x1, x8, #48, #16
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    autda x16, x1
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_auth_too_large_discriminator:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov w8, #65536 ; =0x10000
+; CHECKED-NEXT:    bfi x1, x8, #48, #16
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    autda x16, x1
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_auth_too_large_discriminator:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov w8, #65536 ; =0x10000
+; TRAP-NEXT:    bfi x1, x8, #48, #16
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    autda x16, x1
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpacd x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_4
+; TRAP-NEXT:    brk #0xc472
+; TRAP-NEXT:  Lauth_success_4:
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp0 = call i64 @llvm.ptrauth.blend(i64 %arg1, i64 65536)
+  %tmp1 = call i64 @llvm.ptrauth.auth(i64 %arg, i32 2, i64 %tmp0)
+  ret i64 %tmp1
+}
+
+declare i64 @llvm.ptrauth.auth(i64, i32, i64)
+declare i64 @llvm.ptrauth.resign(i64, i32, i64, i32, i64)
+declare i64 @llvm.ptrauth.blend(i64, i64)
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; GISEL-CHECKED: {{.*}}
+; GISEL-TRAP: {{.*}}
+; GISEL-UNCHECKED: {{.*}}
+; SDAG-CHECKED: {{.*}}
+; SDAG-TRAP: {{.*}}
+; SDAG-UNCHECKED: {{.*}}
diff --git a/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-resign.ll b/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-resign.ll
new file mode 100644
index 000000000000000..e818d2aa982a840
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-resign.ll
@@ -0,0 +1,764 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel=0                    -verify-machineinstrs \
+; RUN:   -aarch64-ptrauth-auth-checks=none | FileCheck %s --check-prefixes=UNCHECKED
+; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel -global-isel-abort=1 -verify-machineinstrs \
+; RUN:   -aarch64-ptrauth-auth-checks=none | FileCheck %s --check-prefixes=UNCHECKED
+
+; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel=0                    -verify-machineinstrs \
+; RUN:                                     | FileCheck %s --check-prefixes=CHECKED
+; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel -global-isel-abort=1 -verify-machineinstrs \
+; RUN:                                     | FileCheck %s --check-prefixes=CHECKED
+
+; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel=0                    -verify-machineinstrs \
+; RUN:   -aarch64-ptrauth-auth-checks=trap | FileCheck %s --check-prefixes=TRAP
+; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel -global-isel-abort=1 -verify-machineinstrs \
+; RUN:   -aarch64-ptrauth-auth-checks=trap | FileCheck %s --check-prefixes=TRAP
+
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
+
+define i64 @test_auth_ia(i64 %arg, i64 %arg1) {
+; UNCHECKED-LABEL: test_auth_ia:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    autia x16, x1
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_auth_ia:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    autia x16, x1
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_auth_ia:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    autia x16, x1
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpaci x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_0
+; TRAP-NEXT:    brk #0xc470
+; TRAP-NEXT:  Lauth_success_0:
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 0, i64 %arg1)
+  ret i64 %tmp
+}
+
+define i64 @test_auth_ia_zero(i64 %arg) {
+; UNCHECKED-LABEL: test_auth_ia_zero:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    autiza x16
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_auth_ia_zero:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    autiza x16
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_auth_ia_zero:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    autiza x16
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpaci x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_1
+; TRAP-NEXT:    brk #0xc470
+; TRAP-NEXT:  Lauth_success_1:
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 0, i64 0)
+  ret i64 %tmp
+}
+
+define i64 @test_auth_ib(i64 %arg, i64 %arg1) {
+; UNCHECKED-LABEL: test_auth_ib:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    autib x16, x1
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_auth_ib:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    autib x16, x1
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_auth_ib:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    autib x16, x1
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpaci x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_2
+; TRAP-NEXT:    brk #0xc471
+; TRAP-NEXT:  Lauth_success_2:
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 1, i64 %arg1)
+  ret i64 %tmp
+}
+
+define i64 @test_auth_ib_zero(i64 %arg) {
+; UNCHECKED-LABEL: test_auth_ib_zero:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    autizb x16
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_auth_ib_zero:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    autizb x16
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_auth_ib_zero:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    autizb x16
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpaci x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_3
+; TRAP-NEXT:    brk #0xc471
+; TRAP-NEXT:  Lauth_success_3:
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 1, i64 0)
+  ret i64 %tmp
+}
+
+define i64 @test_auth_da(i64 %arg, i64 %arg1) {
+; UNCHECKED-LABEL: test_auth_da:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    autda x16, x1
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_auth_da:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    autda x16, x1
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_auth_da:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    autda x16, x1
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpacd x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_4
+; TRAP-NEXT:    brk #0xc472
+; TRAP-NEXT:  Lauth_success_4:
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 2, i64 %arg1)
+  ret i64 %tmp
+}
+
+define i64 @test_auth_da_zero(i64 %arg) {
+; UNCHECKED-LABEL: test_auth_da_zero:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    autdza x16
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_auth_da_zero:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    autdza x16
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_auth_da_zero:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    autdza x16
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpacd x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_5
+; TRAP-NEXT:    brk #0xc472
+; TRAP-NEXT:  Lauth_success_5:
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 2, i64 0)
+  ret i64 %tmp
+}
+
+define i64 @test_auth_db(i64 %arg, i64 %arg1) {
+; UNCHECKED-LABEL: test_auth_db:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    autdb x16, x1
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_auth_db:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    autdb x16, x1
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_auth_db:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    autdb x16, x1
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpacd x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_6
+; TRAP-NEXT:    brk #0xc473
+; TRAP-NEXT:  Lauth_success_6:
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 3, i64 %arg1)
+  ret i64 %tmp
+}
+
+define i64 @test_auth_db_zero(i64 %arg) {
+; UNCHECKED-LABEL: test_auth_db_zero:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    autdzb x16
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_auth_db_zero:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    autdzb x16
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_auth_db_zero:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    autdzb x16
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpacd x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_7
+; TRAP-NEXT:    brk #0xc473
+; TRAP-NEXT:  Lauth_success_7:
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 3, i64 0)
+  ret i64 %tmp
+}
+
+; Note that this might seem like a no-op but is actually a valid way to enforce
+; the validity of a signature.
+define i64 @test_resign_ia_ia(i64 %arg, i64 %arg1, i64 %arg2) {
+; UNCHECKED-LABEL: test_resign_ia_ia:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    autia x16, x1
+; UNCHECKED-NEXT:    pacia x16, x2
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_ia_ia:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    autia x16, x1
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    xpaci x17
+; CHECKED-NEXT:    cmp x16, x17
+; CHECKED-NEXT:    b.eq Lauth_success_0
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    b Lresign_end_0
+; CHECKED-NEXT:  Lauth_success_0:
+; CHECKED-NEXT:    pacia x16, x2
+; CHECKED-NEXT:  Lresign_end_0:
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_ia_ia:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    autia x16, x1
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpaci x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_8
+; TRAP-NEXT:    brk #0xc470
+; TRAP-NEXT:  Lauth_success_8:
+; TRAP-NEXT:    pacia x16, x2
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 0, i64 %arg1, i32 0, i64 %arg2)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_ib_ia(i64 %arg, i64 %arg1, i64 %arg2) {
+; UNCHECKED-LABEL: test_resign_ib_ia:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    autib x16, x1
+; UNCHECKED-NEXT:    pacia x16, x2
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_ib_ia:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    autib x16, x1
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    xpaci x17
+; CHECKED-NEXT:    cmp x16, x17
+; CHECKED-NEXT:    b.eq Lauth_success_1
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    b Lresign_end_1
+; CHECKED-NEXT:  Lauth_success_1:
+; CHECKED-NEXT:    pacia x16, x2
+; CHECKED-NEXT:  Lresign_end_1:
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_ib_ia:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    autib x16, x1
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpaci x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_9
+; TRAP-NEXT:    brk #0xc471
+; TRAP-NEXT:  Lauth_success_9:
+; TRAP-NEXT:    pacia x16, x2
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 1, i64 %arg1, i32 0, i64 %arg2)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_da_ia(i64 %arg, i64 %arg1, i64 %arg2) {
+; UNCHECKED-LABEL: test_resign_da_ia:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    autda x16, x1
+; UNCHECKED-NEXT:    pacia x16, x2
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_da_ia:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    autda x16, x1
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    xpacd x17
+; CHECKED-NEXT:    cmp x16, x17
+; CHECKED-NEXT:    b.eq Lauth_success_2
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    b Lresign_end_2
+; CHECKED-NEXT:  Lauth_success_2:
+; CHECKED-NEXT:    pacia x16, x2
+; CHECKED-NEXT:  Lresign_end_2:
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_da_ia:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    autda x16, x1
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpacd x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_10
+; TRAP-NEXT:    brk #0xc472
+; TRAP-NEXT:  Lauth_success_10:
+; TRAP-NEXT:    pacia x16, x2
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 2, i64 %arg1, i32 0, i64 %arg2)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_db_ia(i64 %arg, i64 %arg1, i64 %arg2) {
+; UNCHECKED-LABEL: test_resign_db_ia:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    autdb x16, x1
+; UNCHECKED-NEXT:    pacia x16, x2
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_db_ia:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    autdb x16, x1
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    xpacd x17
+; CHECKED-NEXT:    cmp x16, x17
+; CHECKED-NEXT:    b.eq Lauth_success_3
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    b Lresign_end_3
+; CHECKED-NEXT:  Lauth_success_3:
+; CHECKED-NEXT:    pacia x16, x2
+; CHECKED-NEXT:  Lresign_end_3:
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_db_ia:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    autdb x16, x1
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpacd x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_11
+; TRAP-NEXT:    brk #0xc473
+; TRAP-NEXT:  Lauth_success_11:
+; TRAP-NEXT:    pacia x16, x2
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 3, i64 %arg1, i32 0, i64 %arg2)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_db_ib(i64 %arg, i64 %arg1, i64 %arg2) {
+; UNCHECKED-LABEL: test_resign_db_ib:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    autdb x16, x1
+; UNCHECKED-NEXT:    pacib x16, x2
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_db_ib:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    autdb x16, x1
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    xpacd x17
+; CHECKED-NEXT:    cmp x16, x17
+; CHECKED-NEXT:    b.eq Lauth_success_4
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    b Lresign_end_4
+; CHECKED-NEXT:  Lauth_success_4:
+; CHECKED-NEXT:    pacib x16, x2
+; CHECKED-NEXT:  Lresign_end_4:
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_db_ib:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    autdb x16, x1
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpacd x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_12
+; TRAP-NEXT:    brk #0xc473
+; TRAP-NEXT:  Lauth_success_12:
+; TRAP-NEXT:    pacib x16, x2
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 3, i64 %arg1, i32 1, i64 %arg2)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_db_da(i64 %arg, i64 %arg1, i64 %arg2) {
+; UNCHECKED-LABEL: test_resign_db_da:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    autdb x16, x1
+; UNCHECKED-NEXT:    pacda x16, x2
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_db_da:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    autdb x16, x1
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    xpacd x17
+; CHECKED-NEXT:    cmp x16, x17
+; CHECKED-NEXT:    b.eq Lauth_success_5
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    b Lresign_end_5
+; CHECKED-NEXT:  Lauth_success_5:
+; CHECKED-NEXT:    pacda x16, x2
+; CHECKED-NEXT:  Lresign_end_5:
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_db_da:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    autdb x16, x1
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpacd x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_13
+; TRAP-NEXT:    brk #0xc473
+; TRAP-NEXT:  Lauth_success_13:
+; TRAP-NEXT:    pacda x16, x2
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 3, i64 %arg1, i32 2, i64 %arg2)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_db_db(i64 %arg, i64 %arg1, i64 %arg2) {
+; UNCHECKED-LABEL: test_resign_db_db:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    autdb x16, x1
+; UNCHECKED-NEXT:    pacdb x16, x2
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_db_db:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    autdb x16, x1
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    xpacd x17
+; CHECKED-NEXT:    cmp x16, x17
+; CHECKED-NEXT:    b.eq Lauth_success_6
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    b Lresign_end_6
+; CHECKED-NEXT:  Lauth_success_6:
+; CHECKED-NEXT:    pacdb x16, x2
+; CHECKED-NEXT:  Lresign_end_6:
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_db_db:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    autdb x16, x1
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpacd x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_14
+; TRAP-NEXT:    brk #0xc473
+; TRAP-NEXT:  Lauth_success_14:
+; TRAP-NEXT:    pacdb x16, x2
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 3, i64 %arg1, i32 3, i64 %arg2)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_iza_db(i64 %arg, i64 %arg1, i64 %arg2) {
+; UNCHECKED-LABEL: test_resign_iza_db:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    autiza x16
+; UNCHECKED-NEXT:    pacdb x16, x2
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_iza_db:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    autiza x16
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    xpaci x17
+; CHECKED-NEXT:    cmp x16, x17
+; CHECKED-NEXT:    b.eq Lauth_success_7
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    b Lresign_end_7
+; CHECKED-NEXT:  Lauth_success_7:
+; CHECKED-NEXT:    pacdb x16, x2
+; CHECKED-NEXT:  Lresign_end_7:
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_iza_db:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    autiza x16
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpaci x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_15
+; TRAP-NEXT:    brk #0xc470
+; TRAP-NEXT:  Lauth_success_15:
+; TRAP-NEXT:    pacdb x16, x2
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 0, i64 0, i32 3, i64 %arg2)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_da_dzb(i64 %arg, i64 %arg1, i64 %arg2) {
+; UNCHECKED-LABEL: test_resign_da_dzb:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    autda x16, x1
+; UNCHECKED-NEXT:    pacdzb x16
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_da_dzb:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    autda x16, x1
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    xpacd x17
+; CHECKED-NEXT:    cmp x16, x17
+; CHECKED-NEXT:    b.eq Lauth_success_8
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    b Lresign_end_8
+; CHECKED-NEXT:  Lauth_success_8:
+; CHECKED-NEXT:    pacdzb x16
+; CHECKED-NEXT:  Lresign_end_8:
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_da_dzb:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    autda x16, x1
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpacd x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_16
+; TRAP-NEXT:    brk #0xc472
+; TRAP-NEXT:  Lauth_success_16:
+; TRAP-NEXT:    pacdzb x16
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 2, i64 %arg1, i32 3, i64 0)
+  ret i64 %tmp
+}
+
+define i64 @test_auth_trap_attribute(i64 %arg, i64 %arg1) "ptrauth-auth-traps" {
+; UNCHECKED-LABEL: test_auth_trap_attribute:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    autia x16, x1
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_auth_trap_attribute:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    autia x16, x1
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    xpaci x17
+; CHECKED-NEXT:    cmp x16, x17
+; CHECKED-NEXT:    b.eq Lauth_success_9
+; CHECKED-NEXT:    brk #0xc470
+; CHECKED-NEXT:  Lauth_success_9:
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_auth_trap_attribute:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    autia x16, x1
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpaci x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_17
+; TRAP-NEXT:    brk #0xc470
+; TRAP-NEXT:  Lauth_success_17:
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 0, i64 %arg1)
+  ret i64 %tmp
+}
+
+define i64 @test_auth_ia_constdisc(i64 %arg) {
+; UNCHECKED-LABEL: test_auth_ia_constdisc:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    mov x17, #256
+; UNCHECKED-NEXT:    autia x16, x17
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_auth_ia_constdisc:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    mov x17, #256
+; CHECKED-NEXT:    autia x16, x17
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_auth_ia_constdisc:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    mov x17, #256
+; TRAP-NEXT:    autia x16, x17
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpaci x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_18
+; TRAP-NEXT:    brk #0xc470
+; TRAP-NEXT:  Lauth_success_18:
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 0, i64 256)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_da_constdisc(i64 %arg, i64 %arg1) {
+; UNCHECKED-LABEL: test_resign_da_constdisc:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    autda x16, x1
+; UNCHECKED-NEXT:    mov x17, #256
+; UNCHECKED-NEXT:    pacda x16, x17
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_da_constdisc:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    autda x16, x1
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    xpacd x17
+; CHECKED-NEXT:    cmp x16, x17
+; CHECKED-NEXT:    b.eq Lauth_success_10
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    b Lresign_end_9
+; CHECKED-NEXT:  Lauth_success_10:
+; CHECKED-NEXT:    mov x17, #256
+; CHECKED-NEXT:    pacda x16, x17
+; CHECKED-NEXT:  Lresign_end_9:
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_da_constdisc:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    autda x16, x1
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpacd x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq Lauth_success_19
+; TRAP-NEXT:    brk #0xc472
+; TRAP-NEXT:  Lauth_success_19:
+; TRAP-NEXT:    mov x17, #256
+; TRAP-NEXT:    pacda x16, x17
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 2, i64 %arg1, i32 2, i64 256)
+  ret i64 %tmp
+}
+
+declare i64 @llvm.ptrauth.auth(i64, i32, i64)
+declare i64 @llvm.ptrauth.resign(i64, i32, i64, i32, i64)

>From eac305fde591478027eec83ffac15541669adbde Mon Sep 17 00:00:00 2001
From: Ahmed Bougacha <ahmed at bougacha.org>
Date: Mon, 24 Oct 2022 08:39:10 -0700
Subject: [PATCH 2/3] [AArch64] Add FPAC feature.

---
 llvm/lib/Target/AArch64/AArch64.td          | 3 +++
 llvm/lib/Target/AArch64/AArch64InstrInfo.td | 2 ++
 2 files changed, 5 insertions(+)

diff --git a/llvm/lib/Target/AArch64/AArch64.td b/llvm/lib/Target/AArch64/AArch64.td
index 36700f73df4b20a..61cde7322b9e0f9 100644
--- a/llvm/lib/Target/AArch64/AArch64.td
+++ b/llvm/lib/Target/AArch64/AArch64.td
@@ -459,6 +459,9 @@ def FeatureMatMulFP32 : SubtargetFeature<"f32mm", "HasMatMulFP32",
 def FeatureMatMulFP64 : SubtargetFeature<"f64mm", "HasMatMulFP64",
     "true", "Enable Matrix Multiply FP64 Extension (FEAT_F64MM)", [FeatureSVE]>;
 
+def FeatureFPAC : SubtargetFeature<"fpac", "HasFPAC",
+    "true", "Enable Armv8.6-A Pointer Authentication Faulting enhancement">;
+
 def FeatureXS : SubtargetFeature<"xs", "HasXS",
     "true", "Enable Armv8.7-A limited-TLB-maintenance instruction (FEAT_XS)">;
 
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index 964bb431a652a25..1ada94469d5d116 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -260,6 +260,8 @@ def HasMatMulFP32    : Predicate<"Subtarget->hasMatMulFP32()">,
                        AssemblerPredicateWithAll<(all_of FeatureMatMulFP32), "f32mm">;
 def HasMatMulFP64    : Predicate<"Subtarget->hasMatMulFP64()">,
                        AssemblerPredicateWithAll<(all_of FeatureMatMulFP64), "f64mm">;
+def HasFPAC          : Predicate<"Subtarget->hasFPAC())">,
+                       AssemblerPredicateWithAll<(all_of FeatureFPAC), "fpac">;
 def HasXS            : Predicate<"Subtarget->hasXS()">,
                        AssemblerPredicateWithAll<(all_of FeatureXS), "xs">;
 def HasWFxT          : Predicate<"Subtarget->hasWFxT()">,

>From 20527de61d612da328144cb5e13b5ef16732ea86 Mon Sep 17 00:00:00 2001
From: Ahmed Bougacha <ahmed at bougacha.org>
Date: Mon, 24 Oct 2022 08:36:13 -0700
Subject: [PATCH 3/3] [AArch64][PAC] Don't emit auth/resign checks when
 targeting FPAC.

When the FPAC feature is present, we can rely on its faulting
behavior to avoid emitting the expensive authentication failure
check sequence ourvelves.  In which case we emit the same
sequence as a plain unchecked auth/resign.
---
 llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp |   5 +
 llvm/test/CodeGen/AArch64/ptrauth-fpac.ll     | 374 ++++++++++++++++++
 2 files changed, 379 insertions(+)
 create mode 100644 llvm/test/CodeGen/AArch64/ptrauth-fpac.ll

diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
index a2afbdb9e68820d..b10c1c1712ecfb8 100644
--- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
+++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
@@ -1527,6 +1527,11 @@ void AArch64AsmPrinter::emitPtrauthAuthResign(const MachineInstr *MI) {
   // In the checked sequence, we only trap if explicitly requested.
   bool ShouldTrap = MF->getFunction().hasFnAttribute("ptrauth-auth-traps");
 
+  // On an FPAC CPU, you get traps whether you want them or not: there's
+  // no point in emitting checks or traps.
+  if (STI->hasFPAC())
+    ShouldCheck = ShouldTrap = false;
+
   // However, command-line flags can override this, for experimentation.
   switch (PtrauthAuthChecks) {
   case PtrauthCheckMode::Default: break;
diff --git a/llvm/test/CodeGen/AArch64/ptrauth-fpac.ll b/llvm/test/CodeGen/AArch64/ptrauth-fpac.ll
new file mode 100644
index 000000000000000..6afe1a93d986ede
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/ptrauth-fpac.ll
@@ -0,0 +1,374 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc < %s -mtriple arm64e-apple-darwin                                                -verify-machineinstrs | FileCheck %s --check-prefixes=ALL,NOFPAC
+; RUN: llc < %s -mtriple arm64e-apple-darwin -mattr=+fpac                                   -verify-machineinstrs | FileCheck %s --check-prefixes=ALL,FPAC
+
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
+
+define i64 @test_auth_ia(i64 %arg, i64 %arg1) {
+; ALL-LABEL: test_auth_ia:
+; ALL:       ; %bb.0:
+; ALL-NEXT:    mov x16, x0
+; ALL-NEXT:    autia x16, x1
+; ALL-NEXT:    mov x0, x16
+; ALL-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 0, i64 %arg1)
+  ret i64 %tmp
+}
+
+define i64 @test_auth_ia_zero(i64 %arg) {
+; ALL-LABEL: test_auth_ia_zero:
+; ALL:       ; %bb.0:
+; ALL-NEXT:    mov x16, x0
+; ALL-NEXT:    autiza x16
+; ALL-NEXT:    mov x0, x16
+; ALL-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 0, i64 0)
+  ret i64 %tmp
+}
+
+define i64 @test_auth_ib(i64 %arg, i64 %arg1) {
+; ALL-LABEL: test_auth_ib:
+; ALL:       ; %bb.0:
+; ALL-NEXT:    mov x16, x0
+; ALL-NEXT:    autib x16, x1
+; ALL-NEXT:    mov x0, x16
+; ALL-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 1, i64 %arg1)
+  ret i64 %tmp
+}
+
+define i64 @test_auth_ib_zero(i64 %arg) {
+; ALL-LABEL: test_auth_ib_zero:
+; ALL:       ; %bb.0:
+; ALL-NEXT:    mov x16, x0
+; ALL-NEXT:    autizb x16
+; ALL-NEXT:    mov x0, x16
+; ALL-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 1, i64 0)
+  ret i64 %tmp
+}
+
+define i64 @test_auth_da(i64 %arg, i64 %arg1) {
+; ALL-LABEL: test_auth_da:
+; ALL:       ; %bb.0:
+; ALL-NEXT:    mov x16, x0
+; ALL-NEXT:    autda x16, x1
+; ALL-NEXT:    mov x0, x16
+; ALL-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 2, i64 %arg1)
+  ret i64 %tmp
+}
+
+define i64 @test_auth_da_zero(i64 %arg) {
+; ALL-LABEL: test_auth_da_zero:
+; ALL:       ; %bb.0:
+; ALL-NEXT:    mov x16, x0
+; ALL-NEXT:    autdza x16
+; ALL-NEXT:    mov x0, x16
+; ALL-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 2, i64 0)
+  ret i64 %tmp
+}
+
+define i64 @test_auth_db(i64 %arg, i64 %arg1) {
+; ALL-LABEL: test_auth_db:
+; ALL:       ; %bb.0:
+; ALL-NEXT:    mov x16, x0
+; ALL-NEXT:    autdb x16, x1
+; ALL-NEXT:    mov x0, x16
+; ALL-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 3, i64 %arg1)
+  ret i64 %tmp
+}
+
+define i64 @test_auth_db_zero(i64 %arg) {
+; ALL-LABEL: test_auth_db_zero:
+; ALL:       ; %bb.0:
+; ALL-NEXT:    mov x16, x0
+; ALL-NEXT:    autdzb x16
+; ALL-NEXT:    mov x0, x16
+; ALL-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 3, i64 0)
+  ret i64 %tmp
+}
+
+; Note that this might seem like a no-op but is actually a valid way to enforce
+; the validity of a signature.
+define i64 @test_resign_ia_ia(i64 %arg, i64 %arg1, i64 %arg2) {
+; NOFPAC-LABEL: test_resign_ia_ia:
+; NOFPAC:       ; %bb.0:
+; NOFPAC-NEXT:    mov x16, x0
+; NOFPAC-NEXT:    autia x16, x1
+; NOFPAC-NEXT:    mov x17, x16
+; NOFPAC-NEXT:    xpaci x17
+; NOFPAC-NEXT:    cmp x16, x17
+; NOFPAC-NEXT:    b.eq Lauth_success_0
+; NOFPAC-NEXT:    mov x16, x17
+; NOFPAC-NEXT:    b Lresign_end_0
+; NOFPAC-NEXT:  Lauth_success_0:
+; NOFPAC-NEXT:    pacia x16, x2
+; NOFPAC-NEXT:  Lresign_end_0:
+; NOFPAC-NEXT:    mov x0, x16
+; NOFPAC-NEXT:    ret
+;
+; FPAC-LABEL: test_resign_ia_ia:
+; FPAC:       ; %bb.0:
+; FPAC-NEXT:    mov x16, x0
+; FPAC-NEXT:    autia x16, x1
+; FPAC-NEXT:    pacia x16, x2
+; FPAC-NEXT:    mov x0, x16
+; FPAC-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 0, i64 %arg1, i32 0, i64 %arg2)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_ib_ia(i64 %arg, i64 %arg1, i64 %arg2) {
+; NOFPAC-LABEL: test_resign_ib_ia:
+; NOFPAC:       ; %bb.0:
+; NOFPAC-NEXT:    mov x16, x0
+; NOFPAC-NEXT:    autib x16, x1
+; NOFPAC-NEXT:    mov x17, x16
+; NOFPAC-NEXT:    xpaci x17
+; NOFPAC-NEXT:    cmp x16, x17
+; NOFPAC-NEXT:    b.eq Lauth_success_1
+; NOFPAC-NEXT:    mov x16, x17
+; NOFPAC-NEXT:    b Lresign_end_1
+; NOFPAC-NEXT:  Lauth_success_1:
+; NOFPAC-NEXT:    pacia x16, x2
+; NOFPAC-NEXT:  Lresign_end_1:
+; NOFPAC-NEXT:    mov x0, x16
+; NOFPAC-NEXT:    ret
+;
+; FPAC-LABEL: test_resign_ib_ia:
+; FPAC:       ; %bb.0:
+; FPAC-NEXT:    mov x16, x0
+; FPAC-NEXT:    autib x16, x1
+; FPAC-NEXT:    pacia x16, x2
+; FPAC-NEXT:    mov x0, x16
+; FPAC-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 1, i64 %arg1, i32 0, i64 %arg2)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_da_ia(i64 %arg, i64 %arg1, i64 %arg2) {
+; NOFPAC-LABEL: test_resign_da_ia:
+; NOFPAC:       ; %bb.0:
+; NOFPAC-NEXT:    mov x16, x0
+; NOFPAC-NEXT:    autda x16, x1
+; NOFPAC-NEXT:    mov x17, x16
+; NOFPAC-NEXT:    xpacd x17
+; NOFPAC-NEXT:    cmp x16, x17
+; NOFPAC-NEXT:    b.eq Lauth_success_2
+; NOFPAC-NEXT:    mov x16, x17
+; NOFPAC-NEXT:    b Lresign_end_2
+; NOFPAC-NEXT:  Lauth_success_2:
+; NOFPAC-NEXT:    pacia x16, x2
+; NOFPAC-NEXT:  Lresign_end_2:
+; NOFPAC-NEXT:    mov x0, x16
+; NOFPAC-NEXT:    ret
+;
+; FPAC-LABEL: test_resign_da_ia:
+; FPAC:       ; %bb.0:
+; FPAC-NEXT:    mov x16, x0
+; FPAC-NEXT:    autda x16, x1
+; FPAC-NEXT:    pacia x16, x2
+; FPAC-NEXT:    mov x0, x16
+; FPAC-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 2, i64 %arg1, i32 0, i64 %arg2)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_db_ia(i64 %arg, i64 %arg1, i64 %arg2) {
+; NOFPAC-LABEL: test_resign_db_ia:
+; NOFPAC:       ; %bb.0:
+; NOFPAC-NEXT:    mov x16, x0
+; NOFPAC-NEXT:    autdb x16, x1
+; NOFPAC-NEXT:    mov x17, x16
+; NOFPAC-NEXT:    xpacd x17
+; NOFPAC-NEXT:    cmp x16, x17
+; NOFPAC-NEXT:    b.eq Lauth_success_3
+; NOFPAC-NEXT:    mov x16, x17
+; NOFPAC-NEXT:    b Lresign_end_3
+; NOFPAC-NEXT:  Lauth_success_3:
+; NOFPAC-NEXT:    pacia x16, x2
+; NOFPAC-NEXT:  Lresign_end_3:
+; NOFPAC-NEXT:    mov x0, x16
+; NOFPAC-NEXT:    ret
+;
+; FPAC-LABEL: test_resign_db_ia:
+; FPAC:       ; %bb.0:
+; FPAC-NEXT:    mov x16, x0
+; FPAC-NEXT:    autdb x16, x1
+; FPAC-NEXT:    pacia x16, x2
+; FPAC-NEXT:    mov x0, x16
+; FPAC-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 3, i64 %arg1, i32 0, i64 %arg2)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_db_ib(i64 %arg, i64 %arg1, i64 %arg2) {
+; NOFPAC-LABEL: test_resign_db_ib:
+; NOFPAC:       ; %bb.0:
+; NOFPAC-NEXT:    mov x16, x0
+; NOFPAC-NEXT:    autdb x16, x1
+; NOFPAC-NEXT:    mov x17, x16
+; NOFPAC-NEXT:    xpacd x17
+; NOFPAC-NEXT:    cmp x16, x17
+; NOFPAC-NEXT:    b.eq Lauth_success_4
+; NOFPAC-NEXT:    mov x16, x17
+; NOFPAC-NEXT:    b Lresign_end_4
+; NOFPAC-NEXT:  Lauth_success_4:
+; NOFPAC-NEXT:    pacib x16, x2
+; NOFPAC-NEXT:  Lresign_end_4:
+; NOFPAC-NEXT:    mov x0, x16
+; NOFPAC-NEXT:    ret
+;
+; FPAC-LABEL: test_resign_db_ib:
+; FPAC:       ; %bb.0:
+; FPAC-NEXT:    mov x16, x0
+; FPAC-NEXT:    autdb x16, x1
+; FPAC-NEXT:    pacib x16, x2
+; FPAC-NEXT:    mov x0, x16
+; FPAC-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 3, i64 %arg1, i32 1, i64 %arg2)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_db_da(i64 %arg, i64 %arg1, i64 %arg2) {
+; NOFPAC-LABEL: test_resign_db_da:
+; NOFPAC:       ; %bb.0:
+; NOFPAC-NEXT:    mov x16, x0
+; NOFPAC-NEXT:    autdb x16, x1
+; NOFPAC-NEXT:    mov x17, x16
+; NOFPAC-NEXT:    xpacd x17
+; NOFPAC-NEXT:    cmp x16, x17
+; NOFPAC-NEXT:    b.eq Lauth_success_5
+; NOFPAC-NEXT:    mov x16, x17
+; NOFPAC-NEXT:    b Lresign_end_5
+; NOFPAC-NEXT:  Lauth_success_5:
+; NOFPAC-NEXT:    pacda x16, x2
+; NOFPAC-NEXT:  Lresign_end_5:
+; NOFPAC-NEXT:    mov x0, x16
+; NOFPAC-NEXT:    ret
+;
+; FPAC-LABEL: test_resign_db_da:
+; FPAC:       ; %bb.0:
+; FPAC-NEXT:    mov x16, x0
+; FPAC-NEXT:    autdb x16, x1
+; FPAC-NEXT:    pacda x16, x2
+; FPAC-NEXT:    mov x0, x16
+; FPAC-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 3, i64 %arg1, i32 2, i64 %arg2)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_db_db(i64 %arg, i64 %arg1, i64 %arg2) {
+; NOFPAC-LABEL: test_resign_db_db:
+; NOFPAC:       ; %bb.0:
+; NOFPAC-NEXT:    mov x16, x0
+; NOFPAC-NEXT:    autdb x16, x1
+; NOFPAC-NEXT:    mov x17, x16
+; NOFPAC-NEXT:    xpacd x17
+; NOFPAC-NEXT:    cmp x16, x17
+; NOFPAC-NEXT:    b.eq Lauth_success_6
+; NOFPAC-NEXT:    mov x16, x17
+; NOFPAC-NEXT:    b Lresign_end_6
+; NOFPAC-NEXT:  Lauth_success_6:
+; NOFPAC-NEXT:    pacdb x16, x2
+; NOFPAC-NEXT:  Lresign_end_6:
+; NOFPAC-NEXT:    mov x0, x16
+; NOFPAC-NEXT:    ret
+;
+; FPAC-LABEL: test_resign_db_db:
+; FPAC:       ; %bb.0:
+; FPAC-NEXT:    mov x16, x0
+; FPAC-NEXT:    autdb x16, x1
+; FPAC-NEXT:    pacdb x16, x2
+; FPAC-NEXT:    mov x0, x16
+; FPAC-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 3, i64 %arg1, i32 3, i64 %arg2)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_iza_db(i64 %arg, i64 %arg1, i64 %arg2) {
+; NOFPAC-LABEL: test_resign_iza_db:
+; NOFPAC:       ; %bb.0:
+; NOFPAC-NEXT:    mov x16, x0
+; NOFPAC-NEXT:    autiza x16
+; NOFPAC-NEXT:    mov x17, x16
+; NOFPAC-NEXT:    xpaci x17
+; NOFPAC-NEXT:    cmp x16, x17
+; NOFPAC-NEXT:    b.eq Lauth_success_7
+; NOFPAC-NEXT:    mov x16, x17
+; NOFPAC-NEXT:    b Lresign_end_7
+; NOFPAC-NEXT:  Lauth_success_7:
+; NOFPAC-NEXT:    pacdb x16, x2
+; NOFPAC-NEXT:  Lresign_end_7:
+; NOFPAC-NEXT:    mov x0, x16
+; NOFPAC-NEXT:    ret
+;
+; FPAC-LABEL: test_resign_iza_db:
+; FPAC:       ; %bb.0:
+; FPAC-NEXT:    mov x16, x0
+; FPAC-NEXT:    autiza x16
+; FPAC-NEXT:    pacdb x16, x2
+; FPAC-NEXT:    mov x0, x16
+; FPAC-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 0, i64 0, i32 3, i64 %arg2)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_da_dzb(i64 %arg, i64 %arg1, i64 %arg2) {
+; NOFPAC-LABEL: test_resign_da_dzb:
+; NOFPAC:       ; %bb.0:
+; NOFPAC-NEXT:    mov x16, x0
+; NOFPAC-NEXT:    autda x16, x1
+; NOFPAC-NEXT:    mov x17, x16
+; NOFPAC-NEXT:    xpacd x17
+; NOFPAC-NEXT:    cmp x16, x17
+; NOFPAC-NEXT:    b.eq Lauth_success_8
+; NOFPAC-NEXT:    mov x16, x17
+; NOFPAC-NEXT:    b Lresign_end_8
+; NOFPAC-NEXT:  Lauth_success_8:
+; NOFPAC-NEXT:    pacdzb x16
+; NOFPAC-NEXT:  Lresign_end_8:
+; NOFPAC-NEXT:    mov x0, x16
+; NOFPAC-NEXT:    ret
+;
+; FPAC-LABEL: test_resign_da_dzb:
+; FPAC:       ; %bb.0:
+; FPAC-NEXT:    mov x16, x0
+; FPAC-NEXT:    autda x16, x1
+; FPAC-NEXT:    pacdzb x16
+; FPAC-NEXT:    mov x0, x16
+; FPAC-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 2, i64 %arg1, i32 3, i64 0)
+  ret i64 %tmp
+}
+
+define i64 @test_auth_trap_attribute(i64 %arg, i64 %arg1) "ptrauth-auth-traps" {
+; NOFPAC-LABEL: test_auth_trap_attribute:
+; NOFPAC:       ; %bb.0:
+; NOFPAC-NEXT:    mov x16, x0
+; NOFPAC-NEXT:    autia x16, x1
+; NOFPAC-NEXT:    mov x17, x16
+; NOFPAC-NEXT:    xpaci x17
+; NOFPAC-NEXT:    cmp x16, x17
+; NOFPAC-NEXT:    b.eq Lauth_success_9
+; NOFPAC-NEXT:    brk #0xc470
+; NOFPAC-NEXT:  Lauth_success_9:
+; NOFPAC-NEXT:    mov x0, x16
+; NOFPAC-NEXT:    ret
+;
+; FPAC-LABEL: test_auth_trap_attribute:
+; FPAC:       ; %bb.0:
+; FPAC-NEXT:    mov x16, x0
+; FPAC-NEXT:    autia x16, x1
+; FPAC-NEXT:    mov x0, x16
+; FPAC-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 0, i64 %arg1)
+  ret i64 %tmp
+}
+
+declare i64 @llvm.ptrauth.auth(i64, i32, i64)
+declare i64 @llvm.ptrauth.resign(i64, i32, i64, i32, i64)



More information about the llvm-commits mailing list