Reid Kleckner | a3609b0 | 2013-04-11 18:13:19 +0000 | [diff] [blame] | 1 | // RUN: %clang_cc1 -fno-rtti -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 2 | // FIXME: Test x86_64 member pointers when codegen no longer asserts on records |
| 3 | // with virtual bases. |
Reid Kleckner | a3609b0 | 2013-04-11 18:13:19 +0000 | [diff] [blame] | 4 | |
| 5 | struct B1 { |
Reid Kleckner | 79e0291 | 2013-05-03 01:15:11 +0000 | [diff] [blame] | 6 | void foo(); |
Reid Kleckner | a3609b0 | 2013-04-11 18:13:19 +0000 | [diff] [blame] | 7 | int b; |
| 8 | }; |
Reid Kleckner | 79e0291 | 2013-05-03 01:15:11 +0000 | [diff] [blame] | 9 | struct B2 { |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 10 | int b2; |
Reid Kleckner | 79e0291 | 2013-05-03 01:15:11 +0000 | [diff] [blame] | 11 | void foo(); |
| 12 | }; |
| 13 | struct Single : B1 { |
| 14 | void foo(); |
| 15 | }; |
| 16 | struct Multiple : B1, B2 { |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 17 | int m; |
Reid Kleckner | 79e0291 | 2013-05-03 01:15:11 +0000 | [diff] [blame] | 18 | void foo(); |
| 19 | }; |
Reid Kleckner | a3609b0 | 2013-04-11 18:13:19 +0000 | [diff] [blame] | 20 | struct Virtual : virtual B1 { |
| 21 | int v; |
Reid Kleckner | 79e0291 | 2013-05-03 01:15:11 +0000 | [diff] [blame] | 22 | void foo(); |
Reid Kleckner | a3609b0 | 2013-04-11 18:13:19 +0000 | [diff] [blame] | 23 | }; |
Reid Kleckner | a8a0f76 | 2013-03-22 19:02:54 +0000 | [diff] [blame] | 24 | |
| 25 | struct POD { |
| 26 | int a; |
| 27 | int b; |
| 28 | }; |
| 29 | |
Reid Kleckner | a3609b0 | 2013-04-11 18:13:19 +0000 | [diff] [blame] | 30 | struct Polymorphic { |
| 31 | virtual void myVirtual(); |
| 32 | int a; |
| 33 | int b; |
| 34 | }; |
| 35 | |
| 36 | // This class uses the virtual inheritance model, yet its vbptr offset is not 0. |
| 37 | // We still use zero for the null field offset, despite it being a valid field |
| 38 | // offset. |
| 39 | struct NonZeroVBPtr : POD, Virtual { |
| 40 | int n; |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 41 | void foo(); |
Reid Kleckner | a3609b0 | 2013-04-11 18:13:19 +0000 | [diff] [blame] | 42 | }; |
| 43 | |
| 44 | struct Unspecified; |
| 45 | |
Reid Kleckner | 79e0291 | 2013-05-03 01:15:11 +0000 | [diff] [blame] | 46 | // Check that we can lower the LLVM types and get the null initializers right. |
Reid Kleckner | a3609b0 | 2013-04-11 18:13:19 +0000 | [diff] [blame] | 47 | int Single ::*s_d_memptr; |
| 48 | int Polymorphic::*p_d_memptr; |
| 49 | int Multiple ::*m_d_memptr; |
| 50 | int Virtual ::*v_d_memptr; |
| 51 | int NonZeroVBPtr::*n_d_memptr; |
| 52 | int Unspecified::*u_d_memptr; |
David Majnemer | c0e64f3 | 2013-08-05 22:43:06 +0000 | [diff] [blame^] | 53 | // CHECK: @"\01?s_d_memptr@@3PQSingle@@HQ1@" = global i32 -1, align 4 |
| 54 | // CHECK: @"\01?p_d_memptr@@3PQPolymorphic@@HQ1@" = global i32 0, align 4 |
| 55 | // CHECK: @"\01?m_d_memptr@@3PQMultiple@@HQ1@" = global i32 -1, align 4 |
| 56 | // CHECK: @"\01?v_d_memptr@@3PQVirtual@@HQ1@" = global { i32, i32 } |
Reid Kleckner | a3609b0 | 2013-04-11 18:13:19 +0000 | [diff] [blame] | 57 | // CHECK: { i32 0, i32 -1 }, align 4 |
David Majnemer | c0e64f3 | 2013-08-05 22:43:06 +0000 | [diff] [blame^] | 58 | // CHECK: @"\01?n_d_memptr@@3PQNonZeroVBPtr@@HQ1@" = global { i32, i32 } |
Reid Kleckner | a3609b0 | 2013-04-11 18:13:19 +0000 | [diff] [blame] | 59 | // CHECK: { i32 0, i32 -1 }, align 4 |
David Majnemer | c0e64f3 | 2013-08-05 22:43:06 +0000 | [diff] [blame^] | 60 | // CHECK: @"\01?u_d_memptr@@3PQUnspecified@@HQ1@" = global { i32, i32, i32 } |
Reid Kleckner | a3609b0 | 2013-04-11 18:13:19 +0000 | [diff] [blame] | 61 | // CHECK: { i32 0, i32 0, i32 -1 }, align 4 |
| 62 | |
| 63 | void (Single ::*s_f_memptr)(); |
| 64 | void (Multiple::*m_f_memptr)(); |
| 65 | void (Virtual ::*v_f_memptr)(); |
David Majnemer | c0e64f3 | 2013-08-05 22:43:06 +0000 | [diff] [blame^] | 66 | // CHECK: @"\01?s_f_memptr@@3P8Single@@AEXXZQ1@" = global i8* null, align 4 |
| 67 | // CHECK: @"\01?m_f_memptr@@3P8Multiple@@AEXXZQ1@" = global { i8*, i32 } zeroinitializer, align 4 |
| 68 | // CHECK: @"\01?v_f_memptr@@3P8Virtual@@AEXXZQ1@" = global { i8*, i32, i32 } zeroinitializer, align 4 |
Reid Kleckner | a3609b0 | 2013-04-11 18:13:19 +0000 | [diff] [blame] | 69 | |
Reid Kleckner | 79e0291 | 2013-05-03 01:15:11 +0000 | [diff] [blame] | 70 | // We can define Unspecified after locking in the inheritance model. |
Reid Kleckner | a06d585 | 2013-06-05 15:58:29 +0000 | [diff] [blame] | 71 | struct Unspecified : Multiple, Virtual { |
Reid Kleckner | 79e0291 | 2013-05-03 01:15:11 +0000 | [diff] [blame] | 72 | void foo(); |
| 73 | int u; |
| 74 | }; |
| 75 | |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 76 | // Test memptr emission in a constant expression. |
| 77 | namespace Const { |
| 78 | void (Single ::*s_f_mp)() = &Single::foo; |
| 79 | void (Multiple ::*m_f_mp)() = &B2::foo; |
| 80 | void (Virtual ::*v_f_mp)() = &Virtual::foo; |
| 81 | void (Unspecified::*u_f_mp)() = &Unspecified::foo; |
David Majnemer | c0e64f3 | 2013-08-05 22:43:06 +0000 | [diff] [blame^] | 82 | // CHECK: @"\01?s_f_mp@Const@@3P8Single@@AEXXZQ2@" = |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 83 | // CHECK: global i8* bitcast ({{.*}} @"\01?foo@Single@@QAEXXZ" to i8*), align 4 |
David Majnemer | c0e64f3 | 2013-08-05 22:43:06 +0000 | [diff] [blame^] | 84 | // CHECK: @"\01?m_f_mp@Const@@3P8Multiple@@AEXXZQ2@" = |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 85 | // CHECK: global { i8*, i32 } { i8* bitcast ({{.*}} @"\01?foo@B2@@QAEXXZ" to i8*), i32 4 }, align 4 |
David Majnemer | c0e64f3 | 2013-08-05 22:43:06 +0000 | [diff] [blame^] | 86 | // CHECK: @"\01?v_f_mp@Const@@3P8Virtual@@AEXXZQ2@" = |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 87 | // CHECK: global { i8*, i32, i32 } { i8* bitcast ({{.*}} @"\01?foo@Virtual@@QAEXXZ" to i8*), i32 0, i32 0 }, align 4 |
David Majnemer | c0e64f3 | 2013-08-05 22:43:06 +0000 | [diff] [blame^] | 88 | // CHECK: @"\01?u_f_mp@Const@@3P8Unspecified@@AEXXZQ2@" = |
Reid Kleckner | a06d585 | 2013-06-05 15:58:29 +0000 | [diff] [blame] | 89 | // CHECK: global { i8*, i32, i32, i32 } { i8* bitcast ({{.*}} @"\01?foo@Unspecified@@QAEXXZ" to i8*), i32 0, i32 12, i32 0 }, align 4 |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 90 | } |
| 91 | |
| 92 | namespace CastParam { |
| 93 | // This exercises ConstExprEmitter instead of ValueDecl::evaluateValue. The |
| 94 | // extra reinterpret_cast for the parameter type requires more careful folding. |
| 95 | // FIXME: Or does it? If reinterpret_casts are no-ops, we should be able to |
| 96 | // strip them in evaluateValue() and just proceed as normal with an APValue. |
| 97 | struct A { |
| 98 | int a; |
| 99 | void foo(A *p); |
| 100 | }; |
| 101 | struct B { int b; }; |
| 102 | struct C : B, A { int c; }; |
| 103 | |
| 104 | void (A::*ptr1)(void *) = (void (A::*)(void *)) &A::foo; |
David Majnemer | c0e64f3 | 2013-08-05 22:43:06 +0000 | [diff] [blame^] | 105 | // CHECK: @"\01?ptr1@CastParam@@3P8A@1@AEXPAX@ZQ21@" = |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 106 | // CHECK: global i8* bitcast (void ({{.*}})* @"\01?foo@A@CastParam@@QAEXPAU12@@Z" to i8*), align 4 |
| 107 | |
| 108 | // Try a reinterpret_cast followed by a memptr conversion. |
| 109 | void (C::*ptr2)(void *) = (void (C::*)(void *)) (void (A::*)(void *)) &A::foo; |
David Majnemer | c0e64f3 | 2013-08-05 22:43:06 +0000 | [diff] [blame^] | 110 | // CHECK: @"\01?ptr2@CastParam@@3P8C@1@AEXPAX@ZQ21@" = |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 111 | // CHECK: global { i8*, i32 } { i8* bitcast (void ({{.*}})* @"\01?foo@A@CastParam@@QAEXPAU12@@Z" to i8*), i32 4 }, align 4 |
| 112 | |
| 113 | void (C::*ptr3)(void *) = (void (C::*)(void *)) (void (A::*)(void *)) (void (A::*)(A *)) 0; |
David Majnemer | c0e64f3 | 2013-08-05 22:43:06 +0000 | [diff] [blame^] | 114 | // CHECK: @"\01?ptr3@CastParam@@3P8C@1@AEXPAX@ZQ21@" = |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 115 | // CHECK: global { i8*, i32 } zeroinitializer, align 4 |
| 116 | |
| 117 | struct D : C { |
| 118 | virtual void isPolymorphic(); |
| 119 | int d; |
| 120 | }; |
| 121 | |
| 122 | // Try a cast that changes the inheritance model. Null for D is 0, but null for |
| 123 | // C is -1. We need the cast to long in order to hit the non-APValue path. |
| 124 | int C::*ptr4 = (int C::*) (int D::*) (long D::*) 0; |
David Majnemer | c0e64f3 | 2013-08-05 22:43:06 +0000 | [diff] [blame^] | 125 | // CHECK: @"\01?ptr4@CastParam@@3PQC@1@HQ21@" = global i32 -1, align 4 |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 126 | |
| 127 | // MSVC rejects this but we accept it. |
| 128 | int C::*ptr5 = (int C::*) (long D::*) 0; |
David Majnemer | c0e64f3 | 2013-08-05 22:43:06 +0000 | [diff] [blame^] | 129 | // CHECK: @"\01?ptr5@CastParam@@3PQC@1@HQ21@" = global i32 -1, align 4 |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 130 | } |
| 131 | |
Reid Kleckner | 79e0291 | 2013-05-03 01:15:11 +0000 | [diff] [blame] | 132 | struct UnspecWithVBPtr; |
| 133 | int UnspecWithVBPtr::*forceUnspecWithVBPtr; |
| 134 | struct UnspecWithVBPtr : B1, virtual B2 { |
| 135 | int u; |
| 136 | void foo(); |
| 137 | }; |
| 138 | |
| 139 | // Test emitting non-virtual member pointers in a non-constexpr setting. |
| 140 | void EmitNonVirtualMemberPointers() { |
| 141 | void (Single ::*s_f_memptr)() = &Single::foo; |
| 142 | void (Multiple ::*m_f_memptr)() = &Multiple::foo; |
| 143 | void (Virtual ::*v_f_memptr)() = &Virtual::foo; |
| 144 | void (Unspecified::*u_f_memptr)() = &Unspecified::foo; |
| 145 | void (UnspecWithVBPtr::*u2_f_memptr)() = &UnspecWithVBPtr::foo; |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 146 | // CHECK: define void @"\01?EmitNonVirtualMemberPointers@@YAXXZ"() {{.*}} { |
Reid Kleckner | 79e0291 | 2013-05-03 01:15:11 +0000 | [diff] [blame] | 147 | // CHECK: alloca i8*, align 4 |
| 148 | // CHECK: alloca { i8*, i32 }, align 4 |
| 149 | // CHECK: alloca { i8*, i32, i32 }, align 4 |
| 150 | // CHECK: alloca { i8*, i32, i32, i32 }, align 4 |
| 151 | // CHECK: store i8* bitcast (void (%{{.*}}*)* @"\01?foo@Single@@QAEXXZ" to i8*), i8** %{{.*}}, align 4 |
| 152 | // CHECK: store { i8*, i32 } |
| 153 | // CHECK: { i8* bitcast (void (%{{.*}}*)* @"\01?foo@Multiple@@QAEXXZ" to i8*), i32 0 }, |
| 154 | // CHECK: { i8*, i32 }* %{{.*}}, align 4 |
| 155 | // CHECK: store { i8*, i32, i32 } |
| 156 | // CHECK: { i8* bitcast (void (%{{.*}}*)* @"\01?foo@Virtual@@QAEXXZ" to i8*), i32 0, i32 0 }, |
| 157 | // CHECK: { i8*, i32, i32 }* %{{.*}}, align 4 |
| 158 | // CHECK: store { i8*, i32, i32, i32 } |
Reid Kleckner | a06d585 | 2013-06-05 15:58:29 +0000 | [diff] [blame] | 159 | // CHECK: { i8* bitcast (void (%{{.*}}*)* @"\01?foo@Unspecified@@QAEXXZ" to i8*), i32 0, i32 12, i32 0 }, |
Reid Kleckner | 79e0291 | 2013-05-03 01:15:11 +0000 | [diff] [blame] | 160 | // CHECK: { i8*, i32, i32, i32 }* %{{.*}}, align 4 |
| 161 | // CHECK: store { i8*, i32, i32, i32 } |
| 162 | // CHECK: { i8* bitcast (void (%{{.*}}*)* @"\01?foo@UnspecWithVBPtr@@QAEXXZ" to i8*), |
| 163 | // CHECK: i32 0, i32 4, i32 0 }, |
| 164 | // CHECK: { i8*, i32, i32, i32 }* %{{.*}}, align 4 |
| 165 | // CHECK: ret void |
| 166 | // CHECK: } |
| 167 | } |
| 168 | |
Reid Kleckner | a8a0f76 | 2013-03-22 19:02:54 +0000 | [diff] [blame] | 169 | void podMemPtrs() { |
| 170 | int POD::*memptr; |
| 171 | memptr = &POD::a; |
| 172 | memptr = &POD::b; |
| 173 | if (memptr) |
| 174 | memptr = 0; |
| 175 | // Check that member pointers use the right offsets and that null is -1. |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 176 | // CHECK: define void @"\01?podMemPtrs@@YAXXZ"() {{.*}} { |
Reid Kleckner | a8a0f76 | 2013-03-22 19:02:54 +0000 | [diff] [blame] | 177 | // CHECK: %[[memptr:.*]] = alloca i32, align 4 |
| 178 | // CHECK-NEXT: store i32 0, i32* %[[memptr]], align 4 |
| 179 | // CHECK-NEXT: store i32 4, i32* %[[memptr]], align 4 |
| 180 | // CHECK-NEXT: %[[memptr_val:.*]] = load i32* %[[memptr]], align 4 |
| 181 | // CHECK-NEXT: %{{.*}} = icmp ne i32 %[[memptr_val]], -1 |
| 182 | // CHECK-NEXT: br i1 %{{.*}}, label %{{.*}}, label %{{.*}} |
| 183 | // CHECK: store i32 -1, i32* %[[memptr]], align 4 |
| 184 | // CHECK: ret void |
| 185 | // CHECK: } |
| 186 | } |
| 187 | |
Reid Kleckner | a8a0f76 | 2013-03-22 19:02:54 +0000 | [diff] [blame] | 188 | void polymorphicMemPtrs() { |
| 189 | int Polymorphic::*memptr; |
| 190 | memptr = &Polymorphic::a; |
| 191 | memptr = &Polymorphic::b; |
| 192 | if (memptr) |
| 193 | memptr = 0; |
| 194 | // Member pointers for polymorphic classes include the vtable slot in their |
| 195 | // offset and use 0 to represent null. |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 196 | // CHECK: define void @"\01?polymorphicMemPtrs@@YAXXZ"() {{.*}} { |
Reid Kleckner | a8a0f76 | 2013-03-22 19:02:54 +0000 | [diff] [blame] | 197 | // CHECK: %[[memptr:.*]] = alloca i32, align 4 |
| 198 | // CHECK-NEXT: store i32 4, i32* %[[memptr]], align 4 |
| 199 | // CHECK-NEXT: store i32 8, i32* %[[memptr]], align 4 |
| 200 | // CHECK-NEXT: %[[memptr_val:.*]] = load i32* %[[memptr]], align 4 |
| 201 | // CHECK-NEXT: %{{.*}} = icmp ne i32 %[[memptr_val]], 0 |
| 202 | // CHECK-NEXT: br i1 %{{.*}}, label %{{.*}}, label %{{.*}} |
| 203 | // CHECK: store i32 0, i32* %[[memptr]], align 4 |
| 204 | // CHECK: ret void |
| 205 | // CHECK: } |
| 206 | } |
Reid Kleckner | a3609b0 | 2013-04-11 18:13:19 +0000 | [diff] [blame] | 207 | |
| 208 | bool nullTestDataUnspecified(int Unspecified::*mp) { |
| 209 | return mp; |
| 210 | // CHECK: define zeroext i1 @"\01?nullTestDataUnspecified@@YA_NPQUnspecified@@H@Z"{{.*}} { |
| 211 | // CHECK: %{{.*}} = load { i32, i32, i32 }* %{{.*}}, align 4 |
| 212 | // CHECK: store { i32, i32, i32 } {{.*}} align 4 |
| 213 | // CHECK: %[[mp:.*]] = load { i32, i32, i32 }* %{{.*}}, align 4 |
| 214 | // CHECK: %[[mp0:.*]] = extractvalue { i32, i32, i32 } %[[mp]], 0 |
| 215 | // CHECK: %[[cmp0:.*]] = icmp ne i32 %[[mp0]], 0 |
| 216 | // CHECK: %[[mp1:.*]] = extractvalue { i32, i32, i32 } %[[mp]], 1 |
| 217 | // CHECK: %[[cmp1:.*]] = icmp ne i32 %[[mp1]], 0 |
| 218 | // CHECK: %[[and0:.*]] = and i1 %[[cmp0]], %[[cmp1]] |
| 219 | // CHECK: %[[mp2:.*]] = extractvalue { i32, i32, i32 } %[[mp]], 2 |
| 220 | // CHECK: %[[cmp2:.*]] = icmp ne i32 %[[mp2]], -1 |
| 221 | // CHECK: %[[and1:.*]] = and i1 %[[and0]], %[[cmp2]] |
| 222 | // CHECK: ret i1 %[[and1]] |
| 223 | // CHECK: } |
| 224 | } |
| 225 | |
| 226 | bool nullTestFunctionUnspecified(void (Unspecified::*mp)()) { |
| 227 | return mp; |
| 228 | // CHECK: define zeroext i1 @"\01?nullTestFunctionUnspecified@@YA_NP8Unspecified@@AEXXZ@Z"{{.*}} { |
| 229 | // CHECK: %{{.*}} = load { i8*, i32, i32, i32 }* %{{.*}}, align 4 |
| 230 | // CHECK: store { i8*, i32, i32, i32 } {{.*}} align 4 |
| 231 | // CHECK: %[[mp:.*]] = load { i8*, i32, i32, i32 }* %{{.*}}, align 4 |
| 232 | // CHECK: %[[mp0:.*]] = extractvalue { i8*, i32, i32, i32 } %[[mp]], 0 |
| 233 | // CHECK: %[[cmp0:.*]] = icmp ne i8* %[[mp0]], null |
| 234 | // CHECK: ret i1 %[[cmp0]] |
| 235 | // CHECK: } |
| 236 | } |
| 237 | |
| 238 | int loadDataMemberPointerVirtual(Virtual *o, int Virtual::*memptr) { |
| 239 | return o->*memptr; |
| 240 | // Test that we can unpack this aggregate member pointer and load the member |
| 241 | // data pointer. |
| 242 | // CHECK: define i32 @"\01?loadDataMemberPointerVirtual@@YAHPAUVirtual@@PQ1@H@Z"{{.*}} { |
| 243 | // CHECK: %[[o:.*]] = load %{{.*}}** %{{.*}}, align 4 |
Reid Kleckner | 49eef34 | 2013-04-11 19:01:17 +0000 | [diff] [blame] | 244 | // CHECK: %[[memptr:.*]] = load { i32, i32 }* %{{.*}}, align 4 |
Reid Kleckner | a3609b0 | 2013-04-11 18:13:19 +0000 | [diff] [blame] | 245 | // CHECK: %[[memptr0:.*]] = extractvalue { i32, i32 } %[[memptr:.*]], 0 |
| 246 | // CHECK: %[[memptr1:.*]] = extractvalue { i32, i32 } %[[memptr:.*]], 1 |
| 247 | // CHECK: %[[v6:.*]] = bitcast %{{.*}}* %[[o]] to i8* |
| 248 | // CHECK: %[[vbptr:.*]] = getelementptr inbounds i8* %[[v6]], i32 0 |
| 249 | // CHECK: %[[vbptr_a:.*]] = bitcast i8* %[[vbptr]] to i8** |
| 250 | // CHECK: %[[vbtable:.*]] = load i8** %[[vbptr_a:.*]] |
| 251 | // CHECK: %[[v7:.*]] = getelementptr inbounds i8* %[[vbtable]], i32 %[[memptr1]] |
| 252 | // CHECK: %[[v8:.*]] = bitcast i8* %[[v7]] to i32* |
| 253 | // CHECK: %[[vbase_offs:.*]] = load i32* %[[v8]] |
| 254 | // CHECK: %[[v10:.*]] = getelementptr inbounds i8* %[[vbptr]], i32 %[[vbase_offs]] |
| 255 | // CHECK: %[[offset:.*]] = getelementptr inbounds i8* %[[v10]], i32 %[[memptr0]] |
| 256 | // CHECK: %[[v11:.*]] = bitcast i8* %[[offset]] to i32* |
| 257 | // CHECK: %[[v12:.*]] = load i32* %[[v11]] |
| 258 | // CHECK: ret i32 %[[v12]] |
| 259 | // CHECK: } |
| 260 | } |
| 261 | |
| 262 | int loadDataMemberPointerUnspecified(Unspecified *o, int Unspecified::*memptr) { |
| 263 | return o->*memptr; |
| 264 | // Test that we can unpack this aggregate member pointer and load the member |
| 265 | // data pointer. |
| 266 | // CHECK: define i32 @"\01?loadDataMemberPointerUnspecified@@YAHPAUUnspecified@@PQ1@H@Z"{{.*}} { |
| 267 | // CHECK: %[[o:.*]] = load %{{.*}}** %{{.*}}, align 4 |
Reid Kleckner | 49eef34 | 2013-04-11 19:01:17 +0000 | [diff] [blame] | 268 | // CHECK: %[[memptr:.*]] = load { i32, i32, i32 }* %{{.*}}, align 4 |
Reid Kleckner | a3609b0 | 2013-04-11 18:13:19 +0000 | [diff] [blame] | 269 | // CHECK: %[[memptr0:.*]] = extractvalue { i32, i32, i32 } %[[memptr:.*]], 0 |
| 270 | // CHECK: %[[memptr1:.*]] = extractvalue { i32, i32, i32 } %[[memptr:.*]], 1 |
| 271 | // CHECK: %[[memptr2:.*]] = extractvalue { i32, i32, i32 } %[[memptr:.*]], 2 |
| 272 | // CHECK: %[[base:.*]] = bitcast %{{.*}}* %[[o]] to i8* |
| 273 | // CHECK: %[[is_vbase:.*]] = icmp ne i32 %[[memptr2]], 0 |
| 274 | // CHECK: br i1 %[[is_vbase]], label %[[vadjust:.*]], label %[[skip:.*]] |
| 275 | // |
Reid Kleckner | 49eef34 | 2013-04-11 19:01:17 +0000 | [diff] [blame] | 276 | // CHECK: [[vadjust]] |
Reid Kleckner | a3609b0 | 2013-04-11 18:13:19 +0000 | [diff] [blame] | 277 | // CHECK: %[[vbptr:.*]] = getelementptr inbounds i8* %[[base]], i32 %[[memptr1]] |
| 278 | // CHECK: %[[vbptr_a:.*]] = bitcast i8* %[[vbptr]] to i8** |
| 279 | // CHECK: %[[vbtable:.*]] = load i8** %[[vbptr_a:.*]] |
| 280 | // CHECK: %[[v7:.*]] = getelementptr inbounds i8* %[[vbtable]], i32 %[[memptr2]] |
| 281 | // CHECK: %[[v8:.*]] = bitcast i8* %[[v7]] to i32* |
| 282 | // CHECK: %[[vbase_offs:.*]] = load i32* %[[v8]] |
| 283 | // CHECK: %[[base_adj:.*]] = getelementptr inbounds i8* %[[vbptr]], i32 %[[vbase_offs]] |
| 284 | // |
Reid Kleckner | 49eef34 | 2013-04-11 19:01:17 +0000 | [diff] [blame] | 285 | // CHECK: [[skip]] |
| 286 | // CHECK: %[[new_base:.*]] = phi i8* [ %[[base]], %{{.*}} ], [ %[[base_adj]], %[[vadjust]] ] |
Reid Kleckner | a3609b0 | 2013-04-11 18:13:19 +0000 | [diff] [blame] | 287 | // CHECK: %[[offset:.*]] = getelementptr inbounds i8* %[[new_base]], i32 %[[memptr0]] |
| 288 | // CHECK: %[[v11:.*]] = bitcast i8* %[[offset]] to i32* |
| 289 | // CHECK: %[[v12:.*]] = load i32* %[[v11]] |
| 290 | // CHECK: ret i32 %[[v12]] |
| 291 | // CHECK: } |
| 292 | } |
| 293 | |
| 294 | void callMemberPointerSingle(Single *o, void (Single::*memptr)()) { |
| 295 | (o->*memptr)(); |
| 296 | // Just look for an indirect thiscall. |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 297 | // CHECK: define void @"\01?callMemberPointerSingle@@{{.*}} {{.*}} { |
Reid Kleckner | a3609b0 | 2013-04-11 18:13:19 +0000 | [diff] [blame] | 298 | // CHECK: call x86_thiscallcc void %{{.*}}(%{{.*}} %{{.*}}) |
| 299 | // CHECK: ret void |
| 300 | // CHECK: } |
| 301 | } |
| 302 | |
| 303 | void callMemberPointerMultiple(Multiple *o, void (Multiple::*memptr)()) { |
| 304 | (o->*memptr)(); |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 305 | // CHECK: define void @"\01?callMemberPointerMultiple@@{{.*}} { |
Reid Kleckner | a3609b0 | 2013-04-11 18:13:19 +0000 | [diff] [blame] | 306 | // CHECK: %[[memptr0:.*]] = extractvalue { i8*, i32 } %{{.*}}, 0 |
| 307 | // CHECK: %[[memptr1:.*]] = extractvalue { i8*, i32 } %{{.*}}, 1 |
| 308 | // CHECK: %[[this_adjusted:.*]] = getelementptr inbounds i8* %{{.*}}, i32 %[[memptr1]] |
| 309 | // CHECK: %[[this:.*]] = bitcast i8* %[[this_adjusted]] to {{.*}} |
| 310 | // CHECK: %[[fptr:.*]] = bitcast i8* %[[memptr0]] to {{.*}} |
| 311 | // CHECK: call x86_thiscallcc void %[[fptr]](%{{.*}} %[[this]]) |
| 312 | // CHECK: ret void |
| 313 | // CHECK: } |
| 314 | } |
| 315 | |
| 316 | void callMemberPointerVirtualBase(Virtual *o, void (Virtual::*memptr)()) { |
| 317 | (o->*memptr)(); |
| 318 | // This shares a lot with virtual data member pointers. |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 319 | // CHECK: define void @"\01?callMemberPointerVirtualBase@@{{.*}} { |
Reid Kleckner | a3609b0 | 2013-04-11 18:13:19 +0000 | [diff] [blame] | 320 | // CHECK: %[[memptr0:.*]] = extractvalue { i8*, i32, i32 } %{{.*}}, 0 |
| 321 | // CHECK: %[[memptr1:.*]] = extractvalue { i8*, i32, i32 } %{{.*}}, 1 |
| 322 | // CHECK: %[[memptr2:.*]] = extractvalue { i8*, i32, i32 } %{{.*}}, 2 |
| 323 | // CHECK: %[[vbptr:.*]] = getelementptr inbounds i8* %{{.*}}, i32 0 |
| 324 | // CHECK: %[[vbptr_a:.*]] = bitcast i8* %[[vbptr]] to i8** |
| 325 | // CHECK: %[[vbtable:.*]] = load i8** %[[vbptr_a:.*]] |
| 326 | // CHECK: %[[v7:.*]] = getelementptr inbounds i8* %[[vbtable]], i32 %[[memptr2]] |
| 327 | // CHECK: %[[v8:.*]] = bitcast i8* %[[v7]] to i32* |
| 328 | // CHECK: %[[vbase_offs:.*]] = load i32* %[[v8]] |
| 329 | // CHECK: %[[v10:.*]] = getelementptr inbounds i8* %[[vbptr]], i32 %[[vbase_offs]] |
| 330 | // CHECK: %[[this_adjusted:.*]] = getelementptr inbounds i8* %[[v10]], i32 %[[memptr1]] |
| 331 | // CHECK: %[[fptr:.*]] = bitcast i8* %[[memptr0]] to void ({{.*}}) |
| 332 | // CHECK: %[[this:.*]] = bitcast i8* %[[this_adjusted]] to {{.*}} |
| 333 | // CHECK: call x86_thiscallcc void %[[fptr]](%{{.*}} %[[this]]) |
| 334 | // CHECK: ret void |
| 335 | // CHECK: } |
| 336 | } |
Reid Kleckner | 3d2f000 | 2013-04-30 20:15:14 +0000 | [diff] [blame] | 337 | |
| 338 | bool compareSingleFunctionMemptr(void (Single::*l)(), void (Single::*r)()) { |
| 339 | return l == r; |
| 340 | // Should only be one comparison here. |
| 341 | // CHECK: define zeroext i1 @"\01?compareSingleFunctionMemptr@@YA_NP8Single@@AEXXZ0@Z"{{.*}} { |
| 342 | // CHECK-NOT: icmp |
| 343 | // CHECK: %[[r:.*]] = icmp eq |
| 344 | // CHECK-NOT: icmp |
| 345 | // CHECK: ret i1 %[[r]] |
| 346 | // CHECK: } |
| 347 | } |
| 348 | |
| 349 | bool compareNeqSingleFunctionMemptr(void (Single::*l)(), void (Single::*r)()) { |
| 350 | return l != r; |
| 351 | // Should only be one comparison here. |
| 352 | // CHECK: define zeroext i1 @"\01?compareNeqSingleFunctionMemptr@@YA_NP8Single@@AEXXZ0@Z"{{.*}} { |
| 353 | // CHECK-NOT: icmp |
| 354 | // CHECK: %[[r:.*]] = icmp ne |
| 355 | // CHECK-NOT: icmp |
| 356 | // CHECK: ret i1 %[[r]] |
| 357 | // CHECK: } |
| 358 | } |
| 359 | |
| 360 | bool unspecFuncMemptrEq(void (Unspecified::*l)(), void (Unspecified::*r)()) { |
| 361 | return l == r; |
| 362 | // CHECK: define zeroext i1 @"\01?unspecFuncMemptrEq@@YA_NP8Unspecified@@AEXXZ0@Z"{{.*}} { |
| 363 | // CHECK: %[[lhs0:.*]] = extractvalue { i8*, i32, i32, i32 } %[[l:.*]], 0 |
| 364 | // CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[r:.*]], 0 |
| 365 | // CHECK: %[[cmp0:.*]] = icmp eq i8* %[[lhs0]], %{{.*}} |
| 366 | // CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[l]], 1 |
| 367 | // CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[r]], 1 |
| 368 | // CHECK: %[[cmp1:.*]] = icmp eq i32 |
| 369 | // CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[l]], 2 |
| 370 | // CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[r]], 2 |
| 371 | // CHECK: %[[cmp2:.*]] = icmp eq i32 |
| 372 | // CHECK: %[[res12:.*]] = and i1 %[[cmp1]], %[[cmp2]] |
| 373 | // CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[l]], 3 |
| 374 | // CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[r]], 3 |
| 375 | // CHECK: %[[cmp3:.*]] = icmp eq i32 |
| 376 | // CHECK: %[[res123:.*]] = and i1 %[[res12]], %[[cmp3]] |
| 377 | // CHECK: %[[iszero:.*]] = icmp eq i8* %[[lhs0]], null |
| 378 | // CHECK: %[[bits_or_null:.*]] = or i1 %[[res123]], %[[iszero]] |
| 379 | // CHECK: %{{.*}} = and i1 %[[bits_or_null]], %[[cmp0]] |
| 380 | // CHECK: ret i1 %{{.*}} |
| 381 | // CHECK: } |
| 382 | } |
| 383 | |
| 384 | bool unspecFuncMemptrNeq(void (Unspecified::*l)(), void (Unspecified::*r)()) { |
| 385 | return l != r; |
| 386 | // CHECK: define zeroext i1 @"\01?unspecFuncMemptrNeq@@YA_NP8Unspecified@@AEXXZ0@Z"{{.*}} { |
| 387 | // CHECK: %[[lhs0:.*]] = extractvalue { i8*, i32, i32, i32 } %[[l:.*]], 0 |
| 388 | // CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[r:.*]], 0 |
| 389 | // CHECK: %[[cmp0:.*]] = icmp ne i8* %[[lhs0]], %{{.*}} |
| 390 | // CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[l]], 1 |
| 391 | // CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[r]], 1 |
| 392 | // CHECK: %[[cmp1:.*]] = icmp ne i32 |
| 393 | // CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[l]], 2 |
| 394 | // CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[r]], 2 |
| 395 | // CHECK: %[[cmp2:.*]] = icmp ne i32 |
| 396 | // CHECK: %[[res12:.*]] = or i1 %[[cmp1]], %[[cmp2]] |
| 397 | // CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[l]], 3 |
| 398 | // CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[r]], 3 |
| 399 | // CHECK: %[[cmp3:.*]] = icmp ne i32 |
| 400 | // CHECK: %[[res123:.*]] = or i1 %[[res12]], %[[cmp3]] |
| 401 | // CHECK: %[[iszero:.*]] = icmp ne i8* %[[lhs0]], null |
| 402 | // CHECK: %[[bits_or_null:.*]] = and i1 %[[res123]], %[[iszero]] |
| 403 | // CHECK: %{{.*}} = or i1 %[[bits_or_null]], %[[cmp0]] |
| 404 | // CHECK: ret i1 %{{.*}} |
| 405 | // CHECK: } |
| 406 | } |
| 407 | |
| 408 | bool unspecDataMemptrEq(int Unspecified::*l, int Unspecified::*r) { |
| 409 | return l == r; |
| 410 | // CHECK: define zeroext i1 @"\01?unspecDataMemptrEq@@YA_NPQUnspecified@@H0@Z"{{.*}} { |
| 411 | // CHECK: extractvalue { i32, i32, i32 } %{{.*}}, 0 |
| 412 | // CHECK: extractvalue { i32, i32, i32 } %{{.*}}, 0 |
| 413 | // CHECK: icmp eq i32 |
| 414 | // CHECK: extractvalue { i32, i32, i32 } %{{.*}}, 1 |
| 415 | // CHECK: extractvalue { i32, i32, i32 } %{{.*}}, 1 |
| 416 | // CHECK: icmp eq i32 |
| 417 | // CHECK: extractvalue { i32, i32, i32 } %{{.*}}, 2 |
| 418 | // CHECK: extractvalue { i32, i32, i32 } %{{.*}}, 2 |
| 419 | // CHECK: icmp eq i32 |
| 420 | // CHECK: and i1 |
| 421 | // CHECK: and i1 |
| 422 | // CHECK: ret i1 |
| 423 | // CHECK: } |
| 424 | } |
Reid Kleckner | f632730 | 2013-05-09 21:01:17 +0000 | [diff] [blame] | 425 | |
| 426 | void (Multiple::*convertB2FuncToMultiple(void (B2::*mp)()))() { |
| 427 | return mp; |
| 428 | // CHECK: define i64 @"\01?convertB2FuncToMultiple@@YAP8Multiple@@AEXXZP8B2@@AEXXZ@Z"{{.*}} { |
| 429 | // CHECK: store |
| 430 | // CHECK: %[[mp:.*]] = load i8** %{{.*}}, align 4 |
| 431 | // CHECK: icmp ne i8* %[[mp]], null |
| 432 | // CHECK: br i1 %{{.*}} label %{{.*}}, label %{{.*}} |
| 433 | // |
| 434 | // memptr.convert: ; preds = %entry |
| 435 | // CHECK: insertvalue { i8*, i32 } undef, i8* %[[mp]], 0 |
| 436 | // CHECK: insertvalue { i8*, i32 } %{{.*}}, i32 4, 1 |
| 437 | // CHECK: br label |
| 438 | // |
| 439 | // memptr.converted: ; preds = %memptr.convert, %entry |
| 440 | // CHECK: phi { i8*, i32 } [ zeroinitializer, %{{.*}} ], [ {{.*}} ] |
| 441 | // CHECK: } |
| 442 | } |
| 443 | |
| 444 | void (B2::*convertMultipleFuncToB2(void (Multiple::*mp)()))() { |
| 445 | // FIXME: cl emits warning C4407 on this code because of the representation |
| 446 | // change. We might want to do the same. |
| 447 | return static_cast<void (B2::*)()>(mp); |
| 448 | // FIXME: We should return i8* instead of i32 here. The ptrtoint cast prevents |
| 449 | // LLVM from optimizing away the branch. This is likely a bug in |
| 450 | // lib/CodeGen/TargetInfo.cpp with how we classify memptr types for returns. |
| 451 | // |
| 452 | // CHECK: define i32 @"\01?convertMultipleFuncToB2@@YAP8B2@@AEXXZP8Multiple@@AEXXZ@Z"{{.*}} { |
| 453 | // CHECK: store |
| 454 | // CHECK: %[[src:.*]] = load { i8*, i32 }* %{{.*}}, align 4 |
| 455 | // CHECK: extractvalue { i8*, i32 } %[[src]], 0 |
| 456 | // CHECK: icmp ne i8* %{{.*}}, null |
| 457 | // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}} |
| 458 | // |
| 459 | // memptr.convert: ; preds = %entry |
| 460 | // CHECK: %[[fp:.*]] = extractvalue { i8*, i32 } %[[src]], 0 |
| 461 | // CHECK: br label |
| 462 | // |
| 463 | // memptr.converted: ; preds = %memptr.convert, %entry |
| 464 | // CHECK: phi i8* [ null, %{{.*}} ], [ %[[fp]], %{{.*}} ] |
| 465 | // CHECK: } |
| 466 | } |
| 467 | |
| 468 | namespace Test1 { |
| 469 | |
| 470 | struct A { int a; }; |
| 471 | struct B { int b; }; |
| 472 | struct C : virtual A { int c; }; |
| 473 | struct D : B, C { int d; }; |
| 474 | |
| 475 | void (D::*convertCToD(void (C::*mp)()))() { |
| 476 | return mp; |
| 477 | // CHECK: define void @"\01?convertCToD@Test1@@YAP8D@1@AEXXZP8C@1@AEXXZ@Z"{{.*}} { |
| 478 | // CHECK: store |
| 479 | // CHECK: load { i8*, i32, i32 }* %{{.*}}, align 4 |
| 480 | // CHECK: extractvalue { i8*, i32, i32 } %{{.*}}, 0 |
| 481 | // CHECK: icmp ne i8* %{{.*}}, null |
| 482 | // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}} |
| 483 | // |
| 484 | // memptr.convert: ; preds = %entry |
| 485 | // CHECK: extractvalue { i8*, i32, i32 } %{{.*}}, 0 |
| 486 | // CHECK: extractvalue { i8*, i32, i32 } %{{.*}}, 1 |
| 487 | // CHECK: extractvalue { i8*, i32, i32 } %{{.*}}, 2 |
| 488 | // CHECK: %[[adj:.*]] = add nsw i32 %{{.*}}, 4 |
| 489 | // CHECK: insertvalue { i8*, i32, i32 } undef, i8* {{.*}}, 0 |
| 490 | // CHECK: insertvalue { i8*, i32, i32 } {{.*}}, i32 %[[adj]], 1 |
| 491 | // CHECK: insertvalue { i8*, i32, i32 } {{.*}}, i32 {{.*}}, 2 |
| 492 | // CHECK: br label |
| 493 | // |
| 494 | // memptr.converted: ; preds = %memptr.convert, %entry |
| 495 | // CHECK: phi { i8*, i32, i32 } [ { i8* null, i32 0, i32 -1 }, {{.*}} ], [ {{.*}} ] |
| 496 | // CHECK: } |
| 497 | } |
| 498 | |
| 499 | } |
| 500 | |
| 501 | namespace Test2 { |
| 502 | // Test that we dynamically convert between different null reps. |
| 503 | |
| 504 | struct A { int a; }; |
| 505 | struct B : A { int b; }; |
| 506 | struct C : A { |
| 507 | int c; |
| 508 | virtual void hasVfPtr(); |
| 509 | }; |
| 510 | |
| 511 | int A::*reinterpret(int B::*mp) { |
| 512 | return reinterpret_cast<int A::*>(mp); |
| 513 | // CHECK: define i32 @"\01?reinterpret@Test2@@YAPQA@1@HPQB@1@H@Z"{{.*}} { |
| 514 | // CHECK-NOT: select |
| 515 | // CHECK: ret i32 |
| 516 | // CHECK: } |
| 517 | } |
| 518 | |
| 519 | int A::*reinterpret(int C::*mp) { |
| 520 | return reinterpret_cast<int A::*>(mp); |
| 521 | // CHECK: define i32 @"\01?reinterpret@Test2@@YAPQA@1@HPQC@1@H@Z"{{.*}} { |
| 522 | // CHECK: %[[mp:.*]] = load i32* |
| 523 | // CHECK: %[[cmp:.*]] = icmp ne i32 %[[mp]], 0 |
| 524 | // CHECK: select i1 %[[cmp]], i32 %[[mp]], i32 -1 |
| 525 | // CHECK: } |
| 526 | } |
| 527 | |
| 528 | } |