Skip to content

Commit 794a84e

Browse files
committed
[AArch64][PAC] Precommit tests for handling ptrauth.blend in GVN
1 parent 54b4005 commit 794a84e

File tree

1 file changed

+173
-0
lines changed

1 file changed

+173
-0
lines changed
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt < %s -mtriple aarch64 -S -passes=gvn | FileCheck --check-prefix=GVN %s
3+
; RUN: opt < %s -mtriple aarch64 -S -passes='gvn,simplifycfg<hoist-common-insts>' | FileCheck --check-prefix=GVN-SCFG %s
4+
5+
; When processing ptrauth.* intrinsics accepting a discriminator operand
6+
; on AArch64, the instruction selector tries to detect a common pattern of
7+
; the discriminator value being computed by a call to `blend(addr, imm)`.
8+
; In such case, a pseudo instruction is generated with `addr` and `imm` as
9+
; separate operands, which is not expanded until AsmPrinter. This way, it is
10+
; possible to enforce the immediate modifier, even if an attacker is able to
11+
; substitute the address modifier.
12+
;
13+
; While it should be more robust to use two separate arguments per discriminator
14+
; in any relevant intrinsic, a best-effort matching is currently performed by
15+
; the instruction selector. For that reason, it is important not to introduce
16+
; PHI nodes hiding the results of multiple identical blend operations.
17+
18+
; In test_simple, four different signed values are stored into memory, but
19+
; the discriminators are pairwise equal and thus could be moved by GVN's
20+
; partial redundancy elimination.
21+
define void @test_simple(i1 %cond, ptr %storage1, ptr %storage2, i64 %a, i64 %b) {
22+
; GVN-LABEL: define void @test_simple(
23+
; GVN-SAME: i1 [[COND:%.*]], ptr [[STORAGE1:%.*]], ptr [[STORAGE2:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
24+
; GVN-NEXT: [[ENTRY:.*:]]
25+
; GVN-NEXT: [[STORAGE1_I:%.*]] = ptrtoint ptr [[STORAGE1]] to i64
26+
; GVN-NEXT: [[STORAGE2_I:%.*]] = ptrtoint ptr [[STORAGE2]] to i64
27+
; GVN-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[ENTRY_EXIT_CRIT_EDGE:.*]]
28+
; GVN: [[ENTRY_EXIT_CRIT_EDGE]]:
29+
; GVN-NEXT: [[DOTPRE:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE1_I]], i64 42)
30+
; GVN-NEXT: [[DOTPRE1:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE2_I]], i64 42)
31+
; GVN-NEXT: br label %[[EXIT:.*]]
32+
; GVN: [[IF_THEN]]:
33+
; GVN-NEXT: [[DISCR1_THEN:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE1_I]], i64 42)
34+
; GVN-NEXT: [[DISCR2_THEN:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE2_I]], i64 42)
35+
; GVN-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[A]], i32 2, i64 [[DISCR1_THEN]])
36+
; GVN-NEXT: [[T2:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[A]], i32 2, i64 [[DISCR2_THEN]])
37+
; GVN-NEXT: store volatile i64 [[T1]], ptr [[STORAGE1]], align 8
38+
; GVN-NEXT: store volatile i64 [[T2]], ptr [[STORAGE2]], align 8
39+
; GVN-NEXT: br label %[[EXIT]]
40+
; GVN: [[EXIT]]:
41+
; GVN-NEXT: [[DISCR2_EXIT_PRE_PHI:%.*]] = phi i64 [ [[DOTPRE1]], %[[ENTRY_EXIT_CRIT_EDGE]] ], [ [[DISCR2_THEN]], %[[IF_THEN]] ]
42+
; GVN-NEXT: [[DISCR1_EXIT_PRE_PHI:%.*]] = phi i64 [ [[DOTPRE]], %[[ENTRY_EXIT_CRIT_EDGE]] ], [ [[DISCR1_THEN]], %[[IF_THEN]] ]
43+
; GVN-NEXT: [[T3:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR1_EXIT_PRE_PHI]])
44+
; GVN-NEXT: [[T4:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR2_EXIT_PRE_PHI]])
45+
; GVN-NEXT: store volatile i64 [[T3]], ptr [[STORAGE1]], align 8
46+
; GVN-NEXT: store volatile i64 [[T4]], ptr [[STORAGE2]], align 8
47+
; GVN-NEXT: ret void
48+
;
49+
; GVN-SCFG-LABEL: define void @test_simple(
50+
; GVN-SCFG-SAME: i1 [[COND:%.*]], ptr [[STORAGE1:%.*]], ptr [[STORAGE2:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
51+
; GVN-SCFG-NEXT: [[ENTRY:.*:]]
52+
; GVN-SCFG-NEXT: [[STORAGE1_I:%.*]] = ptrtoint ptr [[STORAGE1]] to i64
53+
; GVN-SCFG-NEXT: [[STORAGE2_I:%.*]] = ptrtoint ptr [[STORAGE2]] to i64
54+
; GVN-SCFG-NEXT: [[DISCR1_THEN:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE1_I]], i64 42)
55+
; GVN-SCFG-NEXT: [[DISCR2_THEN:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE2_I]], i64 42)
56+
; GVN-SCFG-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[EXIT:.*]]
57+
; GVN-SCFG: [[IF_THEN]]:
58+
; GVN-SCFG-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[A]], i32 2, i64 [[DISCR1_THEN]])
59+
; GVN-SCFG-NEXT: [[T2:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[A]], i32 2, i64 [[DISCR2_THEN]])
60+
; GVN-SCFG-NEXT: store volatile i64 [[T1]], ptr [[STORAGE1]], align 8
61+
; GVN-SCFG-NEXT: store volatile i64 [[T2]], ptr [[STORAGE2]], align 8
62+
; GVN-SCFG-NEXT: br label %[[EXIT]]
63+
; GVN-SCFG: [[EXIT]]:
64+
; GVN-SCFG-NEXT: [[T3:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR1_THEN]])
65+
; GVN-SCFG-NEXT: [[T4:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR2_THEN]])
66+
; GVN-SCFG-NEXT: store volatile i64 [[T3]], ptr [[STORAGE1]], align 8
67+
; GVN-SCFG-NEXT: store volatile i64 [[T4]], ptr [[STORAGE2]], align 8
68+
; GVN-SCFG-NEXT: ret void
69+
;
70+
entry:
71+
%storage1.i = ptrtoint ptr %storage1 to i64
72+
%storage2.i = ptrtoint ptr %storage2 to i64
73+
br i1 %cond, label %if.then, label %exit
74+
75+
if.then:
76+
%discr1.then = call i64 @llvm.ptrauth.blend(i64 %storage1.i, i64 42)
77+
%discr2.then = call i64 @llvm.ptrauth.blend(i64 %storage2.i, i64 42)
78+
%t1 = call i64 @llvm.ptrauth.sign(i64 %a, i32 2, i64 %discr1.then)
79+
%t2 = call i64 @llvm.ptrauth.sign(i64 %a, i32 2, i64 %discr2.then)
80+
store volatile i64 %t1, ptr %storage1
81+
store volatile i64 %t2, ptr %storage2
82+
br label %exit
83+
84+
exit:
85+
%discr1.exit = call i64 @llvm.ptrauth.blend(i64 %storage1.i, i64 42)
86+
%discr2.exit = call i64 @llvm.ptrauth.blend(i64 %storage2.i, i64 42)
87+
%t3 = call i64 @llvm.ptrauth.sign(i64 %b, i32 2, i64 %discr1.exit)
88+
%t4 = call i64 @llvm.ptrauth.sign(i64 %b, i32 2, i64 %discr2.exit)
89+
store volatile i64 %t3, ptr %storage1
90+
store volatile i64 %t4, ptr %storage2
91+
ret void
92+
}
93+
94+
; test_interleaved is similar to test_simple, but interleaving blend and sign
95+
; operations makes it harder for SimplifyCFG pass to hoist blends into the
96+
; entry basic block later and thus eliminate PHI nodes.
97+
define void @test_interleaved(i1 %cond, ptr %storage1, ptr %storage2, i64 %a, i64 %b) {
98+
; GVN-LABEL: define void @test_interleaved(
99+
; GVN-SAME: i1 [[COND:%.*]], ptr [[STORAGE1:%.*]], ptr [[STORAGE2:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
100+
; GVN-NEXT: [[ENTRY:.*:]]
101+
; GVN-NEXT: [[STORAGE1_I:%.*]] = ptrtoint ptr [[STORAGE1]] to i64
102+
; GVN-NEXT: [[STORAGE2_I:%.*]] = ptrtoint ptr [[STORAGE2]] to i64
103+
; GVN-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[ENTRY_EXIT_CRIT_EDGE:.*]]
104+
; GVN: [[ENTRY_EXIT_CRIT_EDGE]]:
105+
; GVN-NEXT: [[DOTPRE:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE1_I]], i64 42)
106+
; GVN-NEXT: [[DOTPRE1:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE2_I]], i64 42)
107+
; GVN-NEXT: br label %[[EXIT:.*]]
108+
; GVN: [[IF_THEN]]:
109+
; GVN-NEXT: [[DISCR1_THEN:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE1_I]], i64 42)
110+
; GVN-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[A]], i32 2, i64 [[DISCR1_THEN]])
111+
; GVN-NEXT: [[DISCR2_THEN:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE2_I]], i64 42)
112+
; GVN-NEXT: [[T2:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[A]], i32 2, i64 [[DISCR2_THEN]])
113+
; GVN-NEXT: store volatile i64 [[T1]], ptr [[STORAGE1]], align 8
114+
; GVN-NEXT: store volatile i64 [[T2]], ptr [[STORAGE2]], align 8
115+
; GVN-NEXT: br label %[[EXIT]]
116+
; GVN: [[EXIT]]:
117+
; GVN-NEXT: [[DISCR2_EXIT_PRE_PHI:%.*]] = phi i64 [ [[DOTPRE1]], %[[ENTRY_EXIT_CRIT_EDGE]] ], [ [[DISCR2_THEN]], %[[IF_THEN]] ]
118+
; GVN-NEXT: [[DISCR1_EXIT_PRE_PHI:%.*]] = phi i64 [ [[DOTPRE]], %[[ENTRY_EXIT_CRIT_EDGE]] ], [ [[DISCR1_THEN]], %[[IF_THEN]] ]
119+
; GVN-NEXT: [[T3:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR1_EXIT_PRE_PHI]])
120+
; GVN-NEXT: [[T4:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR2_EXIT_PRE_PHI]])
121+
; GVN-NEXT: store volatile i64 [[T3]], ptr [[STORAGE1]], align 8
122+
; GVN-NEXT: store volatile i64 [[T4]], ptr [[STORAGE2]], align 8
123+
; GVN-NEXT: ret void
124+
;
125+
; GVN-SCFG-LABEL: define void @test_interleaved(
126+
; GVN-SCFG-SAME: i1 [[COND:%.*]], ptr [[STORAGE1:%.*]], ptr [[STORAGE2:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
127+
; GVN-SCFG-NEXT: [[ENTRY:.*:]]
128+
; GVN-SCFG-NEXT: [[STORAGE1_I:%.*]] = ptrtoint ptr [[STORAGE1]] to i64
129+
; GVN-SCFG-NEXT: [[STORAGE2_I:%.*]] = ptrtoint ptr [[STORAGE2]] to i64
130+
; GVN-SCFG-NEXT: [[DISCR1_THEN:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE1_I]], i64 42)
131+
; GVN-SCFG-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[ENTRY_EXIT_CRIT_EDGE:.*]]
132+
; GVN-SCFG: [[ENTRY_EXIT_CRIT_EDGE]]:
133+
; GVN-SCFG-NEXT: [[DOTPRE1:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE2_I]], i64 42)
134+
; GVN-SCFG-NEXT: br label %[[EXIT:.*]]
135+
; GVN-SCFG: [[IF_THEN]]:
136+
; GVN-SCFG-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[A]], i32 2, i64 [[DISCR1_THEN]])
137+
; GVN-SCFG-NEXT: [[DISCR2_THEN:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE2_I]], i64 42)
138+
; GVN-SCFG-NEXT: [[T2:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[A]], i32 2, i64 [[DISCR2_THEN]])
139+
; GVN-SCFG-NEXT: store volatile i64 [[T1]], ptr [[STORAGE1]], align 8
140+
; GVN-SCFG-NEXT: store volatile i64 [[T2]], ptr [[STORAGE2]], align 8
141+
; GVN-SCFG-NEXT: br label %[[EXIT]]
142+
; GVN-SCFG: [[EXIT]]:
143+
; GVN-SCFG-NEXT: [[DISCR2_EXIT_PRE_PHI:%.*]] = phi i64 [ [[DOTPRE1]], %[[ENTRY_EXIT_CRIT_EDGE]] ], [ [[DISCR2_THEN]], %[[IF_THEN]] ]
144+
; GVN-SCFG-NEXT: [[DISCR1_EXIT_PRE_PHI:%.*]] = phi i64 [ [[DISCR1_THEN]], %[[ENTRY_EXIT_CRIT_EDGE]] ], [ [[DISCR1_THEN]], %[[IF_THEN]] ]
145+
; GVN-SCFG-NEXT: [[T3:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR1_EXIT_PRE_PHI]])
146+
; GVN-SCFG-NEXT: [[T4:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR2_EXIT_PRE_PHI]])
147+
; GVN-SCFG-NEXT: store volatile i64 [[T3]], ptr [[STORAGE1]], align 8
148+
; GVN-SCFG-NEXT: store volatile i64 [[T4]], ptr [[STORAGE2]], align 8
149+
; GVN-SCFG-NEXT: ret void
150+
;
151+
entry:
152+
%storage1.i = ptrtoint ptr %storage1 to i64
153+
%storage2.i = ptrtoint ptr %storage2 to i64
154+
br i1 %cond, label %if.then, label %exit
155+
156+
if.then:
157+
%discr1.then = call i64 @llvm.ptrauth.blend(i64 %storage1.i, i64 42)
158+
%t1 = call i64 @llvm.ptrauth.sign(i64 %a, i32 2, i64 %discr1.then)
159+
%discr2.then = call i64 @llvm.ptrauth.blend(i64 %storage2.i, i64 42)
160+
%t2 = call i64 @llvm.ptrauth.sign(i64 %a, i32 2, i64 %discr2.then)
161+
store volatile i64 %t1, ptr %storage1
162+
store volatile i64 %t2, ptr %storage2
163+
br label %exit
164+
165+
exit:
166+
%discr1.exit = call i64 @llvm.ptrauth.blend(i64 %storage1.i, i64 42)
167+
%t3 = call i64 @llvm.ptrauth.sign(i64 %b, i32 2, i64 %discr1.exit)
168+
%discr2.exit = call i64 @llvm.ptrauth.blend(i64 %storage2.i, i64 42)
169+
%t4 = call i64 @llvm.ptrauth.sign(i64 %b, i32 2, i64 %discr2.exit)
170+
store volatile i64 %t3, ptr %storage1
171+
store volatile i64 %t4, ptr %storage2
172+
ret void
173+
}

0 commit comments

Comments
 (0)