Skip to content

Commit 99d0081

Browse files
committed
[AArch64][PAC] Implement code generation for @llvm.ptrauth.auth
This patch introduces PAUTH_AUTH pseudo instruction that can encode well-known discriminator computations in its operands: - small immediate integer discriminator - blend of a GPR64 register and an immediate integer - arbitrary GPR64 register as a fallback For convenience, a PAUTH_BLEND pseudo instruction is introduced as well that is selected for @llvm.ptrauth.blend intrinsic. For @llvm.ptrauth.auth, the TableGen-erated code selects a PAUTH_AUTH instruction in its "fallback" form. After that, custom inserter tries to detect a well-known signing schema and refines the operands of PAUTH_AUTH instruction, if possible. It may be necessary to use fixed X17 and X16 for pointer and scratch registers, either for security or compatibility with Armv8.2. For that purpose, implicit defs of X16 and X17 are added by TableGen-erated code, to make sure that custom inserter can safely use these registers as pointer and scratch operands. These temporary implicit-def operands are removed by custom inserter. As it is worth asking register allocator to place $reg_disc right in the $scratch register, these operands are tied. Thus, as a special case it is permitted to assign XZR to $scratch and provide the real scratch register as an implicit-def operand. While it would be possible to use 2 separate pseudo instructions: one for immediate integer discriminator and one for everything else, it would require 2*2 pseudos for expressing @llvm.ptrauth.resign the same way (or even 3*3 if clearly separating register/immediate/blended discriminator cases).
1 parent f49ccca commit 99d0081

9 files changed

+952
-10
lines changed

llvm/lib/Target/AArch64/AArch64ISelLowering.cpp

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2801,6 +2801,130 @@ AArch64TargetLowering::EmitZero(MachineInstr &MI, MachineBasicBlock *BB) const {
28012801
return BB;
28022802
}
28032803

