1 // RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -fstrict-vtable-pointers -o - %s \ 2 // RUN: | FileCheck --check-prefixes=CHECK,CHECK-STRICT %s 3 // RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -o - %s \ 4 // RUN: | FileCheck --check-prefixes=CHECK,CHECK-NONSTRICT %s 5 6 //===----------------------------------------------------------------------===// 7 // Positive Cases 8 //===----------------------------------------------------------------------===// 9 10 struct TestVirtualFn { 11 virtual void foo() {} 12 }; 13 14 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_virtual_fn 15 extern "C" void test_builtin_launder_virtual_fn(TestVirtualFn *p) { 16 // CHECK: store [[TYPE:%[^ ]+]] %p, [[TYPE]]* %p.addr 17 // CHECK-NEXT: [[TMP0:%.*]] = load [[TYPE]], [[TYPE]]* %p.addr 18 19 // CHECK-NONSTRICT-NEXT: store [[TYPE]] [[TMP0]], [[TYPE]]* %d 20 21 // CHECK-STRICT-NEXT: [[TMP1:%.*]] = bitcast [[TYPE]] [[TMP0]] to i8* 22 // CHECK-STRICT-NEXT: [[TMP2:%.*]] = call i8* @llvm.launder.invariant.group.p0i8(i8* [[TMP1]]) 23 // CHECK-STRICT-NEXT: [[TMP3:%.*]] = bitcast i8* [[TMP2]] to [[TYPE]] 24 // CHECK-STRICT-NEXT: store [[TYPE]] [[TMP3]], [[TYPE]]* %d 25 26 // CHECK-NEXT: ret void 27 TestVirtualFn *d = __builtin_launder(p); 28 } 29 30 struct TestPolyBase : TestVirtualFn { 31 }; 32 33 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_poly_base 34 extern "C" void test_builtin_launder_poly_base(TestPolyBase *p) { 35 // CHECK-STRICT-NOT: ret void 36 // CHECK-STRICT: @llvm.launder.invariant.group 37 38 // CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group 39 40 // CHECK: ret void 41 TestPolyBase *d = __builtin_launder(p); 42 } 43 44 struct TestBase {}; 45 struct TestVirtualBase : virtual TestBase {}; 46 47 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_virtual_base 48 extern "C" void test_builtin_launder_virtual_base(TestVirtualBase *p) { 49 // CHECK-STRICT-NOT: ret void 50 // CHECK-STRICT: @llvm.launder.invariant.group 51 52 // CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group 53 54 // CHECK: ret void 55 TestVirtualBase *d = __builtin_launder(p); 56 } 57 58 //===----------------------------------------------------------------------===// 59 // Negative Cases 60 //===----------------------------------------------------------------------===// 61 62 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_ommitted_one 63 extern "C" void test_builtin_launder_ommitted_one(int *p) { 64 // CHECK: entry 65 // CHECK-NEXT: %p.addr = alloca i32* 66 // CHECK-NEXT: %d = alloca i32* 67 // CHECK-NEXT: store i32* %p, i32** %p.addr, align 8 68 // CHECK-NEXT: [[TMP:%.*]] = load i32*, i32** %p.addr 69 // CHECK-NEXT: store i32* [[TMP]], i32** %d 70 // CHECK-NEXT: ret void 71 int *d = __builtin_launder(p); 72 } 73 74 struct TestNoInvariant { 75 int x; 76 }; 77 78 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_ommitted_two 79 extern "C" void test_builtin_launder_ommitted_two(TestNoInvariant *p) { 80 // CHECK: entry 81 // CHECK-NOT: llvm.launder.invariant.group 82 // CHECK-NEXT: %p.addr = alloca [[TYPE:%.*]], align 8 83 // CHECK-NEXT: %d = alloca [[TYPE]] 84 // CHECK-NEXT: store [[TYPE]] %p, [[TYPE]]* %p.addr 85 // CHECK-NEXT: [[TMP:%.*]] = load [[TYPE]], [[TYPE]]* %p.addr 86 // CHECK-NEXT: store [[TYPE]] [[TMP]], [[TYPE]]* %d 87 // CHECK-NEXT: ret void 88 TestNoInvariant *d = __builtin_launder(p); 89 } 90 91 struct TestVirtualMember { 92 TestVirtualFn member; 93 }; 94 95 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_virtual_member 96 extern "C" void test_builtin_launder_virtual_member(TestVirtualMember *p) { 97 // CHECK: entry 98 // CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group 99 // CHECK-STRICT: @llvm.launder.invariant.group 100 // CHECK: ret void 101 TestVirtualMember *d = __builtin_launder(p); 102 } 103 104 struct TestVirtualMemberDepth2 { 105 TestVirtualMember member; 106 }; 107 108 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_virtual_member_depth_2 109 extern "C" void test_builtin_launder_virtual_member_depth_2(TestVirtualMemberDepth2 *p) { 110 // CHECK: entry 111 // CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group 112 // CHECK-STRICT: @llvm.launder.invariant.group 113 // CHECK: ret void 114 TestVirtualMemberDepth2 *d = __builtin_launder(p); 115 } 116 117 struct TestVirtualReferenceMember { 118 TestVirtualFn &member; 119 }; 120 121 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_virtual_reference_member 122 extern "C" void test_builtin_launder_virtual_reference_member(TestVirtualReferenceMember *p) { 123 // CHECK: entry 124 // CHECK-NOT: @llvm.launder.invariant.group 125 // CHECK: ret void 126 TestVirtualReferenceMember *d = __builtin_launder(p); 127 } 128 129 struct TestRecursiveMember { 130 TestRecursiveMember() : member(*this) {} 131 TestRecursiveMember &member; 132 }; 133 134 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_recursive_member 135 extern "C" void test_builtin_launder_recursive_member(TestRecursiveMember *p) { 136 // CHECK: entry 137 // CHECK-NOT: @llvm.launder.invariant.group 138 // CHECK: ret void 139 TestRecursiveMember *d = __builtin_launder(p); 140 } 141 142 struct TestVirtualRecursiveMember { 143 TestVirtualRecursiveMember() : member(*this) {} 144 TestVirtualRecursiveMember &member; 145 virtual void foo(); 146 }; 147 148 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_virtual_recursive_member 149 extern "C" void test_builtin_launder_virtual_recursive_member(TestVirtualRecursiveMember *p) { 150 // CHECK: entry 151 // CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group 152 // CHECK-STRICT: @llvm.launder.invariant.group 153 // CHECK: ret void 154 TestVirtualRecursiveMember *d = __builtin_launder(p); 155 } 156 157 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_array( 158 extern "C" void test_builtin_launder_array(TestVirtualFn (&Arr)[5]) { 159 // CHECK: entry 160 // CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group 161 // CHECK-STRICT: @llvm.launder.invariant.group 162 // CHECK: ret void 163 TestVirtualFn *d = __builtin_launder(Arr); 164 } 165 166 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_array_nested( 167 extern "C" void test_builtin_launder_array_nested(TestVirtualFn (&Arr)[5][2]) { 168 // CHECK: entry 169 // CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group 170 // CHECK-STRICT: @llvm.launder.invariant.group 171 // CHECK: ret void 172 using RetTy = TestVirtualFn(*)[2]; 173 RetTy d = __builtin_launder(Arr); 174 } 175 176 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_array_no_invariant( 177 extern "C" void test_builtin_launder_array_no_invariant(TestNoInvariant (&Arr)[5]) { 178 // CHECK: entry 179 // CHECK-NOT: @llvm.launder.invariant.group 180 // CHECK: ret void 181 TestNoInvariant *d = __builtin_launder(Arr); 182 } 183 184 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_array_nested_no_invariant( 185 extern "C" void test_builtin_launder_array_nested_no_invariant(TestNoInvariant (&Arr)[5][2]) { 186 // CHECK: entry 187 // CHECK-NOT: @llvm.launder.invariant.group 188 // CHECK: ret void 189 using RetTy = TestNoInvariant(*)[2]; 190 RetTy d = __builtin_launder(Arr); 191 } 192 193 template <class Member> 194 struct WithMember { 195 Member mem; 196 }; 197 198 template struct WithMember<TestVirtualFn[5]>; 199 200 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_member_array( 201 extern "C" void test_builtin_launder_member_array(WithMember<TestVirtualFn[5]> *p) { 202 // CHECK: entry 203 // CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group 204 // CHECK-STRICT: @llvm.launder.invariant.group 205 // CHECK: ret void 206 auto *d = __builtin_launder(p); 207 } 208 209 template struct WithMember<TestVirtualFn[5][2]>; 210 211 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_member_array_nested( 212 extern "C" void test_builtin_launder_member_array_nested(WithMember<TestVirtualFn[5][2]> *p) { 213 // CHECK: entry 214 // CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group 215 // CHECK-STRICT: @llvm.launder.invariant.group 216 // CHECK: ret void 217 auto *d = __builtin_launder(p); 218 } 219 220 template struct WithMember<TestNoInvariant[5]>; 221 222 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_member_array_no_invariant( 223 extern "C" void test_builtin_launder_member_array_no_invariant(WithMember<TestNoInvariant[5]> *p) { 224 // CHECK: entry 225 // CHECK-NOT: @llvm.launder.invariant.group 226 // CHECK: ret void 227 auto *d = __builtin_launder(p); 228 } 229 230 template struct WithMember<TestNoInvariant[5][2]>; 231 232 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_member_array_nested_no_invariant( 233 extern "C" void test_builtin_launder_member_array_nested_no_invariant(WithMember<TestNoInvariant[5][2]> *p) { 234 // CHECK: entry 235 // CHECK-NOT: @llvm.launder.invariant.group 236 // CHECK: ret void 237 auto *d = __builtin_launder(p); 238 } 239 240 template <class T> 241 struct WithBase : T {}; 242 243 template struct WithBase<TestNoInvariant>; 244 245 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_base_no_invariant( 246 extern "C" void test_builtin_launder_base_no_invariant(WithBase<TestNoInvariant> *p) { 247 // CHECK: entry 248 // CHECK-NOT: @llvm.launder.invariant.group 249 // CHECK: ret void 250 auto *d = __builtin_launder(p); 251 } 252 253 template struct WithBase<TestVirtualFn>; 254 255 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_base( 256 extern "C" void test_builtin_launder_base(WithBase<TestVirtualFn> *p) { 257 // CHECK: entry 258 // CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group 259 // CHECK-STRICT: @llvm.launder.invariant.group 260 // CHECK: ret void 261 auto *d = __builtin_launder(p); 262 } 263 264 /// The test cases in this namespace technically need to be laundered according 265 /// to the language in the standard (ie they have const or reference subobjects) 266 /// but LLVM doesn't currently optimize on these cases -- so Clang emits 267 /// __builtin_launder as a nop. 268 /// 269 /// NOTE: Adding optimizations for these cases later is an LTO ABI break. That's 270 /// probably OK for now -- but is something to keep in mind. 271 namespace pessimizing_cases { 272 273 struct TestConstMember { 274 const int x; 275 }; 276 277 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_const_member 278 extern "C" void test_builtin_launder_const_member(TestConstMember *p) { 279 // CHECK: entry 280 // CHECK-NOT: @llvm.launder.invariant.group 281 // CHECK: ret void 282 TestConstMember *d = __builtin_launder(p); 283 } 284 285 struct TestConstSubobject { 286 TestConstMember x; 287 }; 288 289 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_const_subobject 290 extern "C" void test_builtin_launder_const_subobject(TestConstSubobject *p) { 291 // CHECK: entry 292 // CHECK-NOT: @llvm.launder.invariant.group 293 // CHECK: ret void 294 TestConstSubobject *d = __builtin_launder(p); 295 } 296 297 struct TestConstObject { 298 const struct TestConstMember x; 299 }; 300 301 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_const_object 302 extern "C" void test_builtin_launder_const_object(TestConstObject *p) { 303 // CHECK: entry 304 // CHECK-NOT: @llvm.launder.invariant.group 305 // CHECK: ret void 306 TestConstObject *d = __builtin_launder(p); 307 } 308 309 struct TestReferenceMember { 310 int &x; 311 }; 312 313 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_reference_member 314 extern "C" void test_builtin_launder_reference_member(TestReferenceMember *p) { 315 // CHECK: entry 316 // CHECK-NOT: @llvm.launder.invariant.group 317 // CHECK: ret void 318 TestReferenceMember *d = __builtin_launder(p); 319 } 320 321 } // namespace pessimizing_cases 322