1 // RUN: %clang_cc1 -no-opaque-pointers -triple x86_64-unknown-linux -flto -flto-unit -fvirtual-function-elimination -fwhole-program-vtables -emit-llvm -o - %s | FileCheck %s 2 // RUN: %clang -Xclang -no-opaque-pointers -target x86_64-unknown-linux -flto -fvirtual-function-elimination -fno-virtual-function-elimination -fwhole-program-vtables -S -emit-llvm -o - %s | FileCheck %s -check-prefix=NOVFE 3 4 struct __attribute__((visibility("default"))) A { 5 virtual void foo(); 6 }; 7 8 void test_1(A *p) { 9 // A has default visibility, so no need for type.checked.load. 10 // CHECK-LABEL: define{{.*}} void @_Z6test_1P1A 11 // NOVFE-LABEL: define dso_local void @_Z6test_1P1A 12 // CHECK: [[FN_PTR_ADDR:%.+]] = getelementptr inbounds void (%struct.A*)*, void (%struct.A*)** {{%.+}}, i64 0 13 // NOVFE: [[FN_PTR_ADDR:%.+]] = getelementptr inbounds void (%struct.A*)*, void (%struct.A*)** {{%.+}}, i64 0 14 // CHECK: [[FN_PTR:%.+]] = load void (%struct.A*)*, void (%struct.A*)** [[FN_PTR_ADDR]] 15 // NOVFE: [[FN_PTR:%.+]] = load void (%struct.A*)*, void (%struct.A*)** [[FN_PTR_ADDR]] 16 // CHECK: call void [[FN_PTR]]( 17 // NOVFE: call void [[FN_PTR]]( 18 p->foo(); 19 } 20 21 22 struct __attribute__((visibility("hidden"))) [[clang::lto_visibility_public]] B { 23 virtual void foo(); 24 }; 25 26 void test_2(B *p) { 27 // B has public LTO visibility, so no need for type.checked.load. 28 // CHECK-LABEL: define{{.*}} void @_Z6test_2P1B 29 // NOVFE-LABEL: define dso_local void @_Z6test_2P1B 30 // CHECK: [[FN_PTR_ADDR:%.+]] = getelementptr inbounds void (%struct.B*)*, void (%struct.B*)** {{%.+}}, i64 0 31 // NOVFE: [[FN_PTR_ADDR:%.+]] = getelementptr inbounds void (%struct.B*)*, void (%struct.B*)** {{%.+}}, i64 0 32 // CHECK: [[FN_PTR:%.+]] = load void (%struct.B*)*, void (%struct.B*)** [[FN_PTR_ADDR]] 33 // NOVFE: [[FN_PTR:%.+]] = load void (%struct.B*)*, void (%struct.B*)** [[FN_PTR_ADDR]] 34 // CHECK: call void [[FN_PTR]]( 35 // NOVFE: call void [[FN_PTR]]( 36 p->foo(); 37 } 38 39 40 struct __attribute__((visibility("hidden"))) C { 41 virtual void foo(); 42 virtual void bar(); 43 }; 44 45 void test_3(C *p) { 46 // C has hidden visibility, so we generate type.checked.load to allow VFE. 47 // CHECK-LABEL: define{{.*}} void @_Z6test_3P1C 48 // NOVFE-LABEL: define dso_local void @_Z6test_3P1C 49 // CHECK: [[LOAD:%.+]] = call { i8*, i1 } @llvm.type.checked.load(i8* {{%.+}}, i32 0, metadata !"_ZTS1C") 50 // NOVFE: call i1 @llvm.type.test(i8* {{%.+}}, metadata !"_ZTS1C") 51 // CHECK: [[FN_PTR_I8:%.+]] = extractvalue { i8*, i1 } [[LOAD]], 0 52 // NOVFE: [[FN_PTR:%.+]] = load void (%struct.C*)*, void (%struct.C*)** {{%.+}}, align 8 53 // CHECK: [[FN_PTR:%.+]] = bitcast i8* [[FN_PTR_I8]] to void (%struct.C*)* 54 // CHECK: call void [[FN_PTR]]( 55 // NOVFE: call void [[FN_PTR]]( 56 p->foo(); 57 } 58 59 void test_4(C *p) { 60 // When using type.checked.load, we pass the vtable offset to the intrinsic, 61 // rather than adding it to the pointer with a GEP. 62 // CHECK-LABEL: define{{.*}} void @_Z6test_4P1C 63 // NOVFE-LABEL: define dso_local void @_Z6test_4P1C 64 // CHECK: [[LOAD:%.+]] = call { i8*, i1 } @llvm.type.checked.load(i8* {{%.+}}, i32 8, metadata !"_ZTS1C") 65 // NOVFE: call i1 @llvm.type.test(i8* {{%.+}}, metadata !"_ZTS1C") 66 // CHECK: [[FN_PTR_I8:%.+]] = extractvalue { i8*, i1 } [[LOAD]], 0 67 // NOVFE: [[FN_PTR:%.+]] = load void (%struct.C*)*, void (%struct.C*)** {{%.+}}, align 8 68 // CHECK: [[FN_PTR:%.+]] = bitcast i8* [[FN_PTR_I8]] to void (%struct.C*)* 69 // CHECK: call void [[FN_PTR]]( 70 // NOVFE: call void [[FN_PTR]]( 71 p->bar(); 72 } 73 74 void test_5(C *p, void (C::*q)(void)) { 75 // We also use type.checked.load for the virtual side of member function 76 // pointer calls. We use a GEP to calculate the address to load from and pass 77 // 0 as the offset to the intrinsic, because we know that the load must be 78 // from exactly the point marked by one of the function-type metadatas (in 79 // this case "_ZTSM1CFvvE.virtual"). If we passed the offset from the member 80 // function pointer to the intrinsic, this information would be lost. No 81 // codegen changes on the non-virtual side. 82 // CHECK-LABEL: define{{.*}} void @_Z6test_5P1CMS_FvvE( 83 // NOVFE-LABEL: define dso_local void @_Z6test_5P1CMS_FvvE( 84 // CHECK: [[FN_PTR_ADDR:%.+]] = getelementptr i8, i8* %vtable, i64 {{%.+}} 85 // CHECK: [[LOAD:%.+]] = call { i8*, i1 } @llvm.type.checked.load(i8* [[FN_PTR_ADDR]], i32 0, metadata !"_ZTSM1CFvvE.virtual") 86 // NOVFE-NOT: call { i8*, i1 } @llvm.type.checked.load(i8* {{%.+}}, i32 0, metadata !"_ZTSM1CFvvE.virtual") 87 // CHECK: [[FN_PTR_I8:%.+]] = extractvalue { i8*, i1 } [[LOAD]], 0 88 // CHECK: [[FN_PTR:%.+]] = bitcast i8* [[FN_PTR_I8]] to void (%struct.C*)* 89 // NOVFE: [[FN_PTR:%.+]] = load void (%struct.C*)*, void (%struct.C*)** {{%.+}}, align 8 90 91 // CHECK: [[PHI:%.+]] = phi void (%struct.C*)* {{.*}}[ [[FN_PTR]], {{.*}} ] 92 // NOVFE: [[PHI:%.+]] = phi void (%struct.C*)* {{.*}}[ [[FN_PTR]], {{.*}} ] 93 // CHECK: call void [[PHI]]( 94 // NOVFE: call void [[PHI]]( 95 (p->*q)(); 96 } 97