2804+
MachineBasicBlock *
2805+
AArch64TargetLowering::EmitPAuthInstr(MachineInstr &MI,
2806+
MachineBasicBlock *BB) const {
2807+
const AArch64InstrInfo *TII = Subtarget->getInstrInfo();
2808+
MachineRegisterInfo &MRI = BB->getParent()->getRegInfo();
2809+
DebugLoc DL = MI.getDebugLoc();
2810+
2811+
// The discriminator should be expressed by consecutive operands
2812+
// (raw_register, immediate_integer, is_blend). This function accepts
2813+
// (reg, 0, 0) operands generated by the instruction selector and tries
2814+
// to detect either small immediate discriminator expressed as
2815+
// (XZR, small_int, 0), blend(something, small_int) expressed as
2816+
// (something, small_int, 1) or keeps the operands as-is otherwise.
2817+
auto DetectDiscriminator = [&](unsigned RegDiscOpIndex) {
2818+
MachineOperand &RegOp = MI.getOperand(RegDiscOpIndex);
2819+
MachineOperand &ImmOp = MI.getOperand(RegDiscOpIndex + 1);
2820+
MachineOperand &IsBlendOp = MI.getOperand(RegDiscOpIndex + 2);
2821+
assert(ImmOp.getImm() == 0 && "Operand was already initialized");
2822+
assert(IsBlendOp.getImm() == 0 && "Operand was already initialized");
2823+
2824+
// Walk through the chain of copy-like instructions until we find
2825+
// a known signing schema, if any.
2826+
Register AddrDisc;
2827+
uint64_t ImmDisc;
2828+
for (Register DiscReg = RegOp.getReg(); DiscReg.isVirtual();) {
2829+
MachineInstr *DefiningMI = MRI.getVRegDef(DiscReg);
2830+
switch (DefiningMI->getOpcode()) {
2831+
case AArch64::COPY:
2832+
DiscReg = DefiningMI->getOperand(1).getReg();
2833+
if (DiscReg == AArch64::XZR) {
2834+
// Zero discriminator: (XZR, 0, 0).
2835+
RegOp.setReg(AArch64::XZR);
2836+
return;
2837+
}
2838+
break;
2839+
case AArch64::SUBREG_TO_REG:
2840+
DiscReg = DefiningMI->getOperand(2).getReg();
2841+
break;
2842+
case AArch64::MOVi32imm:
2843+
ImmDisc = DefiningMI->getOperand(1).getImm();
2844+
// If ImmDisc does not fit in 16 bits,
2845+
// consider it as custom computation.
2846+
if ((ImmDisc & 0xffff) == ImmDisc) {
2847+
// Small immediate integer: (XZR, imm, 0).
2848+
RegOp.setReg(AArch64::XZR);
2849+
ImmOp.setImm(ImmDisc);
2850+
}
2851+
return;
2852+
case AArch64::PAUTH_BLEND:
2853+
AddrDisc = DefiningMI->getOperand(1).getReg();
2854+
ImmDisc = DefiningMI->getOperand(2).getImm();
2855+
assert((ImmDisc & 0xffff) == ImmDisc &&
2856+
"Expected 16-bit integer operand in PAUTH_BLEND");
2857+
RegOp.setReg(AddrDisc);
2858+
ImmOp.setImm(ImmDisc);
2859+
IsBlendOp.setImm(1);
2860+
return;
2861+
default:
2862+
// Custom computation, leave it as-is.
2863+
return;
2864+
}
2865+
}
2866+
};
2867+
2868+
auto PopImplicitDef = [&](Register ExpectedReg) {
2869+
(void)ExpectedReg;
2870+
unsigned Index = MI.getNumOperands() - 1;
2871+
assert(MI.getOperand(Index).isImplicit());
2872+
assert(MI.getOperand(Index).isDef());
2873+
assert(MI.getOperand(Index).getReg() == ExpectedReg);
2874+
MI.removeOperand(Index);
2875+
};
2876+
2877+
auto AdjustDefinedRegisters = [&](unsigned TiedRegDiscOpIndex) {
2878+
Register RegDisc = MI.getOperand(TiedRegDiscOpIndex).getReg();
2879+
2880+
// The instruction, as selected by TableGen-erated code, has X16 and X17
2881+
// registers implicitly defined, to make sure they are safe to clobber.
2882+
//
2883+
// Remove these generic implicit defs here and re-add them as needed and
2884+
// if needed. If assertions are enabled, additionally check that the two
2885+
// implicit operands are the expected ones.
2886+
PopImplicitDef(AArch64::X17);
2887+
PopImplicitDef(AArch64::X16);
2888+
2889+
// $scratch operand is tied to $reg_disc, thus if an immediate integer
2890+
// discriminator is used, $scratch ends up being XZR. In that case, add
2891+
// an implicit-def scratch register - this is a special case known by
2892+
// aarch64-ptrauth pass.
2893+
MachineOperand *RealScratchOp = &MI.getOperand(1);
2894+
if (RegDisc == AArch64::XZR) {
2895+
MI.getOperand(1).setReg(AArch64::XZR);
2896+
Register ScratchReg = MRI.createVirtualRegister(&AArch64::GPR64RegClass);
2897+
MI.addOperand(MachineOperand::CreateReg(ScratchReg, /*isDef=*/true,
2898+
/*isImp=*/true));
2899+
RealScratchOp = &MI.getOperand(MI.getNumOperands() - 1);
2900+
}
2901+
2902+
assert((RegDisc == AArch64::XZR || RegDisc.isVirtual()) &&
2903+
"Accidentally clobbering register?");
2904+
2905+
// If target CPU does not support FEAT_PAuth, IA and IB keys are still
2906+
// usable via HINT-encoded instructions.
2907+
if (!Subtarget->hasPAuth()) {
2908+
Register AutedReg = MI.getOperand(0).getReg();
2909+
2910+
MI.getOperand(0).setReg(AArch64::X17);
2911+
RealScratchOp->setReg(AArch64::X16);
2912+
BuildMI(*BB, MI.getNextNode(), DL, TII->get(AArch64::COPY), AutedReg)
2913+
.addReg(AArch64::X17);
2914+
}
2915+
};
2916+
2917+
switch (MI.getOpcode()) {
2918+
default:
2919+
llvm_unreachable("Unhandled opcode");
2920+
case AArch64::PAUTH_AUTH:
2921+
DetectDiscriminator(/*RegDiscOpIndex=*/3);
2922+
AdjustDefinedRegisters(/*TiedRegDiscOpIndex=*/3);
2923+
break;
2924+
}
2925+
return BB;
2926+
}
2927+
28042928
MachineBasicBlock *AArch64TargetLowering::EmitInstrWithCustomInserter(
28052929
MachineInstr &MI, MachineBasicBlock *BB) const {
28062930

@@ -2854,6 +2978,9 @@ MachineBasicBlock *AArch64TargetLowering::EmitInstrWithCustomInserter(
28542978
case TargetOpcode::PATCHABLE_TYPED_EVENT_CALL:
28552979
return BB;
28562980

2981+
case AArch64::PAUTH_AUTH:
2982+
return EmitPAuthInstr(MI, BB);
2983+
28572984
case AArch64::CATCHRET:
28582985
return EmitLoweredCatchRet(MI, BB);
28592986
case AArch64::LD1_MXIPXX_H_PSEUDO_B:

llvm/lib/Target/AArch64/AArch64ISelLowering.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,9 @@ class AArch64TargetLowering : public TargetLowering {
612612
bool IsSpill) const;
613613
MachineBasicBlock *EmitZero(MachineInstr &MI, MachineBasicBlock *BB) const;
614614

615+
MachineBasicBlock *EmitPAuthInstr(MachineInstr &MI,
616+
MachineBasicBlock *BB) const;
617+
615618
MachineBasicBlock *
616619
EmitInstrWithCustomInserter(MachineInstr &MI,
617620
MachineBasicBlock *MBB) const override;

llvm/lib/Target/AArch64/AArch64InstrInfo.td

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,6 +1533,27 @@ def PAUTH_PROLOGUE : Pseudo<(outs), (ins), []>, Sched<[]>;
15331533
def PAUTH_EPILOGUE : Pseudo<(outs), (ins), []>, Sched<[]>;
15341534
}
15351535

1536+
def PAUTH_BLEND : Pseudo<(outs GPR64:$disc), (ins GPR64:$addr_disc, i32imm:$int_disc), []>, Sched<[]>;
1537+
1538+
// Two tasks are handled by custom inserter:
1539+
// 1. It tries to detect known signing schemas: either small immediate integer
1540+
// discriminator or an arbitrary register blended with a small integer -
1541+
// if such schema is detected, it is saved into the instruction's operands.
1542+
// 2. It is worth to reuse $reg_disc as a scratch register unless we use
1543+
// immediate integer as a discriminator (in that case $reg_disc is XZR).
1544+
// In the latter case $scratch is technically XZR, but another def-ed
1545+
// register is added as an implicit operand by the inserter.
1546+
//
1547+
// See the comments in custom inserter code for explanation of the reason
1548+
// to specify "Defs = [X16, X17]" here.
1549+
let usesCustomInserter = 1, Defs = [X16, X17] in {
1550+
def PAUTH_AUTH : Pseudo<(outs GPR64common:$auted, GPR64:$scratch),
1551+
(ins GPR64common:$signed,
1552+
GPR64:$reg_disc, i32imm:$int_disc,
1553+
i32imm:$is_blended, i32imm:$key_id), [],
1554+
"$auted = $signed, $scratch = $reg_disc">, Sched<[]>;
1555+
}
1556+
15361557
// These pointer authentication instructions require armv8.3a
15371558
let Predicates = [HasPAuth] in {
15381559

@@ -9146,12 +9167,14 @@ let Predicates = [HasMOPS, HasMTE], Defs = [NZCV], Size = 12, mayLoad = 0, maySt
91469167
//-----------------------------------------------------------------------------
91479168
// v8.3 Pointer Authentication late patterns
91489169

9149-
let Predicates = [HasPAuth] in {
91509170
def : Pat<(int_ptrauth_blend GPR64:$Rd, imm64_0_65535:$imm),
9151-
(MOVKXi GPR64:$Rd, (trunc_imm imm64_0_65535:$imm), 48)>;
9171+
(PAUTH_BLEND GPR64:$Rd, (trunc_imm imm64_0_65535:$imm))>;
91529172
def : Pat<(int_ptrauth_blend GPR64:$Rd, GPR64:$Rn),
91539173
(BFMXri GPR64:$Rd, GPR64:$Rn, 16, 15)>;
9154-
}
9174+
9175+
def : Pat<(int_ptrauth_auth GPR64:$signed,
9176+
timm:$key_id, GPR64:$reg_disc),
9177+
(PAUTH_AUTH GPR64:$signed, GPR64:$reg_disc, 0, 0, timm:$key_id)>;
91559178

91569179
//-----------------------------------------------------------------------------
91579180

0 commit comments

Comments
 (0)