1; RUN: sed -e 's/SLHATTR/speculative_load_hardening/' %s | llc -verify-machineinstrs -mtriple=aarch64-none-linux-gnu | FileCheck %s --check-prefixes=CHECK,SLH
2; RUN: sed -e 's/SLHATTR//' %s | llc -verify-machineinstrs -mtriple=aarch64-none-linux-gnu | FileCheck %s --check-prefixes=CHECK,NOSLH
3; RUN: sed -e 's/SLHATTR/speculative_load_hardening/' %s | llc -verify-machineinstrs -mtriple=aarch64-none-linux-gnu -global-isel | FileCheck %s --check-prefixes=CHECK,SLH
4; RUN: sed -e 's/SLHATTR//' %s | llc -verify-machineinstrs -mtriple=aarch64-none-linux-gnu -global-isel | FileCheck %s --check-prefixes=CHECK,NOSLH
5; RUN: sed -e 's/SLHATTR/speculative_load_hardening/' %s | llc -verify-machineinstrs -mtriple=aarch64-none-linux-gnu -fast-isel | FileCheck %s --check-prefixes=CHECK,SLH
6; RUN: sed -e 's/SLHATTR//' %s | llc -verify-machineinstrs -mtriple=aarch64-none-linux-gnu -fast-isel | FileCheck %s --check-prefixes=CHECK,NOSLH
7
8define i32 @f(i8* nocapture readonly %p, i32 %i, i32 %N) local_unnamed_addr SLHATTR {
9; CHECK-LABEL: f
10entry:
11; SLH:  cmp sp, #0
12; SLH:  csetm x16, ne
13; NOSLH-NOT:  cmp sp, #0
14; NOSLH-NOT:  csetm x16, ne
15
16; SLH:  mov [[TMPREG:x[0-9]+]], sp
17; SLH:  and [[TMPREG]], [[TMPREG]], x16
18; SLH:  mov sp, [[TMPREG]]
19; NOSLH-NOT:  mov [[TMPREG:x[0-9]+]], sp
20; NOSLH-NOT:  and [[TMPREG]], [[TMPREG]], x16
21; NOSLH-NOT:  mov sp, [[TMPREG]]
22  %call = tail call i32 @tail_callee(i32 %i)
23; SLH:  cmp sp, #0
24; SLH:  csetm x16, ne
25; NOSLH-NOT:  cmp sp, #0
26; NOSLH-NOT:  csetm x16, ne
27  %cmp = icmp slt i32 %call, %N
28  br i1 %cmp, label %if.then, label %return
29; CHECK: b.[[COND:(ge)|(lt)|(ne)|(eq)]]
30
31if.then:                                          ; preds = %entry
32; NOSLH-NOT: csel x16, x16, xzr, {{(lt)|(ge)|(eq)|(ne)}}
33; SLH-DAG: csel x16, x16, xzr, {{(lt)|(ge)|(eq)|(ne)}}
34  %idxprom = sext i32 %i to i64
35  %arrayidx = getelementptr inbounds i8, i8* %p, i64 %idxprom
36  %0 = load i8, i8* %arrayidx, align 1
37; CHECK-DAG:      ldrb [[LOADED:w[0-9]+]],
38  %conv = zext i8 %0 to i32
39  br label %return
40
41; SLH-DAG: csel x16, x16, xzr, [[COND]]
42; NOSLH-NOT: csel x16, x16, xzr, [[COND]]
43return:                                           ; preds = %entry, %if.then
44  %retval.0 = phi i32 [ %conv, %if.then ], [ 0, %entry ]
45; SLH:  mov [[TMPREG:x[0-9]+]], sp
46; SLH:  and [[TMPREG]], [[TMPREG]], x16
47; SLH:  mov sp, [[TMPREG]]
48; NOSLH-NOT:  mov [[TMPREG:x[0-9]+]], sp
49; NOSLH-NOT:  and [[TMPREG]], [[TMPREG]], x16
50; NOSLH-NOT:  mov sp, [[TMPREG]]
51  ret i32 %retval.0
52}
53
54; Make sure that for a tail call, taint doesn't get put into SP twice.
55define i32 @tail_caller(i32 %a) local_unnamed_addr SLHATTR {
56; CHECK-LABEL: tail_caller:
57; SLH:     mov [[TMPREG:x[0-9]+]], sp
58; SLH:     and [[TMPREG]], [[TMPREG]], x16
59; SLH:     mov sp, [[TMPREG]]
60; NOSLH-NOT:     mov [[TMPREG:x[0-9]+]], sp
61; NOSLH-NOT:     and [[TMPREG]], [[TMPREG]], x16
62; NOSLH-NOT:     mov sp, [[TMPREG]]
63; SLH:     b tail_callee
64; SLH-NOT:        cmp sp, #0
65  %call = tail call i32 @tail_callee(i32 %a)
66  ret i32 %call
67}
68
69declare i32 @tail_callee(i32) local_unnamed_addr
70
71; Verify that no cb(n)z/tb(n)z instructions are produced when implementing
72; SLH
73define i32 @compare_branch_zero(i32, i32) SLHATTR {
74; CHECK-LABEL: compare_branch_zero
75  %3 = icmp eq i32 %0, 0
76  br i1 %3, label %then, label %else
77;SLH-NOT:   cb{{n?}}z
78;NOSLH:     cb{{n?}}z
79then:
80  %4 = sdiv i32 5, %1
81  ret i32 %4
82else:
83  %5 = sdiv i32 %1, %0
84  ret i32 %5
85}
86
87define i32 @test_branch_zero(i32, i32) SLHATTR {
88; CHECK-LABEL: test_branch_zero
89  %3 = and i32 %0, 16
90  %4 = icmp eq i32 %3, 0
91  br i1 %4, label %then, label %else
92;SLH-NOT:   tb{{n?}}z
93;NOSLH:     tb{{n?}}z
94then:
95  %5 = sdiv i32 5, %1
96  ret i32 %5
97else:
98  %6 = sdiv i32 %1, %0
99  ret i32 %6
100}
101
102define i32 @landingpad(i32 %l0, i32 %l1) SLHATTR personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
103; CHECK-LABEL: landingpad
104entry:
105; SLH:  cmp sp, #0
106; SLH:  csetm x16, ne
107; NOSLH-NOT:  cmp sp, #0
108; NOSLH-NOT:  csetm x16, ne
109; CHECK: bl _Z10throwing_fv
110  invoke void @_Z10throwing_fv()
111          to label %exit unwind label %lpad
112; SLH:  cmp sp, #0
113; SLH:  csetm x16, ne
114
115lpad:
116  %l4 = landingpad { i8*, i32 }
117          catch i8* null
118; SLH:  cmp sp, #0
119; SLH:  csetm x16, ne
120; NOSLH-NOT:  cmp sp, #0
121; NOSLH-NOT:  csetm x16, ne
122  %l5 = extractvalue { i8*, i32 } %l4, 0
123  %l6 = tail call i8* @__cxa_begin_catch(i8* %l5)
124  %l7 = icmp sgt i32 %l0, %l1
125  br i1 %l7, label %then, label %else
126; GlobalISel lowers the branch to a b.ne sometimes instead of b.ge as expected..
127; CHECK: b.[[COND:(le)|(gt)|(ne)|(eq)]]
128
129then:
130; SLH-DAG: csel x16, x16, xzr, [[COND]]
131  %l9 = sdiv i32 %l0, %l1
132  br label %postif
133
134else:
135; SLH-DAG: csel x16, x16, xzr, {{(gt)|(le)|(eq)|(ne)}}
136  %l11 = sdiv i32 %l1, %l0
137  br label %postif
138
139postif:
140  %l13 = phi i32 [ %l9, %then ], [ %l11, %else ]
141  tail call void @__cxa_end_catch()
142  br label %exit
143
144exit:
145  %l15 = phi i32 [ %l13, %postif ], [ 0, %entry ]
146  ret i32 %l15
147}
148
149declare i32 @__gxx_personality_v0(...)
150declare void @_Z10throwing_fv() local_unnamed_addr
151declare i8* @__cxa_begin_catch(i8*) local_unnamed_addr
152declare void @__cxa_end_catch() local_unnamed_addr
153