1; RUN: llc -mtriple=x86_64-pc-linux-gnu -start-before=stack-protector \ 2; RUN: -stop-after=stack-protector -o - < %s | FileCheck %s 3; Bugs 42238/43308: Test some additional situations not caught previously. 4 5define void @store_captures() #0 { 6; CHECK-LABEL: @store_captures( 7; CHECK-NEXT: entry: 8; CHECK-NEXT: [[STACKGUARDSLOT:%.*]] = alloca ptr 9; CHECK-NEXT: [[STACKGUARD:%.*]] = load volatile ptr, ptr addrspace(257) inttoptr (i32 40 to ptr addrspace(257)) 10; CHECK-NEXT: call void @llvm.stackprotector(ptr [[STACKGUARD]], ptr [[STACKGUARDSLOT]]) 11; CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 12; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 13; CHECK-NEXT: [[J:%.*]] = alloca ptr, align 8 14; CHECK-NEXT: store i32 0, ptr [[RETVAL]] 15; CHECK-NEXT: [[LOAD:%.*]] = load i32, ptr [[A]], align 4 16; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[LOAD]], 1 17; CHECK-NEXT: store i32 [[ADD]], ptr [[A]], align 4 18; CHECK-NEXT: store ptr [[A]], ptr [[J]], align 8 19; CHECK-NEXT: [[STACKGUARD1:%.*]] = load volatile ptr, ptr addrspace(257) inttoptr (i32 40 to ptr addrspace(257)) 20; CHECK-NEXT: [[TMP0:%.*]] = load volatile ptr, ptr [[STACKGUARDSLOT]] 21; CHECK-NEXT: [[TMP1:%.*]] = icmp eq ptr [[STACKGUARD1]], [[TMP0]] 22; CHECK-NEXT: br i1 [[TMP1]], label [[SP_RETURN:%.*]], label [[CALLSTACKCHECKFAILBLK:%.*]], !prof !0 23; CHECK: SP_return: 24; CHECK-NEXT: ret void 25; CHECK: CallStackCheckFailBlk: 26; CHECK-NEXT: call void @__stack_chk_fail() 27; CHECK-NEXT: unreachable 28; 29entry: 30 %retval = alloca i32, align 4 31 %a = alloca i32, align 4 32 %j = alloca ptr, align 8 33 store i32 0, ptr %retval 34 %load = load i32, ptr %a, align 4 35 %add = add nsw i32 %load, 1 36 store i32 %add, ptr %a, align 4 37 store ptr %a, ptr %j, align 8 38 ret void 39} 40 41define ptr @non_captures() #0 { 42; load, atomicrmw, and ret do not trigger a stack protector. 43; CHECK-LABEL: @non_captures( 44; CHECK-NEXT: entry: 45; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 46; CHECK-NEXT: [[LOAD:%.*]] = load i32, ptr [[A]], align 4 47; CHECK-NEXT: [[ATOM:%.*]] = atomicrmw add ptr [[A]], i32 1 seq_cst 48; CHECK-NEXT: ret ptr [[A]] 49; 50entry: 51 %a = alloca i32, align 4 52 %load = load i32, ptr %a, align 4 53 %atom = atomicrmw add ptr %a, i32 1 seq_cst 54 ret ptr %a 55} 56 57define void @store_addrspacecast_captures() #0 { 58; CHECK-LABEL: @store_addrspacecast_captures( 59; CHECK-NEXT: entry: 60; CHECK-NEXT: [[STACKGUARDSLOT:%.*]] = alloca ptr 61; CHECK-NEXT: [[STACKGUARD:%.*]] = load volatile ptr, ptr addrspace(257) inttoptr (i32 40 to ptr addrspace(257)) 62; CHECK-NEXT: call void @llvm.stackprotector(ptr [[STACKGUARD]], ptr [[STACKGUARDSLOT]]) 63; CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 64; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 65; CHECK-NEXT: [[J:%.*]] = alloca ptr addrspace(1), align 8 66; CHECK-NEXT: store i32 0, ptr [[RETVAL]] 67; CHECK-NEXT: [[LOAD:%.*]] = load i32, ptr [[A]], align 4 68; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[LOAD]], 1 69; CHECK-NEXT: store i32 [[ADD]], ptr [[A]], align 4 70; CHECK-NEXT: [[A_ADDRSPACECAST:%.*]] = addrspacecast ptr [[A]] to ptr addrspace(1) 71; CHECK-NEXT: store ptr addrspace(1) [[A_ADDRSPACECAST]], ptr [[J]], align 8 72; CHECK-NEXT: [[STACKGUARD1:%.*]] = load volatile ptr, ptr addrspace(257) inttoptr (i32 40 to ptr addrspace(257)) 73; CHECK-NEXT: [[TMP0:%.*]] = load volatile ptr, ptr [[STACKGUARDSLOT]] 74; CHECK-NEXT: [[TMP1:%.*]] = icmp eq ptr [[STACKGUARD1]], [[TMP0]] 75; CHECK-NEXT: br i1 [[TMP1]], label [[SP_RETURN:%.*]], label [[CALLSTACKCHECKFAILBLK:%.*]], !prof !0 76; CHECK: SP_return: 77; CHECK-NEXT: ret void 78; CHECK: CallStackCheckFailBlk: 79; CHECK-NEXT: call void @__stack_chk_fail() 80; CHECK-NEXT: unreachable 81; 82entry: 83 %retval = alloca i32, align 4 84 %a = alloca i32, align 4 85 %j = alloca ptr addrspace(1), align 8 86 store i32 0, ptr %retval 87 %load = load i32, ptr %a, align 4 88 %add = add nsw i32 %load, 1 89 store i32 %add, ptr %a, align 4 90 %a.addrspacecast = addrspacecast ptr %a to ptr addrspace(1) 91 store ptr addrspace(1) %a.addrspacecast, ptr %j, align 8 92 ret void 93} 94 95define void @cmpxchg_captures() #0 { 96; CHECK-LABEL: @cmpxchg_captures( 97; CHECK-NEXT: entry: 98; CHECK-NEXT: [[STACKGUARDSLOT:%.*]] = alloca ptr 99; CHECK-NEXT: [[STACKGUARD:%.*]] = load volatile ptr, ptr addrspace(257) inttoptr (i32 40 to ptr addrspace(257)) 100; CHECK-NEXT: call void @llvm.stackprotector(ptr [[STACKGUARD]], ptr [[STACKGUARDSLOT]]) 101; CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 102; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 103; CHECK-NEXT: [[J:%.*]] = alloca ptr, align 8 104; CHECK-NEXT: store i32 0, ptr [[RETVAL]] 105; CHECK-NEXT: [[LOAD:%.*]] = load i32, ptr [[A]], align 4 106; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[LOAD]], 1 107; CHECK-NEXT: store i32 [[ADD]], ptr [[A]], align 4 108; CHECK-NEXT: [[TMP0:%.*]] = cmpxchg ptr [[J]], ptr null, ptr [[A]] seq_cst monotonic 109; CHECK-NEXT: [[STACKGUARD1:%.*]] = load volatile ptr, ptr addrspace(257) inttoptr (i32 40 to ptr addrspace(257)) 110; CHECK-NEXT: [[TMP1:%.*]] = load volatile ptr, ptr [[STACKGUARDSLOT]] 111; CHECK-NEXT: [[TMP2:%.*]] = icmp eq ptr [[STACKGUARD1]], [[TMP1]] 112; CHECK-NEXT: br i1 [[TMP2]], label [[SP_RETURN:%.*]], label [[CALLSTACKCHECKFAILBLK:%.*]], !prof !0 113; CHECK: SP_return: 114; CHECK-NEXT: ret void 115; CHECK: CallStackCheckFailBlk: 116; CHECK-NEXT: call void @__stack_chk_fail() 117; CHECK-NEXT: unreachable 118; 119entry: 120 %retval = alloca i32, align 4 121 %a = alloca i32, align 4 122 %j = alloca ptr, align 8 123 store i32 0, ptr %retval 124 %load = load i32, ptr %a, align 4 125 %add = add nsw i32 %load, 1 126 store i32 %add, ptr %a, align 4 127 128 cmpxchg ptr %j, ptr null, ptr %a seq_cst monotonic 129 ret void 130} 131 132define void @memset_captures(i64 %c) #0 { 133; CHECK-LABEL: @memset_captures( 134; CHECK-NEXT: entry: 135; CHECK-NEXT: [[STACKGUARDSLOT:%.*]] = alloca ptr 136; CHECK-NEXT: [[STACKGUARD:%.*]] = load volatile ptr, ptr addrspace(257) inttoptr (i32 40 to ptr addrspace(257)) 137; CHECK-NEXT: call void @llvm.stackprotector(ptr [[STACKGUARD]], ptr [[STACKGUARDSLOT]]) 138; CHECK-NEXT: [[CADDR:%.*]] = alloca i64, align 8 139; CHECK-NEXT: store i64 %c, ptr [[CADDR]], align 8 140; CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 141; CHECK-NEXT: [[COUNT:%.*]] = load i64, ptr [[CADDR]], align 8 142; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[I]], i8 0, i64 [[COUNT]], i1 false) 143; CHECK-NEXT: [[STACKGUARD1:%.*]] = load volatile ptr, ptr addrspace(257) inttoptr (i32 40 to ptr addrspace(257)) 144; CHECK-NEXT: [[TMP1:%.*]] = load volatile ptr, ptr [[STACKGUARDSLOT]] 145; CHECK-NEXT: [[TMP2:%.*]] = icmp eq ptr [[STACKGUARD1]], [[TMP1]] 146; CHECK-NEXT: br i1 [[TMP2]], label [[SP_RETURN:%.*]], label [[CALLSTACKCHECKFAILBLK:%.*]], !prof !0 147; CHECK: SP_return: 148; CHECK-NEXT: ret void 149; CHECK: CallStackCheckFailBlk: 150; CHECK-NEXT: call void @__stack_chk_fail() 151; CHECK-NEXT: unreachable 152; 153entry: 154 %c.addr = alloca i64, align 8 155 store i64 %c, ptr %c.addr, align 8 156 %i = alloca i32, align 4 157 %count = load i64, ptr %c.addr, align 8 158 call void @llvm.memset.p0.i64(ptr align 4 %i, i8 0, i64 %count, i1 false) 159 ret void 160} 161 162declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) 163 164; Intentionally does not have any fn attrs. 165declare dso_local void @foo(ptr) 166 167; @bar_sspstrong and @bar_nossp are the same function, but differ only in 168; function attributes. Test that a callee without stack protector function 169; attribute does not trigger a stack guard slot in a caller that also does not 170; have a stack protector slot. 171define dso_local void @bar_sspstrong(i64 %0) #0 { 172; CHECK-LABEL: @bar_sspstrong 173; CHECK-NEXT: %StackGuardSlot = alloca ptr 174 %2 = alloca i64, align 8 175 store i64 %0, ptr %2, align 8 176 %3 = load i64, ptr %2, align 8 177 %4 = alloca i8, i64 %3, align 16 178 call void @foo(ptr %4) 179 ret void 180} 181 182; Intentionally does not have any fn attrs. 183define dso_local void @bar_nossp(i64 %0) { 184; CHECK-LABEL: @bar_nossp 185; CHECK-NEXT: %2 = alloca i64 186 %2 = alloca i64, align 8 187 store i64 %0, ptr %2, align 8 188 %3 = load i64, ptr %2, align 8 189 %4 = alloca i8, i64 %3, align 16 190 call void @foo(ptr %4) 191 ret void 192} 193 194; Check stack protect for noreturn call 195define dso_local i32 @foo_no_return(i32 %0) #1 { 196; CHECK-LABEL: @foo_no_return 197entry: 198 %cmp = icmp sgt i32 %0, 4 199 br i1 %cmp, label %if.then, label %if.end 200 201; CHECK: if.then: ; preds = %entry 202; CHECK-NEXT: %StackGuard1 = load volatile ptr, ptr addrspace(257) inttoptr (i32 40 to ptr addrspace(257)), align 8 203; CHECK-NEXT: %1 = load volatile ptr, ptr %StackGuardSlot, align 8 204; CHECK-NEXT: %2 = icmp eq ptr %StackGuard1, %1 205; CHECK-NEXT: br i1 %2, label %SP_return, label %CallStackCheckFailBlk 206; CHECK: SP_return: ; preds = %if.then 207; CHECK-NEXT: %call = call i32 @foo_no_return(i32 1) 208; CHECK-NEXT: br label %return 209; CHECK: if.end: ; preds = %entry 210; CHECK-NEXT: br label %return 211 212if.then: ; preds = %entry 213 %call = call i32 @foo_no_return(i32 1) 214 br label %return 215 216if.end: ; preds = %entry 217 br label %return 218 219return: ; preds = %if.end, %if.then 220 ret i32 0 221} 222 223declare void @callee() noreturn nounwind 224define void @caller() sspstrong { 225; Test that a stack protector is NOT inserted when we call nounwind functions. 226; CHECK-LABEL: @caller 227; CHECK-NEXT: call void @callee 228 call void @callee() noreturn nounwind 229 ret void 230} 231 232attributes #0 = { sspstrong } 233attributes #1 = { noreturn sspreq} 234