| 1 | // RUN: %clang_cc1 %s -triple=x86_64-pc-windows-msvc -debug-info-kind=limited -S -emit-llvm -o - | FileCheck %s |
| 2 | // RUN: %clang_cc1 %s -triple %itanium_abi_triple -debug-info-kind=limited -S -emit-llvm -o - | FileCheck %s -check-prefix=ITANIUM |
| 3 | // |
| 4 | // Validate we emit a "DIFlagThunk" flag on DISubprogram entries for thunks. |
| 5 | // This flag is used for emitting S_THUNK32 symbols for CodeView debugging. |
| 6 | // |
| 7 | // NOTE: |
| 8 | // Because thunks are compiler generated and don't exist in the source, this |
| 9 | // test is dependent upon the linkage name to identify the thunk. Any changes |
| 10 | // in the name mangling may require this test to be updated. |
| 11 | // |
| 12 | // NOTE: |
| 13 | // The FileCheck directives below use CHECK-DAG because the thunks may not be |
| 14 | // emitted in source order. |
| 15 | // |
| 16 | |
| 17 | namespace Test1 { |
| 18 | struct A { |
| 19 | virtual void f(); |
| 20 | }; |
| 21 | |
| 22 | struct B { |
| 23 | virtual void f(); |
| 24 | }; |
| 25 | |
| 26 | struct C : A, B { |
| 27 | virtual void c(); |
| 28 | |
| 29 | virtual void f(); |
| 30 | }; |
| 31 | |
| 32 | // CHECK-DAG: DISubprogram{{.*}}linkageName: "?f@C@Test1@@W7EAAXXZ"{{.*}} flags: {{.*}}DIFlagThunk |
| 33 | void C::f() { } |
| 34 | } |
| 35 | |
| 36 | namespace Test2 { |
| 37 | struct V1 { }; |
| 38 | struct V2 : virtual V1 { }; |
| 39 | |
| 40 | struct A { |
| 41 | virtual V1 *f(); |
| 42 | }; |
| 43 | |
| 44 | struct B : A { |
| 45 | virtual void b(); |
| 46 | |
| 47 | virtual V2 *f(); |
| 48 | }; |
| 49 | |
| 50 | // CHECK-DAG: DISubprogram{{.*}}linkageName: "?f@B@Test2@@QEAAPEAUV1@2@XZ"{{.*}} flags: {{.*}}DIFlagThunk |
| 51 | V2 *B::f() { return 0; } |
| 52 | } |
| 53 | |
| 54 | namespace Test3 { |
| 55 | struct A { |
| 56 | virtual void f(); |
| 57 | }; |
| 58 | |
| 59 | struct B { |
| 60 | virtual void f(); |
| 61 | }; |
| 62 | |
| 63 | struct __attribute__((visibility("protected"))) C : A, B { |
| 64 | virtual void c(); |
| 65 | |
| 66 | virtual void f(); |
| 67 | }; |
| 68 | |
| 69 | // CHECK-DAG: DISubprogram{{.*}}linkageName: "?f@C@Test3@@W7EAAXXZ"{{.*}} flags: {{.*}}DIFlagThunk |
| 70 | void C::f() { } |
| 71 | } |
| 72 | |
| 73 | namespace Test4 { |
| 74 | struct A { |
| 75 | virtual void f(); |
| 76 | }; |
| 77 | |
| 78 | struct B { |
| 79 | virtual void f(); |
| 80 | }; |
| 81 | |
| 82 | namespace { |
| 83 | struct C : A, B { |
| 84 | virtual void c(); |
| 85 | virtual void f(); |
| 86 | }; |
| 87 | } |
| 88 | void C::c() {} |
| 89 | // CHECK-DAG: DISubprogram{{.*}}linkageName: "?f@C@?A0x{{[^@]*}}@Test4@@W7EAAXXZ"{{.*}} flags: {{.*}}DIFlagThunk |
| 90 | void C::f() {} |
| 91 | |
| 92 | // Force C::f to be used. |
| 93 | void f() { |
| 94 | C c; |
| 95 | c.f(); |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | namespace Test5 { |
| 100 | struct X { |
| 101 | X(); |
| 102 | X(const X&); |
| 103 | X &operator=(const X&); |
| 104 | ~X(); |
| 105 | }; |
| 106 | |
| 107 | struct P { |
| 108 | P(); |
| 109 | P(const P&); |
| 110 | ~P(); |
| 111 | X first; |
| 112 | X second; |
| 113 | }; |
| 114 | |
| 115 | P getP(); |
| 116 | |
| 117 | struct Base1 { |
| 118 | int i; |
| 119 | |
| 120 | virtual X f() { return X(); } |
| 121 | }; |
| 122 | |
| 123 | struct Base2 { |
| 124 | float real; |
| 125 | |
| 126 | virtual X f() { return X(); } |
| 127 | }; |
| 128 | |
| 129 | struct Thunks : Base1, Base2 { |
| 130 | long l; |
| 131 | |
| 132 | virtual X f(); |
| 133 | }; |
| 134 | |
| 135 | // CHECK-DAG: DISubprogram{{.*}}linkageName: "?f@Thunks@Test5@@WBA@EAA?AUX@2@XZ"{{.*}} flags: {{.*}}DIFlagThunk |
| 136 | X Thunks::f() { return X(); } |
| 137 | } |
| 138 | |
| 139 | namespace Test6 { |
| 140 | struct X { |
| 141 | X(); |
| 142 | X(const X&); |
| 143 | X &operator=(const X&); |
| 144 | ~X(); |
| 145 | }; |
| 146 | |
| 147 | struct Small { short s; }; |
| 148 | struct Large { |
| 149 | char array[1024]; |
| 150 | }; |
| 151 | |
| 152 | class A { |
| 153 | protected: |
| 154 | virtual void foo() = 0; |
| 155 | }; |
| 156 | |
| 157 | class B : public A { |
| 158 | protected: |
| 159 | virtual void bar() = 0; |
| 160 | }; |
| 161 | |
| 162 | class C : public A { |
| 163 | protected: |
| 164 | virtual void baz(X, X&, _Complex float, Small, Small&, Large) = 0; |
| 165 | }; |
| 166 | |
| 167 | class D : public B, |
| 168 | public C { |
| 169 | // CHECK-DAG: DISubprogram{{.*}}linkageName: "?foo@D@Test6@@G7EAAXXZ"{{.*}} flags: {{.*}}DIFlagThunk |
| 170 | void foo() {} |
| 171 | void bar() {} |
| 172 | void baz(X, X&, _Complex float, Small, Small&, Large); |
| 173 | }; |
| 174 | |
| 175 | void D::baz(X, X&, _Complex float, Small, Small&, Large) { } |
| 176 | |
| 177 | void testD() { D d; } |
| 178 | } |
| 179 | |
| 180 | namespace Test7 { |
| 181 | struct A { virtual void foo(); }; |
| 182 | struct B { virtual void foo(); }; |
| 183 | // CHECK-DAG: DISubprogram{{.*}}linkageName: "?foo@C@Test7@@W7EAAXXZ"{{.*}} flags: {{.*}}DIFlagThunk |
| 184 | struct C : A, B { void foo() {} }; |
| 185 | |
| 186 | // Test later. |
| 187 | void test() { |
| 188 | C c; |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | namespace Test8 { |
| 193 | struct A { virtual A* f(); }; |
| 194 | struct B : virtual A { virtual A* f(); }; |
| 195 | struct C : B { virtual C* f(); }; |
| 196 | // CHECK-DAG: DISubprogram{{.*}}linkageName: "?f@C@Test8@@QEAAPEAUA@2@XZ"{{.*}} flags: {{.*}}DIFlagThunk |
| 197 | C* C::f() { return 0; } |
| 198 | } |
| 199 | |
| 200 | namespace Test9 { |
| 201 | struct B1 { |
| 202 | virtual B1 &foo1(); |
| 203 | }; |
| 204 | struct Pad1 { |
| 205 | virtual ~Pad1(); |
| 206 | }; |
| 207 | struct Proxy1 : Pad1, B1 { |
| 208 | virtual ~Proxy1(); |
| 209 | }; |
| 210 | struct D : virtual Proxy1 { |
| 211 | virtual ~D(); |
| 212 | virtual D &foo1(); |
| 213 | }; |
| 214 | // CHECK-DAG: DISubprogram{{.*}}linkageName: "?foo1@D@Test9@@$4PPPPPPPE@A@EAAAEAUB1@2@XZ"{{.*}} flags: {{.*}}DIFlagThunk |
| 215 | // CHECK-DAG: DISubprogram{{.*}}linkageName: "?foo1@D@Test9@@$4PPPPPPPE@A@EAAAEAU12@XZ"{{.*}} flags: {{.*}}DIFlagThunk |
| 216 | D& D::foo1() { |
| 217 | return *this; |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | namespace Test10 { |
| 222 | class A { |
| 223 | virtual void f(); |
| 224 | }; |
| 225 | class B { |
| 226 | virtual void f(); |
| 227 | }; |
| 228 | class C : public A, public B { |
| 229 | virtual void f(); |
| 230 | }; |
| 231 | // CHECK-DAG: DISubprogram{{.*}}linkageName: "?f@C@Test10@@G7EAAXXZ"{{.*}} flags: {{.*}}DIFlagThunk |
| 232 | void C::f() { |
| 233 | } |
| 234 | } |
| 235 | |
| 236 | namespace Test11 { |
| 237 | class A { |
| 238 | public: |
| 239 | virtual void f(); |
| 240 | }; |
| 241 | |
| 242 | void test() { |
| 243 | // CHECK-DAG: DISubprogram{{.*}}linkageName: "??_9A@Test11@@$BA@AA"{{.*}} flags: {{.*}}DIFlagThunk |
| 244 | void (A::*p)() = &A::f; |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | namespace Test12 { |
| 249 | struct A { |
| 250 | virtual void f(); |
| 251 | }; |
| 252 | |
| 253 | struct B { |
| 254 | virtual void f(); |
| 255 | }; |
| 256 | |
| 257 | struct C : A, B { |
| 258 | virtual void f(); |
| 259 | }; |
| 260 | |
| 261 | void C::f() { } |
| 262 | // ITANIUM: define {{.*}}void @_ZThn{{[48]}}_N6Test121C1fEv |
| 263 | // ITANIUM-SAME: !dbg ![[SP:[0-9]+]] |
| 264 | // ITANIUM-NOT: {{ret }} |
| 265 | // ITANIUM: = load{{.*}} !dbg ![[DBG:[0-9]+]] |
| 266 | // ITANIUM-NOT: {{ret }} |
| 267 | // ITANIUM: ret void, !dbg ![[DBG]] |
| 268 | // |
| 269 | // ITANIUM: ![[SP]] = distinct !DISubprogram(linkageName: "_ZThn{{[48]}}_N6Test121C1fEv" |
| 270 | // ITANIUM-SAME: line: 261 |
| 271 | // ITANIUM-SAME: DIFlagArtificial |
| 272 | // ITANIUM-SAME: DIFlagThunk |
| 273 | // ITANIUM-SAME: DISPFlagDefinition |
| 274 | // ITANIUM-SAME: ){{$}} |
| 275 | // |
| 276 | // ITANIUM: ![[DBG]] = !DILocation(line: 0 |
| 277 | } |
| 278 | |