Skip to content

Commit 7f521df

Browse files
committed
[CGP][PAC] Flip PHI and blends when all immediate modifiers are the same
GVN PRE, SimplifyCFG and possibly other passes may hoist the call to `@llvm.ptrauth.blend` intrinsic, introducing multiple duplicate call instructions hidden behind a PHI node. This prevents the instruction selector from generating safer code by absorbing the address and immediate modifiers into separate operands of AUT, PAC, etc. pseudo instruction. This patch makes CodeGenPrepare pass detect when discriminator is computed as a PHI node with all incoming values being blends with the same immediate modifier. Each such discriminator value is replaced by a single blend, whose address argument is computed by a PHI node.
1 parent bb7815b commit 7f521df

File tree

2 files changed

+217
-0
lines changed

2 files changed

+217
-0
lines changed

llvm/lib/CodeGen/CodeGenPrepare.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ class CodeGenPrepare {
444444
bool optimizeSwitchPhiConstants(SwitchInst *SI);
445445
bool optimizeSwitchInst(SwitchInst *SI);
446446
bool optimizeExtractElementInst(Instruction *Inst);
447+
bool optimizePtrauthInst(Instruction *Inst, Value *Disc);
447448
bool dupRetToEnableTailCallOpts(BasicBlock *BB, ModifyDT &ModifiedDT);
448449
bool fixupDbgVariableRecord(DbgVariableRecord &I);
449450
bool fixupDbgVariableRecordsOnInst(Instruction &I);
@@ -2765,6 +2766,12 @@ bool CodeGenPrepare::optimizeCallInst(CallInst *CI, ModifyDT &ModifiedDT) {
27652766
return optimizeGatherScatterInst(II, II->getArgOperand(0));
27662767
case Intrinsic::masked_scatter:
27672768
return optimizeGatherScatterInst(II, II->getArgOperand(1));
2769+
case Intrinsic::ptrauth_auth:
2770+
case Intrinsic::ptrauth_sign:
2771+
return optimizePtrauthInst(II, II->getArgOperand(2));
2772+
case Intrinsic::ptrauth_resign:
2773+
return optimizePtrauthInst(II, II->getArgOperand(2)) ||
2774+
optimizePtrauthInst(II, II->getArgOperand(4));
27682775
}
27692776

27702777
SmallVector<Value *, 2> PtrOps;
@@ -8311,6 +8318,74 @@ bool CodeGenPrepare::optimizeExtractElementInst(Instruction *Inst) {
83118318
return false;
83128319
}
83138320

8321+
// Given the instruction Inst, rewrite its discriminator operand Disc if it is
8322+
// a PHI node with all incoming values being @llvm.ptrauth.blend(addr, imm)
8323+
// with the same immediate modifier.
8324+
bool CodeGenPrepare::optimizePtrauthInst(Instruction *Inst, Value *Disc) {
8325+
// GVN PRE, SimplifyCFG and possibly other passes may hoist or sink the call
8326+
// to @llvm.ptrauth.blend intrinsic, introducing multiple duplicate call
8327+
// instructions hidden behind a PHI node.
8328+
//
8329+
// To enforce the specific immediate modifier being blended into the
8330+
// discriminator even if the other, address component was reloaded from the
8331+
// stack, the AArch64 backend defines a number of pseudo instructions
8332+
// representing auth, sign and other operations. These pseudo instructions
8333+
// have separate register and immediate operands absorbing the arguments of
8334+
// blend intrinsic in case of a common `op(ptr, key_id, blend(addr, imm))`
8335+
// code pattern.
8336+
//
8337+
// To help the instruction selector, this function detects the discriminators
8338+
// represented by a PHI node with all incoming values being `blend`s with
8339+
// the same integer operand - each such discriminator is rewritten as a single
8340+
// blend, whose address operand is a PHI node.
8341+
8342+
PHINode *P = dyn_cast<PHINode>(Disc);
8343+
if (!P)
8344+
return false;
8345+
8346+
// Checks if V is blend(something, imm), returns imm if it is.
8347+
auto GetImmModifier = [](Value *V) -> std::optional<uint64_t> {
8348+
auto *II = dyn_cast<IntrinsicInst>(V);
8349+
if (!II || II->getIntrinsicID() != Intrinsic::ptrauth_blend)
8350+
return std::nullopt;
8351+
auto *ImmModifier = dyn_cast<ConstantInt>(II->getArgOperand(1));
8352+
if (!ImmModifier)
8353+
return std::nullopt;
8354+
return ImmModifier->getZExtValue();
8355+
};
8356+
8357+
// Check that all incoming values of P are blends with the same immediate
8358+
// modifier.
8359+
std::optional<uint64_t> ImmModifier = GetImmModifier(*P->op_begin());
8360+
if (!ImmModifier ||
8361+
!llvm::all_of(P->operands(), [&ImmModifier, GetImmModifier](Value *V) {
8362+
return ImmModifier == GetImmModifier(V);
8363+
}))
8364+
return false;
8365+
8366+
// At this point, P is a PHI node, with all the incoming values being a blend
8367+
// operations with the same immediate modifier, but possibly different address
8368+
// modifiers. Replace Disc argument of Inst with a single ptrauth_blend
8369+
// whose address modifier if provided by a PHI node.
8370+
8371+
IRBuilder<> Builder(Inst->getContext());
8372+
Type *Int64Ty = Builder.getInt64Ty();
8373+
8374+
Builder.SetInsertPoint(P);
8375+
PHINode *AddrDiscPhi = Builder.CreatePHI(Int64Ty, P->getNumIncomingValues());
8376+
for (auto [Val, BB] : llvm::zip_equal(P->operands(), P->blocks())) {
8377+
Value *AddrDisc = cast<IntrinsicInst>(Val)->getArgOperand(0);
8378+
AddrDiscPhi->addIncoming(AddrDisc, BB);
8379+
}
8380+
8381+
Builder.SetInsertPoint(Inst);
8382+
Value *ImmModifValue = ConstantInt::get(Int64Ty, *ImmModifier);
8383+
Value *Blend = Builder.CreateIntrinsic(Intrinsic::ptrauth_blend,
8384+
{AddrDiscPhi, ImmModifValue});
8385+
Inst->replaceUsesOfWith(Disc, Blend);
8386+
return true;
8387+
}
8388+
83148389
/// For the instruction sequence of store below, F and I values
83158390
/// are bundled together as an i64 value before being stored into memory.
83168391
/// Sometimes it is more efficient to generate separate stores for F and I,
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: -p --version 5
2+
; RUN: opt -mtriple=aarch64-linux-pauthtest -S -passes='require<profile-summary>,function(codegenprepare)' < %s | FileCheck %s
3+
; RUN: opt -mtriple=arm64e-apple-darwin -S -passes='require<profile-summary>,function(codegenprepare)' < %s | FileCheck %s
4+
5+
define i64 @choose_disc(i1 %cond, i64 %addr.1, i64 %addr.2, i64 %ptr) {
6+
; CHECK-LABEL: define i64 @choose_disc(i1 %cond, i64 %addr.1, i64 %addr.2, i64 %ptr) {
7+
; CHECK-NEXT: entry:
8+
; CHECK-NEXT: br i1 %cond, label %bb.blend.1, label %bb.blend.2
9+
; CHECK: bb.blend.1:
10+
; CHECK-NEXT: %disc.1 = call i64 @llvm.ptrauth.blend(i64 %addr.1, i64 1234)
11+
; CHECK-NEXT: br label %bb.user
12+
; CHECK: bb.blend.2:
13+
; CHECK-NEXT: %disc.2 = call i64 @llvm.ptrauth.blend(i64 %addr.2, i64 1234)
14+
; CHECK-NEXT: br label %bb.user
15+
; CHECK: bb.user:
16+
; CHECK-NEXT: %0 = phi i64 [ %addr.1, %bb.blend.1 ], [ %addr.2, %bb.blend.2 ]
17+
; CHECK-NEXT: %disc = phi i64 [ %disc.1, %bb.blend.1 ], [ %disc.2, %bb.blend.2 ]
18+
; CHECK-NEXT: %1 = call i64 @llvm.ptrauth.blend(i64 %0, i64 1234)
19+
; CHECK-NEXT: %result = call i64 @llvm.ptrauth.sign(i64 %ptr, i32 0, i64 %1)
20+
; CHECK-NEXT: ret i64 %result
21+
;
22+
entry:
23+
br i1 %cond, label %bb.blend.1, label %bb.blend.2
24+
bb.blend.1:
25+
%disc.1 = call i64 @llvm.ptrauth.blend(i64 %addr.1, i64 1234)
26+
br label %bb.user
27+
bb.blend.2:
28+
%disc.2 = call i64 @llvm.ptrauth.blend(i64 %addr.2, i64 1234)
29+
br label %bb.user
30+
bb.user:
31+
%disc = phi i64 [ %disc.1, %bb.blend.1 ], [ %disc.2, %bb.blend.2 ]
32+
%result = call i64 @llvm.ptrauth.sign(i64 %ptr, i32 0, i64 %disc)
33+
ret i64 %result
34+
}
35+
36+
define i64 @choose_disc_different_imm(i1 %cond, i64 %addr.1, i64 %addr.2, i64 %ptr) {
37+
; CHECK-LABEL: define i64 @choose_disc_different_imm(i1 %cond, i64 %addr.1, i64 %addr.2, i64 %ptr) {
38+
; CHECK-NEXT: entry:
39+
; CHECK-NEXT: br i1 %cond, label %bb.blend.1, label %bb.blend.2
40+
; CHECK: bb.blend.1:
41+
; CHECK-NEXT: %disc.1 = call i64 @llvm.ptrauth.blend(i64 %addr.1, i64 1234)
42+
; CHECK-NEXT: br label %bb.user
43+
; CHECK: bb.blend.2:
44+
; CHECK-NEXT: %disc.2 = call i64 @llvm.ptrauth.blend(i64 %addr.2, i64 42)
45+
; CHECK-NEXT: br label %bb.user
46+
; CHECK: bb.user:
47+
; CHECK-NEXT: %disc = phi i64 [ %disc.1, %bb.blend.1 ], [ %disc.2, %bb.blend.2 ]
48+
; CHECK-NEXT: %result = call i64 @llvm.ptrauth.sign(i64 %ptr, i32 0, i64 %disc)
49+
; CHECK-NEXT: ret i64 %result
50+
;
51+
entry:
52+
br i1 %cond, label %bb.blend.1, label %bb.blend.2
53+
bb.blend.1:
54+
%disc.1 = call i64 @llvm.ptrauth.blend(i64 %addr.1, i64 1234)
55+
br label %bb.user
56+
bb.blend.2:
57+
%disc.2 = call i64 @llvm.ptrauth.blend(i64 %addr.2, i64 42)
58+
br label %bb.user
59+
bb.user:
60+
%disc = phi i64 [ %disc.1, %bb.blend.1 ], [ %disc.2, %bb.blend.2 ]
61+
%result = call i64 @llvm.ptrauth.sign(i64 %ptr, i32 0, i64 %disc)
62+
ret i64 %result
63+
}
64+
65+
define i64 @choose_disc_multi_edge(i32 %arg, i64 %addr.1, i64 %addr.2, i64 %ptr) {
66+
; CHECK-LABEL: define i64 @choose_disc_multi_edge(i32 %arg, i64 %addr.1, i64 %addr.2, i64 %ptr) {
67+
; CHECK-NEXT: entry:
68+
; CHECK-NEXT: %cond = icmp eq i32 %arg, 0
69+
; CHECK-NEXT: br i1 %cond, label %bb.blend.1, label %bb.blend.2
70+
; CHECK: bb.blend.1:
71+
; CHECK-NEXT: %disc.1 = call i64 @llvm.ptrauth.blend(i64 %addr.1, i64 1234)
72+
; CHECK-NEXT: switch i32 %arg, label %bb.blend.2 [
73+
; CHECK-NEXT: i32 0, label %bb.user
74+
; CHECK-NEXT: i32 3, label %bb.user
75+
; CHECK-NEXT: ]
76+
; CHECK: bb.blend.2:
77+
; CHECK-NEXT: %disc.2 = call i64 @llvm.ptrauth.blend(i64 %addr.2, i64 1234)
78+
; CHECK-NEXT: br label %bb.user
79+
; CHECK: bb.user:
80+
; CHECK-NEXT: %0 = phi i64 [ %addr.1, %bb.blend.1 ], [ %addr.1, %bb.blend.1 ], [ %addr.2, %bb.blend.2 ]
81+
; CHECK-NEXT: %disc = phi i64 [ %disc.1, %bb.blend.1 ], [ %disc.1, %bb.blend.1 ], [ %disc.2, %bb.blend.2 ]
82+
; CHECK-NEXT: %1 = call i64 @llvm.ptrauth.blend(i64 %0, i64 1234)
83+
; CHECK-NEXT: %result = call i64 @llvm.ptrauth.sign(i64 %ptr, i32 0, i64 %1)
84+
; CHECK-NEXT: ret i64 %result
85+
;
86+
entry:
87+
%cond = icmp eq i32 %arg, 0
88+
br i1 %cond, label %bb.blend.1, label %bb.blend.2
89+
bb.blend.1:
90+
%disc.1 = call i64 @llvm.ptrauth.blend(i64 %addr.1, i64 1234)
91+
switch i32 %arg, label %bb.blend.2 [
92+
i32 0, label %bb.user
93+
i32 2, label %bb.blend.2
94+
i32 3, label %bb.user
95+
]
96+
bb.blend.2:
97+
%disc.2 = call i64 @llvm.ptrauth.blend(i64 %addr.2, i64 1234)
98+
br label %bb.user
99+
bb.user:
100+
%disc = phi i64 [ %disc.1, %bb.blend.1 ], [ %disc.1, %bb.blend.1 ], [ %disc.2, %bb.blend.2 ]
101+
%result = call i64 @llvm.ptrauth.sign(i64 %ptr, i32 0, i64 %disc)
102+
ret i64 %result
103+
}
104+
105+
define i64 @choose_disc_phi_placement(i1 %cond, i64 %addr.1, i64 %addr.2, i64 %ptr) {
106+
; CHECK-LABEL: define i64 @choose_disc_phi_placement(i1 %cond, i64 %addr.1, i64 %addr.2, i64 %ptr) {
107+
; CHECK-NEXT: entry:
108+
; CHECK-NEXT: br i1 %cond, label %bb.blend.1, label %bb.blend.2
109+
; CHECK: bb.blend.1:
110+
; CHECK-NEXT: %disc.1 = call i64 @llvm.ptrauth.blend(i64 %addr.1, i64 1234)
111+
; CHECK-NEXT: br label %bb.phi
112+
; CHECK: bb.blend.2:
113+
; CHECK-NEXT: %disc.2 = call i64 @llvm.ptrauth.blend(i64 %addr.2, i64 1234)
114+
; CHECK-NEXT: br label %bb.phi
115+
; CHECK: bb.phi:
116+
; CHECK-NEXT: %0 = phi i64 [ %addr.1, %bb.blend.1 ], [ %addr.2, %bb.blend.2 ]
117+
; CHECK-NEXT: %disc = phi i64 [ %disc.1, %bb.blend.1 ], [ %disc.2, %bb.blend.2 ]
118+
; CHECK-NEXT: br i1 %cond, label %bb.user, label %bb.ret0
119+
; CHECK: bb.user:
120+
; CHECK-NEXT: %1 = call i64 @llvm.ptrauth.blend(i64 %0, i64 1234)
121+
; CHECK-NEXT: %result = call i64 @llvm.ptrauth.sign(i64 %ptr, i32 0, i64 %1)
122+
; CHECK-NEXT: ret i64 %result
123+
; CHECK: bb.ret0:
124+
; CHECK-NEXT: ret i64 0
125+
;
126+
entry:
127+
br i1 %cond, label %bb.blend.1, label %bb.blend.2
128+
bb.blend.1:
129+
%disc.1 = call i64 @llvm.ptrauth.blend(i64 %addr.1, i64 1234)
130+
br label %bb.phi
131+
bb.blend.2:
132+
%disc.2 = call i64 @llvm.ptrauth.blend(i64 %addr.2, i64 1234)
133+
br label %bb.phi
134+
bb.phi:
135+
%disc = phi i64 [ %disc.1, %bb.blend.1 ], [ %disc.2, %bb.blend.2 ]
136+
br i1 %cond, label %bb.user, label %bb.ret0
137+
bb.user:
138+
%result = call i64 @llvm.ptrauth.sign(i64 %ptr, i32 0, i64 %disc)
139+
ret i64 %result
140+
bb.ret0:
141+
ret i64 0
142+
}

0 commit comments

Comments
 (0)