1; RUN: opt < %s -stack-tagging -S -o - | FileCheck %s --check-prefixes=CHECK,SSI
2; RUN: opt < %s -stack-tagging -stack-tagging-use-stack-safety=0 -S -o - | FileCheck %s --check-prefixes=CHECK,NOSSI
3
4target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
5target triple = "aarch64--linux-android"
6
7declare void @use8(i8*)
8declare void @use32(i32*)
9declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture)
10declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture)
11
12define dso_local void @noUse32(i32*) sanitize_memtag {
13entry:
14  ret void
15}
16
17define void @OneVar() sanitize_memtag {
18entry:
19  %x = alloca i32, align 4
20  call void @use32(i32* %x)
21  ret void
22}
23
24; CHECK-LABEL: define void @OneVar(
25; CHECK:  [[BASE:%.*]] = call i8* @llvm.aarch64.irg.sp(i64 0)
26; CHECK:  [[X:%.*]] = alloca { i32, [12 x i8] }, align 16
27; CHECK:  [[TX:%.*]] = call { i32, [12 x i8] }* @llvm.aarch64.tagp.{{.*}}({ i32, [12 x i8] }* [[X]], i8* [[BASE]], i64 0)
28; CHECK:  [[TX8:%.*]] = bitcast { i32, [12 x i8] }* [[TX]] to i8*
29; CHECK:  call void @llvm.aarch64.settag(i8* [[TX8]], i64 16)
30; CHECK:  [[GEP32:%.*]] = bitcast { i32, [12 x i8] }* [[TX]] to i32*
31; CHECK:  call void @use32(i32* [[GEP32]])
32; CHECK:  [[GEP8:%.*]] = bitcast { i32, [12 x i8] }* [[X]] to i8*
33; CHECK:  call void @llvm.aarch64.settag(i8* [[GEP8]], i64 16)
34; CHECK:  ret void
35
36
37define void @ManyVars() sanitize_memtag {
38entry:
39  %x1 = alloca i32, align 4
40  %x2 = alloca i8, align 4
41  %x3 = alloca i32, i32 11, align 4
42  %x4 = alloca i32, align 4
43  call void @use32(i32* %x1)
44  call void @use8(i8* %x2)
45  call void @use32(i32* %x3)
46  ret void
47}
48
49; CHECK-LABEL: define void @ManyVars(
50; CHECK:  alloca { i32, [12 x i8] }, align 16
51; CHECK:  call { i32, [12 x i8] }* @llvm.aarch64.tagp.{{.*}}({ i32, [12 x i8] }* {{.*}}, i64 0)
52; CHECK:  call void @llvm.aarch64.settag(i8* {{.*}}, i64 16)
53; CHECK:  alloca { i8, [15 x i8] }, align 16
54; CHECK:  call { i8, [15 x i8] }* @llvm.aarch64.tagp.{{.*}}({ i8, [15 x i8] }* {{.*}}, i64 1)
55; CHECK:  call void @llvm.aarch64.settag(i8* {{.*}}, i64 16)
56; CHECK:  alloca { [11 x i32], [4 x i8] }, align 16
57; CHECK:  call { [11 x i32], [4 x i8] }* @llvm.aarch64.tagp.{{.*}}({ [11 x i32], [4 x i8] }* {{.*}}, i64 2)
58; CHECK:  call void @llvm.aarch64.settag(i8* {{.*}}, i64 48)
59; SSI:    alloca i32, align 4
60; NOSSI:  alloca { i32, [12 x i8] }, align 16
61; NOSSI: @llvm.aarch64.tagp.
62; NOSSI: call void @llvm.aarch64.settag(i8* {{.*}}, i64 16)
63; SSI-NOT: @llvm.aarch64.tagp
64; SSI-NOT: @llvm.aarch64.settag
65
66; CHECK:  call void @use32(
67; CHECK:  call void @use8(
68; CHECK:  call void @use32(
69
70; CHECK:  call void @llvm.aarch64.settag(i8* {{.*}}, i64 16)
71; CHECK:  call void @llvm.aarch64.settag(i8* {{.*}}, i64 16)
72; CHECK:  call void @llvm.aarch64.settag(i8* {{.*}}, i64 48)
73; NOSSI:  call void @llvm.aarch64.settag(i8* {{.*}}, i64 16)
74; CHECK-NEXT:  ret void
75
76
77define void @Scope(i32 %b) sanitize_memtag {
78entry:
79  %x = alloca i32, align 4
80  %tobool = icmp eq i32 %b, 0
81  br i1 %tobool, label %if.end, label %if.then
82
83if.then:
84  %0 = bitcast i32* %x to i8*
85  call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %0)
86  call void @use8(i8* %0) #3
87  call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %0)
88  br label %if.end
89
90if.end:
91  ret void
92}
93
94; CHECK-LABEL: define void @Scope(
95; CHECK:  br i1
96; CHECK:  call void @llvm.lifetime.start.p0i8(
97; CHECK:  call void @llvm.aarch64.settag(
98; CHECK:  call void @use8(
99; CHECK:  call void @llvm.aarch64.settag(
100; CHECK:  call void @llvm.lifetime.end.p0i8(
101; CHECK:  br label
102; CHECK:  ret void
103
104
105; Spooked by the multiple lifetime ranges, StackTagging remove all of them and sets tags on entry and exit.
106define void @BadScope(i32 %b) sanitize_memtag {
107entry:
108  %x = alloca i32, align 4
109  %tobool = icmp eq i32 %b, 0
110  br i1 %tobool, label %if.end, label %if.then
111
112if.then:
113  %0 = bitcast i32* %x to i8*
114  call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %0)
115  call void @use8(i8* %0) #3
116  call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %0)
117
118  call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %0)
119  call void @use8(i8* %0) #3
120  call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %0)
121  br label %if.end
122
123if.end:
124  ret void
125}
126
127; CHECK-LABEL: define void @BadScope(
128; CHECK:       call void @llvm.aarch64.settag(i8* {{.*}}, i64 16)
129; CHECK:       br i1
130; CHECK:       call void @use8(i8*
131; CHECK-NEXT:  call void @use8(i8*
132; CHECK:       br label
133; CHECK:       call void @llvm.aarch64.settag(i8* {{.*}}, i64 16)
134; CHECK-NEXT:  ret void
135
136define void @DynamicAllocas(i32 %cnt) sanitize_memtag {
137entry:
138  %x = alloca i32, i32 %cnt, align 4
139  br label %l
140l:
141  %y = alloca i32, align 4
142  call void @use32(i32* %x)
143  call void @use32(i32* %y)
144  ret void
145}
146
147; CHECK-LABEL: define void @DynamicAllocas(
148; CHECK-NOT: @llvm.aarch64.irg.sp
149; CHECK:     %x = alloca i32, i32 %cnt, align 4
150; CHECK-NOT: @llvm.aarch64.irg.sp
151; CHECK:     alloca i32, align 4
152; CHECK-NOT: @llvm.aarch64.irg.sp
153; CHECK:     ret void
154
155; If we can't trace one of the lifetime markers to a single alloca, fall back
156; to poisoning all allocas at the beginning of the function.
157; Each alloca must be poisoned only once.
158define void @UnrecognizedLifetime(i8 %v) sanitize_memtag {
159entry:
160  %x = alloca i32, align 4
161  %y = alloca i32, align 4
162  %z = alloca i32, align 4
163  %cx = bitcast i32* %x to i8*
164  %cy = bitcast i32* %y to i8*
165  %cz = bitcast i32* %z to i8*
166  %tobool = icmp eq i8 %v, 0
167  %xy = select i1 %tobool, i32* %x, i32* %y
168  %cxcy = select i1 %tobool, i8* %cx, i8* %cy
169  br label %another_bb
170
171another_bb:
172  call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %cz)
173  store i32 7, i32* %z
174  call void @noUse32(i32* %z)
175  call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %cz)
176  call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %cz)
177  store i32 7, i32* %z
178  call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %cz)
179  call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %cxcy)
180  store i32 8, i32* %xy
181  call void @noUse32(i32* %x)
182  call void @noUse32(i32* %y)
183  call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %cxcy)
184  ret void
185}
186
187; CHECK-LABEL: define void @UnrecognizedLifetime(
188; CHECK: call i8* @llvm.aarch64.irg.sp(i64 0)
189; CHECK: alloca { i32, [12 x i8] }, align 16
190; CHECK: call { i32, [12 x i8] }* @llvm.aarch64.tagp
191; CHECK: call void @llvm.aarch64.settag(
192; CHECK: alloca { i32, [12 x i8] }, align 16
193; CHECK: call { i32, [12 x i8] }* @llvm.aarch64.tagp
194; CHECK: call void @llvm.aarch64.settag(
195; SSI: alloca i32, align 4
196; NOSSI: alloca { i32, [12 x i8] }, align 16
197; NOSSI: call { i32, [12 x i8] }* @llvm.aarch64.tagp
198; NOSSI: call void @llvm.aarch64.settag(
199; CHECK: store i32
200; CHECK: call void @noUse32(i32*
201; CHECK: store i32
202; CHECK: store i32
203; CHECK: call void @noUse32(i32*
204; CHECK: call void @llvm.aarch64.settag(
205; CHECK: call void @llvm.aarch64.settag(
206; NOSSI: call void @llvm.aarch64.settag(
207; CHECK: ret void
208
209!0 = !{}