diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index b59f8d7306046..5c53c1953afbd 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -2869,6 +2869,130 @@ AArch64TargetLowering::EmitZero(MachineInstr &MI, MachineBasicBlock *BB) const { return BB; } +MachineBasicBlock * +AArch64TargetLowering::EmitPAuthInstr(MachineInstr &MI, + MachineBasicBlock *BB) const { + const AArch64InstrInfo *TII = Subtarget->getInstrInfo(); + MachineRegisterInfo &MRI = BB->getParent()->getRegInfo(); + DebugLoc DL = MI.getDebugLoc(); + + // The discriminator should be expressed by consecutive operands + // (raw_register, immediate_integer, is_blend). This function accepts + // (reg, 0, 0) operands generated by the instruction selector and tries + // to detect either small immediate discriminator expressed as + // (XZR, small_int, 0), blend(something, small_int) expressed as + // (something, small_int, 1) or keeps the operands as-is otherwise. + auto DetectDiscriminator = [&](unsigned RegDiscOpIndex) { + MachineOperand &RegOp = MI.getOperand(RegDiscOpIndex); + MachineOperand &ImmOp = MI.getOperand(RegDiscOpIndex + 1); + MachineOperand &IsBlendOp = MI.getOperand(RegDiscOpIndex + 2); + assert(ImmOp.getImm() == 0 && "Operand was already initialized"); + assert(IsBlendOp.getImm() == 0 && "Operand was already initialized"); + + // Walk through the chain of copy-like instructions until we find + // a known signing schema, if any. + Register AddrDisc; + uint64_t ImmDisc; + for (Register DiscReg = RegOp.getReg(); DiscReg.isVirtual();) { + MachineInstr *DefiningMI = MRI.getVRegDef(DiscReg); + switch (DefiningMI->getOpcode()) { + case AArch64::COPY: + DiscReg = DefiningMI->getOperand(1).getReg(); + if (DiscReg == AArch64::XZR) { + // Zero discriminator: (XZR, 0, 0). + RegOp.setReg(AArch64::XZR); + return; + } + break; + case AArch64::SUBREG_TO_REG: + DiscReg = DefiningMI->getOperand(2).getReg(); + break; + case AArch64::MOVi32imm: + ImmDisc = DefiningMI->getOperand(1).getImm(); + // If ImmDisc does not fit in 16 bits, + // consider it as custom computation. + if ((ImmDisc & 0xffff) == ImmDisc) { + // Small immediate integer: (XZR, imm, 0). + RegOp.setReg(AArch64::XZR); + ImmOp.setImm(ImmDisc); + } + return; + case AArch64::PAUTH_BLEND: + AddrDisc = DefiningMI->getOperand(1).getReg(); + ImmDisc = DefiningMI->getOperand(2).getImm(); + assert((ImmDisc & 0xffff) == ImmDisc && + "Expected 16-bit integer operand in PAUTH_BLEND"); + RegOp.setReg(AddrDisc); + ImmOp.setImm(ImmDisc); + IsBlendOp.setImm(1); + return; + default: + // Custom computation, leave it as-is. + return; + } + } + }; + + auto PopImplicitDef = [&](Register ExpectedReg) { + (void)ExpectedReg; + unsigned Index = MI.getNumOperands() - 1; + assert(MI.getOperand(Index).isImplicit()); + assert(MI.getOperand(Index).isDef()); + assert(MI.getOperand(Index).getReg() == ExpectedReg); + MI.removeOperand(Index); + }; + + auto AdjustDefinedRegisters = [&](unsigned TiedRegDiscOpIndex) { + Register RegDisc = MI.getOperand(TiedRegDiscOpIndex).getReg(); + + // The instruction, as selected by TableGen-erated code, has X16 and X17 + // registers implicitly defined, to make sure they are safe to clobber. + // + // Remove these generic implicit defs here and re-add them as needed and + // if needed. If assertions are enabled, additionally check that the two + // implicit operands are the expected ones. + PopImplicitDef(AArch64::X17); + PopImplicitDef(AArch64::X16); + + // $scratch operand is tied to $reg_disc, thus if an immediate integer + // discriminator is used, $scratch ends up being XZR. In that case, add + // an implicit-def scratch register - this is a special case known by + // aarch64-ptrauth pass. + MachineOperand *RealScratchOp = &MI.getOperand(1); + if (RegDisc == AArch64::XZR) { + MI.getOperand(1).setReg(AArch64::XZR); + Register ScratchReg = MRI.createVirtualRegister(&AArch64::GPR64RegClass); + MI.addOperand(MachineOperand::CreateReg(ScratchReg, /*isDef=*/true, + /*isImp=*/true)); + RealScratchOp = &MI.getOperand(MI.getNumOperands() - 1); + } + + assert((RegDisc == AArch64::XZR || RegDisc.isVirtual()) && + "Accidentally clobbering register?"); + + // If target CPU does not support FEAT_PAuth, IA and IB keys are still + // usable via HINT-encoded instructions. + if (!Subtarget->hasPAuth()) { + Register AutedReg = MI.getOperand(0).getReg(); + + MI.getOperand(0).setReg(AArch64::X17); + RealScratchOp->setReg(AArch64::X16); + BuildMI(*BB, MI.getNextNode(), DL, TII->get(AArch64::COPY), AutedReg) + .addReg(AArch64::X17); + } + }; + + switch (MI.getOpcode()) { + default: + llvm_unreachable("Unhandled opcode"); + case AArch64::PAUTH_AUTH: + DetectDiscriminator(/*RegDiscOpIndex=*/3); + AdjustDefinedRegisters(/*TiedRegDiscOpIndex=*/3); + break; + } + return BB; +} + MachineBasicBlock *AArch64TargetLowering::EmitInstrWithCustomInserter( MachineInstr &MI, MachineBasicBlock *BB) const { @@ -2922,6 +3046,9 @@ MachineBasicBlock *AArch64TargetLowering::EmitInstrWithCustomInserter( case TargetOpcode::PATCHABLE_TYPED_EVENT_CALL: return BB; + case AArch64::PAUTH_AUTH: + return EmitPAuthInstr(MI, BB); + case AArch64::CATCHRET: return EmitLoweredCatchRet(MI, BB); diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h index 436b21fd13463..dcf69270bba14 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h @@ -643,6 +643,9 @@ class AArch64TargetLowering : public TargetLowering { unsigned Opcode, bool Op0IsDef) const; MachineBasicBlock *EmitZero(MachineInstr &MI, MachineBasicBlock *BB) const; + MachineBasicBlock *EmitPAuthInstr(MachineInstr &MI, + MachineBasicBlock *BB) const; + MachineBasicBlock * EmitInstrWithCustomInserter(MachineInstr &MI, MachineBasicBlock *MBB) const override; diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index c476617e679f3..89ccfde32647f 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -1584,6 +1584,25 @@ def PAUTH_EPILOGUE : Pseudo<(outs), (ins), []>, Sched<[]>; def PAUTH_BLEND : Pseudo<(outs GPR64:$disc), (ins GPR64:$addr_disc, i32imm:$int_disc), []>, Sched<[]>; +// Two tasks are handled by custom inserter: +// 1. It tries to detect known signing schemas: either small immediate integer +// discriminator or an arbitrary register blended with a small integer - +// if such schema is detected, it is saved into the instruction's operands. +// 2. It is worth to reuse $reg_disc as a scratch register unless we use +// immediate integer as a discriminator (in that case $reg_disc is XZR). +// In the latter case $scratch is technically XZR, but another def-ed +// register is added as an implicit operand by the inserter. +// +// See the comments in custom inserter code for explanation of the reason +// to specify "Defs = [X16, X17]" here. +let usesCustomInserter = 1, Defs = [X16, X17] in { +def PAUTH_AUTH : Pseudo<(outs GPR64common:$auted, GPR64:$scratch), + (ins GPR64common:$signed, + GPR64:$reg_disc, i32imm:$int_disc, + i32imm:$is_blended, i32imm:$key_id), [], + "$auted = $signed, $scratch = $reg_disc">, Sched<[]>; +} + // These pointer authentication instructions require armv8.3a let Predicates = [HasPAuth] in { @@ -9337,6 +9356,10 @@ def : Pat<(int_ptrauth_blend GPR64:$Rd, imm64_0_65535:$imm), def : Pat<(int_ptrauth_blend GPR64:$Rd, GPR64:$Rn), (BFMXri GPR64:$Rd, GPR64:$Rn, 16, 15)>; +def : Pat<(int_ptrauth_auth GPR64:$signed, + timm:$key_id, GPR64:$reg_disc), + (PAUTH_AUTH GPR64:$signed, GPR64:$reg_disc, 0, 0, timm:$key_id)>; + //----------------------------------------------------------------------------- // This gets lowered into an instruction sequence of 20 bytes diff --git a/llvm/lib/Target/AArch64/AArch64PointerAuth.cpp b/llvm/lib/Target/AArch64/AArch64PointerAuth.cpp index abde099be382b..fffd2c467fa5e 100644 --- a/llvm/lib/Target/AArch64/AArch64PointerAuth.cpp +++ b/llvm/lib/Target/AArch64/AArch64PointerAuth.cpp @@ -16,6 +16,7 @@ #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/IR/Intrinsics.h" using namespace llvm; using namespace llvm::AArch64PAuth; @@ -57,9 +58,73 @@ class AArch64PointerAuth : public MachineFunctionPass { /// Expands PAUTH_BLEND pseudo instruction. void expandPAuthBlend(MachineBasicBlock::iterator MBBI) const; + /// Stores the discriminator value to the Result register. + /// + /// This is an utility function used when expanding several PAUTH_* + /// pseudo instructions that follow the same conventions on expressing + /// the requested signing schema. + void storeDiscriminator(MachineBasicBlock::iterator MBBI, Register Result, + bool IsBlended, Register RegDisc, unsigned IntDisc, + bool ShouldStoreZero) const; + + /// Emits discriminator computation followed by AUT* or PAC* instruction. + /// + /// By encoding the common cases of discriminator computation right in + /// the instruction's operands, this pass strives to emit blend operation + /// or move-immediate as close to AUT* or PAC* instruction as possible. + /// + /// The instruction is expected to have operand list starting with + /// - def $dst_ptr + /// - def $scratch + /// - use $src_ptr (tied to $dst_ptr) + /// and a contiguous sequence of operands describing the signing schema: + /// - use $reg_disc + /// - use $int_disc + /// - use $is_blended + /// - use $key_id + /// + /// As tying $scratch operand to (one of) $reg_disc is beneficial for + /// eliminating excessive register moves, it is allowed as an exception to + /// express immediate-only discriminator as $reg_disc (and thus $scratch) + /// being XZR. In that case, custom inserter should provide the actual + /// scratch register as an implicit def. + void expandAutOrSignWithDiscriminator( + MachineBasicBlock::iterator MBBI, unsigned RegDiscOpIndex, + unsigned (*GetHintOpcode)(Register, Register, AArch64PACKey::ID), + unsigned (*GetOpcode)(AArch64PACKey::ID, bool)) const; + + /// Checks pointer that was authenticated. + /// + /// The instruction is expected to have operand list starting with + /// - def $dst_ptr + /// - def $scratch + /// as well as $key_id and optional implicit def + /// (see expandAutOrSignWithDiscriminator for explanation). + void expandAddressCheck(MachineBasicBlock::iterator MBBI, + unsigned KeyIdOpIndex, + Intrinsic::ID IntrinsicId) const; + + /// Expands PAUTH_AUTH pseudo instruction. + void expandPAuthAuth(MachineBasicBlock::iterator MBBI) const; + bool checkAuthenticatedLR(MachineBasicBlock::iterator TI) const; }; +unsigned getHintAUTOpcode(Register Pointer, Register Discriminator, + AArch64PACKey::ID KeyId) { + if (Pointer != AArch64::X17 || Discriminator != AArch64::X16) + return 0; + + switch (KeyId) { + case AArch64PACKey::IA: + return AArch64::AUTIA1716; + case AArch64PACKey::IB: + return AArch64::AUTIB1716; + default: + return 0; + } +} + } // end anonymous namespace INITIALIZE_PASS(AArch64PointerAuth, "aarch64-ptrauth", @@ -305,6 +370,22 @@ MachineBasicBlock &llvm::AArch64PAuth::checkAuthenticatedRegister( .addImm(AArch64CC::NE) .addMBB(BreakBlock); return *SuccessBlock; + case AuthCheckMethod::XPAC: + BuildMI(CheckBlock, DL, TII->get(AArch64::ORRXrs), TmpReg) + .addReg(AArch64::XZR) + .addReg(AuthenticatedReg) + .addImm(0); + BuildMI(CheckBlock, DL, TII->get(UseIKey ? AArch64::XPACI : AArch64::XPACD), + TmpReg) + .addReg(TmpReg); + BuildMI(CheckBlock, DL, TII->get(AArch64::SUBSXrs), AArch64::XZR) + .addReg(TmpReg) + .addReg(AuthenticatedReg) + .addImm(0); + BuildMI(CheckBlock, DL, TII->get(AArch64::Bcc)) + .addImm(AArch64CC::NE) + .addMBB(BreakBlock); + return *SuccessBlock; } llvm_unreachable("Unknown AuthCheckMethod enum"); } @@ -319,6 +400,8 @@ unsigned llvm::AArch64PAuth::getCheckerSizeInBytes(AuthCheckMethod Method) { return 12; case AuthCheckMethod::XPACHint: return 20; + case AuthCheckMethod::XPAC: + return 20; } llvm_unreachable("Unknown AuthCheckMethod enum"); } @@ -400,6 +483,105 @@ void AArch64PointerAuth::expandPAuthBlend( emitBlend(MBBI, ResultReg, AddrDisc, IntDisc); } +void AArch64PointerAuth::storeDiscriminator(MachineBasicBlock::iterator MBBI, + Register Result, bool IsBlended, + Register RegDisc, unsigned IntDisc, + bool ShouldStoreZero) const { + MachineBasicBlock &MBB = *MBBI->getParent(); + DebugLoc DL = MBBI->getDebugLoc(); + + if (IsBlended) { + // Discriminator is blend(RegDisc, IntDisc). + emitBlend(MBBI, Result, RegDisc, IntDisc); + } else if (RegDisc != AArch64::XZR) { + // Discriminator is RegDisc. + assert(IntDisc == 0 && + "Cannot use both reg and int discriminators without blending"); + if (Result != RegDisc) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::ORRXrs), Result) + .addReg(AArch64::XZR) + .addReg(RegDisc) + .addImm(0); + } else { + // Discriminator is IntDisc (possibly, zero at all). + assert(RegDisc == AArch64::XZR && + "Cannot use both reg and int discriminators without blending"); + if (IntDisc != 0 || ShouldStoreZero) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::MOVZXi), Result) + .addImm(IntDisc) + .addImm(0); + } +} + +void AArch64PointerAuth::expandAutOrSignWithDiscriminator( + MachineBasicBlock::iterator MBBI, unsigned RegDiscOpIndex, + unsigned (*GetHintOpcode)(Register, Register, AArch64PACKey::ID), + unsigned (*GetOpcode)(AArch64PACKey::ID, bool)) const { + MachineBasicBlock &MBB = *MBBI->getParent(); + DebugLoc DL = MBBI->getDebugLoc(); + + Register PointerReg = MBBI->getOperand(0).getReg(); + Register ScratchReg = MBBI->getOperand(1).getReg(); + if (ScratchReg == AArch64::XZR) + ScratchReg = MBBI->getOperand(MBBI->getNumOperands() - 1).getReg(); + + Register RegDisc = MBBI->getOperand(RegDiscOpIndex).getReg(); + unsigned IntDisc = MBBI->getOperand(RegDiscOpIndex + 1).getImm(); + bool IsBlended = MBBI->getOperand(RegDiscOpIndex + 2).getImm(); + auto KeyId = (AArch64PACKey::ID)MBBI->getOperand(RegDiscOpIndex + 3).getImm(); + + // Try using an instruction encoded as HINT, if possible. + unsigned HintOpcode = GetHintOpcode(PointerReg, ScratchReg, KeyId); + assert(HintOpcode || Subtarget->hasPAuth()); + + // Compute discriminator value. + storeDiscriminator(MBBI, ScratchReg, IsBlended, RegDisc, IntDisc, + /*ShouldStoreZero=*/HintOpcode != 0); + + // Now, the discriminator value is in the ScratchReg register, + // if not using zero-discriminator opcode variant. + + if (HintOpcode) { + // All other opcodes require FEAT_PAuth, so check this first. + BuildMI(MBB, MBBI, DL, TII->get(HintOpcode)); + } else { + bool IsZeroDisc = RegDisc == AArch64::XZR && IntDisc == 0; + unsigned RegularOpcode = GetOpcode(KeyId, IsZeroDisc); + + if (IsZeroDisc) { + BuildMI(MBB, MBBI, DL, TII->get(RegularOpcode), PointerReg) + .addReg(PointerReg); + } else { + BuildMI(MBB, MBBI, DL, TII->get(RegularOpcode), PointerReg) + .addReg(PointerReg) + .addReg(ScratchReg); + } + } +} + +void AArch64PointerAuth::expandAddressCheck(MachineBasicBlock::iterator MBBI, + unsigned KeyIdOpIndex, + Intrinsic::ID IntrinsicId) const { + Register PointerReg = MBBI->getOperand(0).getReg(); + Register ScratchReg = MBBI->getOperand(1).getReg(); + if (ScratchReg == AArch64::XZR) + ScratchReg = MBBI->getOperand(MBBI->getNumOperands() - 1).getReg(); + auto KeyId = (AArch64PACKey::ID)MBBI->getOperand(KeyIdOpIndex).getImm(); + + bool UseIKey = KeyId == AArch64PACKey::IA || KeyId == AArch64PACKey::IB; + AuthCheckMethod Method = + Subtarget->getPAuthIntrinsicCheckMethod(IntrinsicId, KeyId); + checkAuthenticatedRegister(MBBI, Method, PointerReg, ScratchReg, UseIKey, + BrkOperandForKey(KeyId)); +} + +void AArch64PointerAuth::expandPAuthAuth( + MachineBasicBlock::iterator MBBI) const { + expandAutOrSignWithDiscriminator(MBBI, /*RegDiscOpIndex=*/3, getHintAUTOpcode, + getAUTOpcodeForKey); + expandAddressCheck(MBBI, /*KeyIdOpIndex=*/6, Intrinsic::ptrauth_auth); +} + bool AArch64PointerAuth::runOnMachineFunction(MachineFunction &MF) { const auto *MFnI = MF.getInfo(); @@ -431,6 +613,7 @@ bool AArch64PointerAuth::runOnMachineFunction(MachineFunction &MF) { break; case AArch64::PAUTH_PROLOGUE: case AArch64::PAUTH_EPILOGUE: + case AArch64::PAUTH_AUTH: case AArch64::PAUTH_BLEND: assert(!MI.isBundled()); PAuthPseudoInstrs.push_back(MI.getIterator()); @@ -448,6 +631,9 @@ bool AArch64PointerAuth::runOnMachineFunction(MachineFunction &MF) { authenticateLR(MF, It); HasAuthenticationInstrs = true; break; + case AArch64::PAUTH_AUTH: + expandPAuthAuth(It); + break; case AArch64::PAUTH_BLEND: expandPAuthBlend(It); break; diff --git a/llvm/lib/Target/AArch64/AArch64PointerAuth.h b/llvm/lib/Target/AArch64/AArch64PointerAuth.h index e1ceaed58abe4..e9f1379c66a7f 100644 --- a/llvm/lib/Target/AArch64/AArch64PointerAuth.h +++ b/llvm/lib/Target/AArch64/AArch64PointerAuth.h @@ -71,6 +71,18 @@ enum class AuthCheckMethod { /// b.ne break_block /// ``` XPACHint, + /// Check by comparing the authenticated value with an XPAC-ed one (handles + /// arbitrary register and key combination, requires FEAT_PAuth). + /// + /// This method modifies control flow and inserts the following checker: + /// + /// ``` + /// mov Xtmp, Xn + /// xpac(i|d) Xtmp + /// cmp Xtmp, Xn + /// b.ne break_block + /// ``` + XPAC, }; #define AUTH_CHECK_METHOD_CL_VALUES_COMMON \ @@ -82,11 +94,18 @@ enum class AuthCheckMethod { "high-bits-notbi", \ "Compare bits 62 and 61 of address (TBI should be disabled)") +/// The methods applicable to LR register authenticated using IA or IB key. #define AUTH_CHECK_METHOD_CL_VALUES_LR \ AUTH_CHECK_METHOD_CL_VALUES_COMMON, \ clEnumValN(AArch64PAuth::AuthCheckMethod::XPACHint, "xpac-hint", \ "Compare with the result of XPACLRI") +/// The methods applicable to any register authenticated using any key. +#define AUTH_CHECK_METHOD_CL_VALUES_GENERIC \ + AUTH_CHECK_METHOD_CL_VALUES_COMMON, \ + clEnumValN(AArch64PAuth::AuthCheckMethod::XPAC, "xpac", \ + "Compare with the result of XPAC (needs FEAT_PAuth)") + /// Explicitly checks that pointer authentication succeeded. /// /// Assuming AuthenticatedReg contains a value returned by one of the AUT* diff --git a/llvm/lib/Target/AArch64/AArch64Subtarget.cpp b/llvm/lib/Target/AArch64/AArch64Subtarget.cpp index 6550c12722166..5ba1f7342dc58 100644 --- a/llvm/lib/Target/AArch64/AArch64Subtarget.cpp +++ b/llvm/lib/Target/AArch64/AArch64Subtarget.cpp @@ -77,6 +77,12 @@ static cl::opt "to authenticated LR during tail call"), cl::values(AUTH_CHECK_METHOD_CL_VALUES_LR)); +static cl::opt PAuthIntrinsicCheckMethod( + "aarch64-intrinsic-check-method", cl::Hidden, + cl::desc("Override the variant of check performed by ptrauth_auth " + "intrinsic"), + cl::values(AUTH_CHECK_METHOD_CL_VALUES_GENERIC)); + static cl::opt AArch64MinimumJumpTableEntries( "aarch64-min-jump-table-entries", cl::init(13), cl::Hidden, cl::desc("Set minimum number of entries to use a jump table on AArch64")); @@ -544,3 +550,39 @@ AArch64Subtarget::getAuthenticatedLRCheckMethod() const { bool AArch64Subtarget::enableMachinePipeliner() const { return getSchedModel().hasInstrSchedModel(); } + +// Various int_ptrauth_* intrinsics may need to check the authenticated pointer +// to prevent introducing an oracle. The decision on the particular method to +// use to check the pointer may depend on whether the target CPU is known to +// support FEAT_FPAC, the desired security/performance trade-off, etc. +AArch64PAuth::AuthCheckMethod +AArch64Subtarget::getPAuthIntrinsicCheckMethod(Intrinsic::ID IntrinsicId, + AArch64PACKey::ID KeyId) const { + using namespace AArch64PAuth; + + bool UseIKey = + KeyId == AArch64PACKey::ID::IA || KeyId == AArch64PACKey::ID::IB; + AuthCheckMethod Method = AuthCheckMethod::None; + cl::opt *OverrideOpt = nullptr; + + switch (IntrinsicId) { + case Intrinsic::ptrauth_auth: + if (!hasPAuth()) { + // If generating Armv8.2-compatible code, only I-keys can be used in + // authentication. + assert(UseIKey && "Authenticating using D-keys without FEAT_PAuth"); + Method = AuthCheckMethod::None; + } else { + // DummyLoad should be the fastest method but breaks execute-only code. + Method = UseIKey ? AuthCheckMethod::XPAC : AuthCheckMethod::DummyLoad; + } + OverrideOpt = &PAuthIntrinsicCheckMethod; + break; + } + + assert(OverrideOpt && "Unhandled intrinsic"); + if (OverrideOpt->getNumOccurrences()) + return OverrideOpt->getValue(); + + return Method; +} diff --git a/llvm/lib/Target/AArch64/AArch64Subtarget.h b/llvm/lib/Target/AArch64/AArch64Subtarget.h index 0292c018f1dbc..b93b26249c268 100644 --- a/llvm/lib/Target/AArch64/AArch64Subtarget.h +++ b/llvm/lib/Target/AArch64/AArch64Subtarget.h @@ -450,6 +450,12 @@ class AArch64Subtarget final : public AArch64GenSubtargetInfo { /// Choose a method of checking LR before performing a tail call. AArch64PAuth::AuthCheckMethod getAuthenticatedLRCheckMethod() const; + /// Choose a method of checking the authenticated pointer when lowering + /// the PAuth intrinsic. + AArch64PAuth::AuthCheckMethod + getPAuthIntrinsicCheckMethod(Intrinsic::ID IntrinsicId, + AArch64PACKey::ID KeyId) const; + const PseudoSourceValue *getAddressCheckPSV() const { return AddressCheckPSV.get(); } diff --git a/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth.ll b/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth.ll new file mode 100644 index 0000000000000..b186c3ada4928 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth.ll @@ -0,0 +1,501 @@ +; RUN: llc -mtriple=aarch64 -verify-machineinstrs -asm-verbose=0 -stop-after=finalize-isel < %s \ +; RUN: | FileCheck --check-prefixes=MIR-HINT %s +; RUN: llc -mtriple=aarch64 -verify-machineinstrs -asm-verbose=0 -mattr=v8.3a -stop-after=finalize-isel < %s \ +; RUN: | FileCheck --check-prefixes=MIR-V83 %s +; RUN: llc -mtriple=aarch64 -verify-machineinstrs -asm-verbose=0 -stop-after=finalize-isel -global-isel=1 -global-isel-abort=1 < %s \ +; RUN: | FileCheck --check-prefixes=MIR-HINT %s +; RUN: llc -mtriple=aarch64 -verify-machineinstrs -asm-verbose=0 -mattr=v8.3a -stop-after=finalize-isel -global-isel=1 -global-isel-abort=1 < %s \ +; RUN: | FileCheck --check-prefixes=MIR-V83 %s + +; RUN: llc -mtriple=aarch64 -verify-machineinstrs -asm-verbose=0 < %s \ +; RUN: | FileCheck --check-prefixes=HINT-DEFAULT %s +; RUN: llc -mtriple=aarch64 -verify-machineinstrs -asm-verbose=0 -mattr=v8.3a < %s \ +; RUN: | FileCheck --check-prefixes=V83,V83-DEFAULT %s +; RUN: llc -mtriple=aarch64 -verify-machineinstrs -asm-verbose=0 -mattr=v8.3a -aarch64-intrinsic-check-method=none < %s \ +; RUN: | FileCheck --check-prefixes=V83,V83-NONE %s +; RUN: llc -mtriple=aarch64 -verify-machineinstrs -asm-verbose=0 -mattr=v8.3a -aarch64-intrinsic-check-method=load < %s \ +; RUN: | FileCheck --check-prefixes=V83,V83-LDR %s +; RUN: llc -mtriple=aarch64 -verify-machineinstrs -asm-verbose=0 -mattr=v8.3a -aarch64-intrinsic-check-method=high-bits-notbi < %s \ +; RUN: | FileCheck --check-prefixes=V83,V83-BITS-NOTBI %s +; RUN: llc -mtriple=aarch64 -verify-machineinstrs -asm-verbose=0 -mattr=v8.3a -aarch64-intrinsic-check-method=xpac < %s \ +; RUN: | FileCheck --check-prefixes=V83,V83-XPAC %s + +; Check that the expected instruction sequences are emitted between +; authentication and return from function. +define i64 @test_check_methods(i64 %signed) { +; V83-NONE-LABEL: test_check_methods: +; V83-NONE-NEXT: .cfi_startproc +; V83-NONE-NEXT: autiza x0 +; V83-NONE-NEXT: ret + +; V83-LDR-LABEL: test_check_methods: +; V83-LDR-NEXT: .cfi_startproc +; V83-LDR-NEXT: autiza x0 +; V83-LDR-NEXT: ldr w8, [x0] +; V83-LDR-NEXT: ret + +; V83-BITS-NOTBI-LABEL: test_check_methods: +; V83-BITS-NOTBI-NEXT: .cfi_startproc +; V83-BITS-NOTBI-NEXT: autiza x0 +; V83-BITS-NOTBI-NEXT: eor x8, x0, x0, lsl #1 +; V83-BITS-NOTBI-NEXT: tbnz x8, #62, .[[FAIL:LBB[_0-9]+]] +; V83-BITS-NOTBI-NEXT: ret +; V83-BITS-NOTBI-NEXT: .[[FAIL]]: +; V83-BITS-NOTBI-NEXT: brk #0xc470 + +; V83-XPAC-LABEL: test_check_methods: +; V83-XPAC-NEXT: .cfi_startproc +; V83-XPAC-NEXT: autiza x0 +; V83-XPAC-NEXT: mov x8, x0 +; V83-XPAC-NEXT: xpaci x8 +; V83-XPAC-NEXT: cmp x8, x0 +; V83-XPAC-NEXT: b.ne .[[FAIL:LBB[_0-9]+]] +; V83-XPAC-NEXT: ret +; V83-XPAC-NEXT: .[[FAIL]]: +; V83-XPAC-NEXT: brk #0xc470 + %auted = call i64 @llvm.ptrauth.auth(i64 %signed, i32 0, i64 0) + ret i64 %auted +} + +; Check for correct XPAC(I|D) instruction and operand of BRK instruction, +; once per key. + +define i64 @test_xpac_and_brk_ia(i64 %signed) { +; V83-XPAC-LABEL: test_xpac_and_brk_ia: +; V83-XPAC-NEXT: .cfi_startproc +; V83-XPAC-NEXT: autiza x0 +; V83-XPAC-NEXT: mov x8, x0 +; V83-XPAC-NEXT: xpaci x8 +; V83-XPAC-NEXT: cmp x8, x0 +; V83-XPAC-NEXT: b.ne .[[FAIL:LBB[_0-9]+]] +; V83-XPAC-NEXT: ret +; V83-XPAC-NEXT: .[[FAIL]]: +; V83-XPAC-NEXT: brk #0xc470 + %auted = call i64 @llvm.ptrauth.auth(i64 %signed, i32 0, i64 0) + ret i64 %auted +} + +define i64 @test_xpac_and_brk_ib(i64 %signed) { +; V83-XPAC-LABEL: test_xpac_and_brk_ib: +; V83-XPAC-NEXT: .cfi_startproc +; V83-XPAC-NEXT: autizb x0 +; V83-XPAC-NEXT: mov x8, x0 +; V83-XPAC-NEXT: xpaci x8 +; V83-XPAC-NEXT: cmp x8, x0 +; V83-XPAC-NEXT: b.ne .[[FAIL:LBB[_0-9]+]] +; V83-XPAC-NEXT: ret +; V83-XPAC-NEXT: .[[FAIL]]: +; V83-XPAC-NEXT: brk #0xc471 + %auted = call i64 @llvm.ptrauth.auth(i64 %signed, i32 1, i64 0) + ret i64 %auted +} + +define i64 @test_xpac_and_brk_da(i64 %signed) "target-features"="+v8.3a" { +; V83-XPAC-LABEL: test_xpac_and_brk_da: +; V83-XPAC-NEXT: .cfi_startproc +; V83-XPAC-NEXT: autdza x0 +; V83-XPAC-NEXT: mov x8, x0 +; V83-XPAC-NEXT: xpacd x8 +; V83-XPAC-NEXT: cmp x8, x0 +; V83-XPAC-NEXT: b.ne .[[FAIL:LBB[_0-9]+]] +; V83-XPAC-NEXT: ret +; V83-XPAC-NEXT: .[[FAIL]]: +; V83-XPAC-NEXT: brk #0xc472 + %auted = call i64 @llvm.ptrauth.auth(i64 %signed, i32 2, i64 0) + ret i64 %auted +} + +define i64 @test_xpac_and_brk_db(i64 %signed) "target-features"="+v8.3a" { +; V83-XPAC-LABEL: test_xpac_and_brk_db: +; V83-XPAC-NEXT: .cfi_startproc +; V83-XPAC-NEXT: autdzb x0 +; V83-XPAC-NEXT: mov x8, x0 +; V83-XPAC-NEXT: xpacd x8 +; V83-XPAC-NEXT: cmp x8, x0 +; V83-XPAC-NEXT: b.ne .[[FAIL:LBB[_0-9]+]] +; V83-XPAC-NEXT: ret +; V83-XPAC-NEXT: .[[FAIL]]: +; V83-XPAC-NEXT: brk #0xc473 + %auted = call i64 @llvm.ptrauth.auth(i64 %signed, i32 3, i64 0) + ret i64 %auted +} + +; Check for correct AUT* instruction (raw discriminator) +; and default check method. + +define i64 @test_aut_ia(i64 %signed, i64 %disc) { +; HINT-DEFAULT-LABEL: test_aut_ia: +; HINT-DEFAULT-NEXT: .cfi_startproc +; HINT-DEFAULT-NEXT: mov x17, x0 +; HINT-DEFAULT-NEXT: mov x16, x1 +; HINT-DEFAULT-NEXT: hint #12 +; HINT-DEFAULT-NEXT: mov x0, x17 +; HINT-DEFAULT-NEXT: ret + +; V83-DEFAULT-LABEL: test_aut_ia: +; V83-DEFAULT-NEXT: .cfi_startproc +; V83-DEFAULT-NEXT: autia x0, x1 +; V83-DEFAULT-NEXT: mov x1, x0 +; V83-DEFAULT-NEXT: xpaci x1 +; V83-DEFAULT-NEXT: cmp x1, x0 +; V83-DEFAULT-NEXT: b.ne .[[FAIL:LBB[_0-9]+]] +; V83-DEFAULT-NEXT: ret +; V83-DEFAULT-NEXT: .[[FAIL]]: +; V83-DEFAULT-NEXT: brk #0xc470 + %auted = call i64 @llvm.ptrauth.auth(i64 %signed, i32 0, i64 %disc) + ret i64 %auted +} + +define i64 @test_aut_ib(i64 %signed, i64 %disc) { +; HINT-DEFAULT-LABEL: test_aut_ib: +; HINT-DEFAULT-NEXT: .cfi_startproc +; HINT-DEFAULT-NEXT: mov x17, x0 +; HINT-DEFAULT-NEXT: mov x16, x1 +; HINT-DEFAULT-NEXT: hint #14 +; HINT-DEFAULT-NEXT: mov x0, x17 +; HINT-DEFAULT-NEXT: ret + +; V83-DEFAULT-LABEL: test_aut_ib: +; V83-DEFAULT-NEXT: .cfi_startproc +; V83-DEFAULT-NEXT: autib x0, x1 +; V83-DEFAULT-NEXT: mov x1, x0 +; V83-DEFAULT-NEXT: xpaci x1 +; V83-DEFAULT-NEXT: cmp x1, x0 +; V83-DEFAULT-NEXT: b.ne .[[FAIL:LBB[_0-9]+]] +; V83-DEFAULT-NEXT: ret +; V83-DEFAULT-NEXT: .[[FAIL]]: +; V83-DEFAULT-NEXT: brk #0xc471 + %auted = call i64 @llvm.ptrauth.auth(i64 %signed, i32 1, i64 %disc) + ret i64 %auted +} + +define i64 @test_aut_da(i64 %signed, i64 %disc) "target-features"="+v8.3a" { +; V83-DEFAULT-LABEL: test_aut_da: +; V83-DEFAULT-NEXT: .cfi_startproc +; V83-DEFAULT-NEXT: autda x0, x1 +; V83-DEFAULT-NEXT: ldr w1, [x0] +; V83-DEFAULT-NEXT: ret + %auted = call i64 @llvm.ptrauth.auth(i64 %signed, i32 2, i64 %disc) + ret i64 %auted +} + +define i64 @test_aut_db(i64 %signed, i64 %disc) "target-features"="+v8.3a" { +; V83-DEFAULT-LABEL: test_aut_db: +; V83-DEFAULT-NEXT: .cfi_startproc +; V83-DEFAULT-NEXT: autdb x0, x1 +; V83-DEFAULT-NEXT: ldr w1, [x0] +; V83-DEFAULT-NEXT: ret + %auted = call i64 @llvm.ptrauth.auth(i64 %signed, i32 3, i64 %disc) + ret i64 %auted +} + +; Check for correct AUT* instruction (zero discriminator). + +define i64 @test_aut_ia_zero(i64 %signed) { +; HINT-DEFAULT-LABEL: test_aut_ia_zero: +; HINT-DEFAULT-NEXT: .cfi_startproc +; HINT-DEFAULT-NEXT: mov x17, x0 +; HINT-DEFAULT-NEXT: mov x16, #0 +; HINT-DEFAULT-NEXT: hint #12 + +; V83-LABEL: test_aut_ia_zero: +; V83-NEXT: .cfi_startproc +; V83-NEXT: autiza x0 + %auted = call i64 @llvm.ptrauth.auth(i64 %signed, i32 0, i64 0) + ret i64 %auted +} + +define i64 @test_aut_ib_zero(i64 %signed) { +; HINT-DEFAULT-LABEL: test_aut_ib_zero: +; HINT-DEFAULT-NEXT: .cfi_startproc +; HINT-DEFAULT-NEXT: mov x17, x0 +; HINT-DEFAULT-NEXT: mov x16, #0 +; HINT-DEFAULT-NEXT: hint #14 + +; V83-LABEL: test_aut_ib_zero: +; V83-NEXT: .cfi_startproc +; V83-NEXT: autizb x0 + %auted = call i64 @llvm.ptrauth.auth(i64 %signed, i32 1, i64 0) + ret i64 %auted +} + +define i64 @test_aut_da_zero(i64 %signed) "target-features"="+v8.3a" { +; V83-LABEL: test_aut_da_zero: +; V83-NEXT: .cfi_startproc +; V83-NEXT: autdza x0 + %auted = call i64 @llvm.ptrauth.auth(i64 %signed, i32 2, i64 0) + ret i64 %auted +} + +define i64 @test_aut_db_zero(i64 %signed) "target-features"="+v8.3a" { +; V83-LABEL: test_aut_db_zero: +; V83-NEXT: .cfi_startproc +; V83-NEXT: autdzb x0 + %auted = call i64 @llvm.ptrauth.auth(i64 %signed, i32 3, i64 0) + ret i64 %auted +} + +; Check different signing schemas, both assembler output and MIR instructions. +; +; IB key is used instead of IA just because it is not encoded as zero +; by AArch64 backend. + +define i64 @test_zero_disc(i64 %signed) { +; HINT-DEFAULT-LABEL: test_zero_disc: +; HINT-DEFAULT-NEXT: .cfi_startproc +; HINT-DEFAULT-NEXT: mov x17, x0 +; HINT-DEFAULT-NEXT: mov x16, #0 +; HINT-DEFAULT-NEXT: hint #14 + +; V83-LABEL: test_zero_disc: +; V83-NEXT: .cfi_startproc +; V83-NEXT: autizb x0 + +; MIR-HINT-LABEL: name: test_zero_disc +; MIR-HINT: body: +; MIR-HINT: bb{{.*}}: +; MIR-HINT: liveins: $x0 +; MIR-HINT-DAG: %[[SIGNED:[0-9]+]]:gpr64common = COPY $x0 +; MIR-HINT: $x17, {{(dead )?}}$xzr = PAUTH_AUTH %[[SIGNED]], $xzr, 0, 0, 1, implicit-def $x16{{$}} +; MIR-HINT: %[[AUTED:[0-9]+]]:gpr64common = COPY $x17 +; MIR-HINT: $x0 = COPY %[[AUTED]] +; MIR-HINT: RET_ReallyLR implicit $x0 + +; MIR-V83-LABEL: name: test_zero_disc +; MIR-V83: body: +; MIR-V83: bb{{.*}}: +; MIR-V83: liveins: $x0 +; MIR-V83-DAG: %[[SIGNED:[0-9]+]]:gpr64common = COPY $x0 +; MIR-V83: %[[AUTED:[0-9]+]]:gpr64common, {{(dead )?}}$xzr = PAUTH_AUTH %[[SIGNED]], $xzr, 0, 0, 1, implicit-def %{{[0-9]+}}{{$}} +; MIR-V83: $x0 = COPY %[[AUTED]] +; MIR-V83: RET_ReallyLR implicit $x0 + %auted = call i64 @llvm.ptrauth.auth(i64 %signed, i32 1, i64 0) + ret i64 %auted +} + +define i64 @test_immediate_disc(i64 %signed) { +; HINT-DEFAULT-LABEL: test_immediate_disc: +; HINT-DEFAULT-NEXT: .cfi_startproc +; HINT-DEFAULT-NEXT: mov x17, x0 +; HINT-DEFAULT-NEXT: mov x16, #42 +; HINT-DEFAULT-NEXT: hint #14 + +; V83-LABEL: test_immediate_disc: +; V83-NEXT: .cfi_startproc +; V83-NEXT: mov x8, #42 +; V83-NEXT: autib x0, x8 + +; MIR-HINT-LABEL: name: test_immediate_disc +; MIR-HINT: body: +; MIR-HINT: bb{{.*}}: +; MIR-HINT: liveins: $x0 +; MIR-HINT-DAG: %[[SIGNED:[0-9]+]]:gpr64common = COPY $x0 +; MIR-HINT: $x17, {{(dead )?}}$xzr = PAUTH_AUTH %[[SIGNED]], $xzr, 42, 0, 1, implicit-def $x16{{$}} +; MIR-HINT: %[[AUTED:[0-9]+]]:gpr64common = COPY $x17 +; MIR-HINT: $x0 = COPY %[[AUTED]] +; MIR-HINT: RET_ReallyLR implicit $x0 + +; MIR-V83-LABEL: name: test_immediate_disc +; MIR-V83: body: +; MIR-V83: bb{{.*}}: +; MIR-V83: liveins: $x0 +; MIR-V83-DAG: %[[SIGNED:[0-9]+]]:gpr64common = COPY $x0 +; MIR-V83: %[[AUTED:[0-9]+]]:gpr64common, {{(dead )?}}$xzr = PAUTH_AUTH %[[SIGNED]], $xzr, 42, 0, 1, implicit-def %{{[0-9]+}}{{$}} +; MIR-V83: $x0 = COPY %[[AUTED]] +; MIR-V83: RET_ReallyLR implicit $x0 + %auted = call i64 @llvm.ptrauth.auth(i64 %signed, i32 1, i64 42) + ret i64 %auted +} + +define i64 @test_raw_disc(i64 %signed, i64 %raw_disc) { +; HINT-DEFAULT-LABEL: test_raw_disc: +; HINT-DEFAULT-NEXT: .cfi_startproc +; HINT-DEFAULT-NEXT: mov x17, x0 +; HINT-DEFAULT-NEXT: mov x16, x1 +; HINT-DEFAULT-NEXT: hint #14 + +; V83-LABEL: test_raw_disc: +; V83-NEXT: .cfi_startproc +; V83-NEXT: autib x0, x1 + +; MIR-HINT-LABEL: name: test_raw_disc +; MIR-HINT: body: +; MIR-HINT: bb{{.*}}: +; MIR-HINT: liveins: $x0, $x1 +; MIR-HINT-DAG: %[[SIGNED:[0-9]+]]:gpr64common = COPY $x0 +; MIR-HINT-DAG: %[[RAW_DISC:[0-9]+]]:gpr64 = COPY $x1 +; MIR-HINT: $x17, {{(dead )?}}$x16 = PAUTH_AUTH %[[SIGNED]], %[[RAW_DISC]], 0, 0, 1{{$}} +; MIR-HINT: %[[AUTED:[0-9]+]]:gpr64common = COPY $x17 +; MIR-HINT: $x0 = COPY %[[AUTED]] +; MIR-HINT: RET_ReallyLR implicit $x0 + +; MIR-V83-LABEL: name: test_raw_disc +; MIR-V83: body: +; MIR-V83: bb{{.*}}: +; MIR-V83: liveins: $x0, $x1 +; MIR-V83-DAG: %[[SIGNED:[0-9]+]]:gpr64common = COPY $x0 +; MIR-V83-DAG: %[[RAW_DISC:[0-9]+]]:gpr64 = COPY $x1 +; MIR-V83: %[[AUTED:[0-9]+]]:gpr64common, {{(dead )?}}%{{[0-9]+}}:gpr64 = PAUTH_AUTH %[[SIGNED]], %[[RAW_DISC]], 0, 0, 1{{$}} +; MIR-V83: $x0 = COPY %[[AUTED]] +; MIR-V83: RET_ReallyLR implicit $x0 + %auted = call i64 @llvm.ptrauth.auth(i64 %signed, i32 1, i64 %raw_disc) + ret i64 %auted +} + +define i64 @test_blended_disc(i64 %signed, i64 %addr_disc) { +; HINT-DEFAULT-LABEL: test_blended_disc: +; HINT-DEFAULT-NEXT: .cfi_startproc +; HINT-DEFAULT-NEXT: mov x17, x0 +; HINT-DEFAULT-NEXT: mov x16, x1 +; HINT-DEFAULT-NEXT: movk x16, #42, lsl #48 +; HINT-DEFAULT-NEXT: hint #14 + +; V83-LABEL: test_blended_disc: +; V83-NEXT: .cfi_startproc +; V83-NEXT: movk x1, #42, lsl #48 +; V83-NEXT: autib x0, x1 + +; MIR-HINT-LABEL: name: test_blended_disc +; MIR-HINT: body: +; MIR-HINT: bb{{.*}}: +; MIR-HINT: liveins: $x0, $x1 +; MIR-HINT-DAG: %[[SIGNED:[0-9]+]]:gpr64common = COPY $x0 +; MIR-HINT-DAG: %[[ADDR_DISC:[0-9]+]]:gpr64 = COPY $x1 +; MIR-HINT: $x17, {{(dead )?}}$x16 = PAUTH_AUTH %[[SIGNED]], %[[ADDR_DISC]], 42, 1, 1{{$}} +; MIR-HINT: %[[AUTED:[0-9]+]]:gpr64common = COPY $x17 +; MIR-HINT: $x0 = COPY %[[AUTED]] +; MIR-HINT: RET_ReallyLR implicit $x0 + +; MIR-V83-LABEL: name: test_blended_disc +; MIR-V83: body: +; MIR-V83: bb{{.*}}: +; MIR-V83: liveins: $x0, $x1 +; MIR-V83-DAG: %[[SIGNED:[0-9]+]]:gpr64common = COPY $x0 +; MIR-V83-DAG: %[[ADDR_DISC:[0-9]+]]:gpr64 = COPY $x1 +; MIR-V83: %[[AUTED:[0-9]+]]:gpr64common, {{(dead )?}}%{{[0-9]+}}:gpr64 = PAUTH_AUTH %[[SIGNED]], %[[ADDR_DISC]], 42, 1, 1{{$}} +; MIR-V83: $x0 = COPY %[[AUTED]] +; MIR-V83: RET_ReallyLR implicit $x0 + %disc = call i64 @llvm.ptrauth.blend(i64 %addr_disc, i64 42) + %auted = call i64 @llvm.ptrauth.auth(i64 %signed, i32 1, i64 %disc) + ret i64 %auted +} + +define i64 @test_blended_with_zero(i64 %signed, i64 %addr_disc) { +; HINT-DEFAULT-LABEL: test_blended_with_zero: +; HINT-DEFAULT-NEXT: .cfi_startproc +; HINT-DEFAULT-NEXT: mov x17, x0 +; HINT-DEFAULT-NEXT: mov x16, x1 +; HINT-DEFAULT-NEXT: movk x16, #0, lsl #48 +; HINT-DEFAULT-NEXT: hint #14 + +; V83-LABEL: test_blended_with_zero: +; V83-NEXT: .cfi_startproc +; V83-NEXT: movk x1, #0, lsl #48 +; V83-NEXT: autib x0, x1 + +; MIR-HINT-LABEL: name: test_blended_with_zero +; MIR-HINT: body: +; MIR-HINT: bb{{.*}}: +; MIR-HINT: liveins: $x0, $x1 +; MIR-HINT-DAG: %[[SIGNED:[0-9]+]]:gpr64common = COPY $x0 +; MIR-HINT-DAG: %[[ADDR_DISC:[0-9]+]]:gpr64 = COPY $x1 +; MIR-HINT: $x17, {{(dead )?}}$x16 = PAUTH_AUTH %[[SIGNED]], %[[ADDR_DISC]], 0, 1, 1{{$}} +; MIR-HINT: %[[AUTED:[0-9]+]]:gpr64common = COPY $x17 +; MIR-HINT: $x0 = COPY %[[AUTED]] +; MIR-HINT: RET_ReallyLR implicit $x0 + +; MIR-V83-LABEL: name: test_blended_with_zero +; MIR-V83: body: +; MIR-V83: bb{{.*}}: +; MIR-V83: liveins: $x0, $x1 +; MIR-V83-DAG: %[[SIGNED:[0-9]+]]:gpr64common = COPY $x0 +; MIR-V83-DAG: %[[ADDR_DISC:[0-9]+]]:gpr64 = COPY $x1 +; MIR-V83: %[[AUTED:[0-9]+]]:gpr64common, {{(dead )?}}%{{[0-9]+}}:gpr64 = PAUTH_AUTH %[[SIGNED]], %[[ADDR_DISC]], 0, 1, 1{{$}} +; MIR-V83: $x0 = COPY %[[AUTED]] +; MIR-V83: RET_ReallyLR implicit $x0 + %disc = call i64 @llvm.ptrauth.blend(i64 %addr_disc, i64 0) + %auted = call i64 @llvm.ptrauth.auth(i64 %signed, i32 1, i64 %disc) + ret i64 %auted +} + +define i64 @test_null_blend(i64 %signed) { +; HINT-DEFAULT-LABEL: test_null_blend: +; HINT-DEFAULT-NEXT: .cfi_startproc +; HINT-DEFAULT-NEXT: mov x17, x0 +; HINT-DEFAULT-NEXT: mov x16, xzr +; HINT-DEFAULT-NEXT: movk x16, #42, lsl #48 +; HINT-DEFAULT-NEXT: hint #14 + +; V83-LABEL: test_null_blend: +; V83-NEXT: .cfi_startproc +; V83-NEXT: mov x8, xzr +; V83-NEXT: movk x8, #42, lsl #48 +; V83-NEXT: autib x0, x8 + +; MIR-HINT-LABEL: name: test_null_blend +; MIR-HINT: body: +; MIR-HINT: bb{{.*}}: +; MIR-HINT: liveins: $x0 +; MIR-HINT-DAG: %[[SIGNED:[0-9]+]]:gpr64common = COPY $x0 +; MIR-HINT-DAG: %[[ZERO:[0-9]+]]:gpr64 = COPY $xzr +; MIR-HINT: $x17, {{(dead )?}}$x16 = PAUTH_AUTH %[[SIGNED]], %[[ZERO]], 42, 1, 1{{$}} +; MIR-HINT: %[[AUTED:[0-9]+]]:gpr64common = COPY $x17 +; MIR-HINT: $x0 = COPY %[[AUTED]] +; MIR-HINT: RET_ReallyLR implicit $x0 + +; MIR-V83-LABEL: name: test_null_blend +; MIR-V83: body: +; MIR-V83: bb{{.*}}: +; MIR-V83: liveins: $x0 +; MIR-V83-DAG: %[[SIGNED:[0-9]+]]:gpr64common = COPY $x0 +; MIR-V83-DAG: %[[ZERO:[0-9]+]]:gpr64 = COPY $xzr +; MIR-V83: %[[AUTED:[0-9]+]]:gpr64common, {{(dead )?}}%{{[0-9]+}}:gpr64 = PAUTH_AUTH %[[SIGNED]], %[[ZERO]], 42, 1, 1{{$}} +; MIR-V83: $x0 = COPY %[[AUTED]] +; MIR-V83: RET_ReallyLR implicit $x0 + %disc = call i64 @llvm.ptrauth.blend(i64 0, i64 42) + %auted = call i64 @llvm.ptrauth.auth(i64 %signed, i32 1, i64 %disc) + ret i64 %auted +} + +define i64 @test_custom_discriminator(i64 %signed, i64 %n) { +; HINT-DEFAULT-LABEL: test_custom_discriminator: +; HINT-DEFAULT-NEXT: .cfi_startproc +; HINT-DEFAULT-NEXT: add x16, x1, #42 +; HINT-DEFAULT-NEXT: mov x17, x0 +; HINT-DEFAULT-NEXT: hint #14 + +; V83-LABEL: test_custom_discriminator: +; V83-NEXT: .cfi_startproc +; V83-NEXT: add x8, x1, #42 +; V83-NEXT: autib x0, x8 + +; MIR-HINT-LABEL: name: test_custom_discriminator +; MIR-HINT: body: +; MIR-HINT: bb{{.*}}: +; MIR-HINT: liveins: $x0, $x1 +; MIR-HINT-DAG: %[[SIGNED:[0-9]+]]:gpr64common = COPY $x0 +; MIR-HINT-DAG: %[[N:[0-9]+]]:gpr64{{.*}} = COPY $x1 +; MIR-HINT-DAG: %[[DISC:[0-9]+]]:gpr64common = ADDXri %[[N]], 42, 0 +; MIR-HINT: $x17, {{(dead )?}}$x16 = PAUTH_AUTH %[[SIGNED]], %[[DISC]], 0, 0, 1{{$}} +; MIR-HINT: %[[AUTED:[0-9]+]]:gpr64common = COPY $x17 +; MIR-HINT: $x0 = COPY %[[AUTED]] +; MIR-HINT: RET_ReallyLR implicit $x0 + +; MIR-V83-LABEL: name: test_custom_discriminator +; MIR-V83: body: +; MIR-V83: bb{{.*}}: +; MIR-V83: liveins: $x0, $x1 +; MIR-V83-DAG: %[[SIGNED:[0-9]+]]:gpr64common = COPY $x0 +; MIR-V83-DAG: %[[N:[0-9]+]]:gpr64{{.*}} = COPY $x1 +; MIR-V83-DAG: %[[DISC:[0-9]+]]:gpr64common = ADDXri %[[N]], 42, 0 +; MIR-V83: %[[AUTED:[0-9]+]]:gpr64common, {{(dead )?}}%{{[0-9]+}}:gpr64 = PAUTH_AUTH %[[SIGNED]], %[[DISC]], 0, 0, 1{{$}} +; MIR-V83: $x0 = COPY %[[AUTED]] +; MIR-V83: RET_ReallyLR implicit $x0 + %disc = add i64 %n, 42 + %auted = call i64 @llvm.ptrauth.auth(i64 %signed, i32 1, i64 %disc) + ret i64 %auted +} + +declare ptr @llvm.frameaddress(i32) +declare i64 @llvm.ptrauth.auth(i64, i32, i64) +declare i64 @llvm.ptrauth.blend(i64, i64)