Skip to content

Commit be81ce3

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 @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 702664e commit be81ce3

8 files changed

+907
-0
lines changed

llvm/lib/Target/AArch64/AArch64ISelLowering.cpp

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

2872+
MachineBasicBlock *
2873+
AArch64TargetLowering::EmitPAuthInstr(MachineInstr &MI,
2874+
MachineBasicBlock *BB) const {
2875+
const AArch64InstrInfo *TII = Subtarget->getInstrInfo();
2876+
MachineRegisterInfo &MRI = BB->getParent()->getRegInfo();
2877+
DebugLoc DL = MI.getDebugLoc();
2878+
2879+
// The discriminator should be expressed by consecutive operands
2880+
// (raw_register, immediate_integer, is_blend). This function accepts
2881+
// (reg, 0, 0) operands generated by the instruction selector and tries
2882+
// to detect either small immediate discriminator expressed as
2883+
// (XZR, small_int, 0), blend(something, small_int) expressed as
2884+
// (something, small_int, 1) or keeps the operands as-is otherwise.
2885+
auto DetectDiscriminator = [&](unsigned RegDiscOpIndex) {
2886+
MachineOperand &RegOp = MI.getOperand(RegDiscOpIndex);
2887+
MachineOperand &ImmOp = MI.getOperand(RegDiscOpIndex + 1);
2888+
MachineOperand &IsBlendOp = MI.getOperand(RegDiscOpIndex + 2);
2889+
assert(ImmOp.getImm() == 0 && "Operand was already initialized");
2890+
assert(IsBlendOp.getImm() == 0 && "Operand was already initialized");
2891+
2892+
// Walk through the chain of copy-like instructions until we find
2893+
// a known signing schema, if any.
2894+
Register AddrDisc;
2895+
uint64_t ImmDisc;
2896+
for (Register DiscReg = RegOp.getReg(); DiscReg.isVirtual();) {
2897+
MachineInstr *DefiningMI = MRI.getVRegDef(DiscReg);
2898+
switch (DefiningMI->getOpcode()) {
2899+
case AArch64::COPY:
2900+
DiscReg = DefiningMI->getOperand(1).getReg();
2901+
if (DiscReg == AArch64::XZR) {
2902+
// Zero discriminator: (XZR, 0, 0).
2903+
RegOp.setReg(AArch64::XZR);
2904+
return;
2905+
}
2906+
break;
2907+
case AArch64::SUBREG_TO_REG:
2908+
DiscReg = DefiningMI->getOperand(2).getReg();
2909+
break;
2910+
case AArch64::MOVi32imm:
2911+
ImmDisc = DefiningMI->getOperand(1).getImm();
2912+
// If ImmDisc does not fit in 16 bits,
2913+
// consider it as custom computation.
2914+
if ((ImmDisc & 0xffff) == ImmDisc) {
2915+
// Small immediate integer: (XZR, imm, 0).
2916+
RegOp.setReg(AArch64::XZR);
2917+
ImmOp.setImm(ImmDisc);
2918+
}
2919+
return;
2920+
case AArch64::PAUTH_BLEND:
2921+
AddrDisc = DefiningMI->getOperand(1).getReg();
2922+
ImmDisc = DefiningMI->getOperand(2).getImm();
2923+
assert((ImmDisc & 0xffff) == ImmDisc &&
2924+
"Expected 16-bit integer operand in PAUTH_BLEND");
2925+
RegOp.setReg(AddrDisc);
2926+
ImmOp.setImm(ImmDisc);
2927+
IsBlendOp.setImm(1);
2928+
return;
2929+
default:
2930+
// Custom computation, leave it as-is.
2931+
return;
2932+
}
2933+
}
2934+
};
2935+
2936+
auto PopImplicitDef = [&](Register ExpectedReg) {
2937+
(void)ExpectedReg;
2938+
unsigned Index = MI.getNumOperands() - 1;
2939+
assert(MI.getOperand(Index).isImplicit());
2940+
assert(MI.getOperand(Index).isDef());
2941+
assert(MI.getOperand(Index).getReg() == ExpectedReg);
2942+
MI.removeOperand(Index);
2943+
};
2944+
2945+
auto AdjustDefinedRegisters = [&](unsigned TiedRegDiscOpIndex) {
2946+
Register RegDisc = MI.getOperand(TiedRegDiscOpIndex).getReg();
2947+
2948+
// The instruction, as selected by TableGen-erated code, has X16 and X17
2949+
// registers implicitly defined, to make sure they are safe to clobber.
2950+
//
2951+
// Remove these generic implicit defs here and re-add them as needed and
2952+
// if needed. If assertions are enabled, additionally check that the two
2953+
// implicit operands are the expected ones.
2954+
PopImplicitDef(AArch64::X17);
2955+
PopImplicitDef(AArch64::X16);
2956+
2957+
// $scratch operand is tied to $reg_disc, thus if an immediate integer
2958+
// discriminator is used, $scratch ends up being XZR. In that case, add
2959+
// an implicit-def scratch register - this is a special case known by
2960+
// aarch64-ptrauth pass.
2961+
MachineOperand *RealScratchOp = &MI.getOperand(1);
2962+
if (RegDisc == AArch64::XZR) {
2963+
MI.getOperand(1).setReg(AArch64::XZR);
2964+
Register ScratchReg = MRI.createVirtualRegister(&AArch64::GPR64RegClass);
2965+
MI.addOperand(MachineOperand::CreateReg(ScratchReg, /*isDef=*/true,
2966+
/*isImp=*/true));
2967+
RealScratchOp = &MI.getOperand(MI.getNumOperands() - 1);
2968+
}
2969+
2970+
assert((RegDisc == AArch64::XZR || RegDisc.isVirtual()) &&
2971+
"Accidentally clobbering register?");
2972+
2973+
// If target CPU does not support FEAT_PAuth, IA and IB keys are still
2974+
// usable via HINT-encoded instructions.
2975+
if (!Subtarget->hasPAuth()) {
2976+
Register AutedReg = MI.getOperand(0).getReg();
2977+
2978+
MI.getOperand(0).setReg(AArch64::X17);
2979+
RealScratchOp->setReg(AArch64::X16);
2980+
BuildMI(*BB, MI.getNextNode(), DL, TII->get(AArch64::COPY), AutedReg)
2981+
.addReg(AArch64::X17);
2982+
}
2983+
};
2984+
2985+
switch (MI.getOpcode()) {
2986+
default:
2987+
llvm_unreachable("Unhandled opcode");
2988+
case AArch64::PAUTH_AUTH:
2989+
DetectDiscriminator(/*RegDiscOpIndex=*/3);
2990+
AdjustDefinedRegisters(/*TiedRegDiscOpIndex=*/3);
2991+
break;
2992+
}
2993+
return BB;
2994+
}
2995+
28722996
MachineBasicBlock *AArch64TargetLowering::EmitInstrWithCustomInserter(
28732997
MachineInstr &MI, MachineBasicBlock *BB) const {
28742998

@@ -2922,6 +3046,9 @@ MachineBasicBlock *AArch64TargetLowering::EmitInstrWithCustomInserter(
29223046
case TargetOpcode::PATCHABLE_TYPED_EVENT_CALL:
29233047
return BB;
29243048

3049+
case AArch64::PAUTH_AUTH:
3050+
return EmitPAuthInstr(MI, BB);
3051+
29253052
case AArch64::CATCHRET:
29263053
return EmitLoweredCatchRet(MI, BB);
29273054

llvm/lib/Target/AArch64/AArch64ISelLowering.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,9 @@ class AArch64TargetLowering : public TargetLowering {
643643
unsigned Opcode, bool Op0IsDef) const;
644644
MachineBasicBlock *EmitZero(MachineInstr &MI, MachineBasicBlock *BB) const;
645645

646+
MachineBasicBlock *EmitPAuthInstr(MachineInstr &MI,
647+
MachineBasicBlock *BB) const;
648+
646649
MachineBasicBlock *
647650
EmitInstrWithCustomInserter(MachineInstr &MI,
648651
MachineBasicBlock *MBB) const override;

llvm/lib/Target/AArch64/AArch64InstrInfo.td

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,6 +1584,25 @@ def PAUTH_EPILOGUE : Pseudo<(outs), (ins), []>, Sched<[]>;
15841584
def PAUTH_BLEND : Pseudo<(outs GPR64:$disc),
15851585
(ins GPR64:$addr_disc, i32imm:$int_disc), []>, Sched<[]>;
15861586

1587+
// Two tasks are handled by custom inserter:
1588+
// 1. It tries to detect known signing schemas: either small immediate integer
1589+
// discriminator or an arbitrary register blended with a small integer -
1590+
// if such schema is detected, it is saved into the instruction's operands.
1591+
// 2. It is worth to reuse $reg_disc as a scratch register unless we use
1592+
// immediate integer as a discriminator (in that case $reg_disc is XZR).
1593+
// In the latter case $scratch is technically XZR, but another def-ed
1594+
// register is added as an implicit operand by the inserter.
1595+
//
1596+
// See the comments in custom inserter code for explanation of the reason
1597+
// to specify "Defs = [X16, X17]" here.
1598+
let usesCustomInserter = 1, Defs = [X16, X17] in {
1599+
def PAUTH_AUTH : Pseudo<(outs GPR64common:$auted, GPR64:$scratch),
1600+
(ins GPR64common:$signed,
1601+
GPR64:$reg_disc, i32imm:$int_disc,
1602+
i32imm:$is_blended, i32imm:$key_id), [],
1603+
"$auted = $signed, $scratch = $reg_disc">, Sched<[]>;
1604+
}
1605+
15871606
// These pointer authentication instructions require armv8.3a
15881607
let Predicates = [HasPAuth] in {
15891608

@@ -9337,6 +9356,10 @@ def : Pat<(int_ptrauth_blend GPR64:$Rd, imm64_0_65535:$imm),
93379356
def : Pat<(int_ptrauth_blend GPR64:$Rd, GPR64:$Rn),
93389357
(BFMXri GPR64:$Rd, GPR64:$Rn, 16, 15)>;
93399358

9359+
def : Pat<(int_ptrauth_auth GPR64:$signed,
9360+
timm:$key_id, GPR64:$reg_disc),
9361+
(PAUTH_AUTH GPR64:$signed, GPR64:$reg_disc, 0, 0, timm:$key_id)>;
9362+
93409363
//-----------------------------------------------------------------------------
93419364

93429365
// This gets lowered into an instruction sequence of 20 bytes

0 commit comments

Comments
 (0)