summaryrefslogtreecommitdiffstats
path: root/clang/test
diff options
context:
space:
mode:
authorReid Kleckner <rnk@google.com>2018-04-02 20:20:33 +0000
committerReid Kleckner <rnk@google.com>2018-04-02 20:20:33 +0000
commit399d96e39c64f1fd3a00b78886a02abe58cdb36f (patch)
tree94d633dd9040a425b97ee0b13625c66d03afd792 /clang/test
parent03fef3a42c226e99a288dd289f514bc7aa9139ac (diff)
downloadbcm5719-llvm-399d96e39c64f1fd3a00b78886a02abe58cdb36f.tar.gz
bcm5719-llvm-399d96e39c64f1fd3a00b78886a02abe58cdb36f.zip
[MS] Emit vftable thunks for functions with incomplete prototypes
Summary: The following class hierarchy requires that we be able to emit a this-adjusting thunk for B::foo in C's vftable: struct Incomplete; struct A { virtual A* foo(Incomplete p) = 0; }; struct B : virtual A { void foo(Incomplete p) override; }; struct C : B { int c; }; This TU is valid, but lacks a definition of 'Incomplete', which makes it hard to build a thunk for the final overrider, B::foo. Before this change, Clang gives up attempting to emit the thunk, because it assumes that if the parameter types are incomplete, it must be emitting the thunk for optimization purposes. This is untrue for the MS ABI, where the implementation of B::foo has no idea what thunks C's vftable may require. Clang needs to emit the thunk without necessarily having access to the complete prototype of foo. This change makes Clang emit a musttail variadic call when it needs such a thunk. I call these "unprototyped" thunks, because they only prototype the "this" parameter, which must always come first in the MS C++ ABI. These thunks work, but they create ugly LLVM IR. If the call to the thunk is devirtualized, it will be a call to a bitcast of a function pointer. Today, LLVM cannot inline through such a call, but I want to address that soon, because we also use this pattern for virtual member pointer thunks. This change also implements an old FIXME in the code about reusing the thunk's computed CGFunctionInfo as much as possible. Now we don't end up computing the thunk's mangled name and arranging it's prototype up to around three times. Fixes PR25641 Reviewers: rjmccall, rsmith, hans Subscribers: Prazek, cfe-commits Differential Revision: https://reviews.llvm.org/D45112 llvm-svn: 329009
Diffstat (limited to 'clang/test')
-rw-r--r--clang/test/CodeGenCXX/ms-thunks-unprototyped-return.cpp15
-rw-r--r--clang/test/CodeGenCXX/ms-thunks-unprototyped.cpp56
2 files changed, 71 insertions, 0 deletions
diff --git a/clang/test/CodeGenCXX/ms-thunks-unprototyped-return.cpp b/clang/test/CodeGenCXX/ms-thunks-unprototyped-return.cpp
new file mode 100644
index 00000000000..ef39fd70607
--- /dev/null
+++ b/clang/test/CodeGenCXX/ms-thunks-unprototyped-return.cpp
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -fno-rtti-data -triple x86_64-windows-msvc -emit-llvm-only %s -verify
+
+// Verify that we error out on this return adjusting thunk that we can't emit.
+
+struct Incomplete;
+
+struct A {
+ virtual A * clone(Incomplete p) = 0;
+};
+struct B : virtual A {
+ // expected-error@+1 2 {{cannot compile this return-adjusting thunk with incomplete parameter type yet}}
+ B * clone(Incomplete p) override;
+};
+struct C : B { int c; };
+C c;
diff --git a/clang/test/CodeGenCXX/ms-thunks-unprototyped.cpp b/clang/test/CodeGenCXX/ms-thunks-unprototyped.cpp
new file mode 100644
index 00000000000..b2ca5cb0d13
--- /dev/null
+++ b/clang/test/CodeGenCXX/ms-thunks-unprototyped.cpp
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 -fno-rtti-data -triple x86_64-windows-msvc -emit-llvm %s -o - | FileCheck %s
+
+// In this example, C does not override B::foo, but it needs to emit a thunk to
+// adjust for the relative difference of position between A-in-B and A-in-C.
+
+struct Incomplete;
+template <typename T>
+struct DoNotInstantiate {
+ typename T::does_not_exist field;
+};
+template <typename T>
+struct InstantiateLater;
+
+struct A {
+ virtual void foo(Incomplete p) = 0;
+ virtual void bar(DoNotInstantiate<int> p) = 0;
+ virtual int baz(InstantiateLater<int> p) = 0;
+};
+struct B : virtual A {
+ void foo(Incomplete p) override;
+ void bar(DoNotInstantiate<int> p) override;
+ inline int baz(InstantiateLater<int> p) override;
+};
+struct C : B { int c; };
+C c;
+
+// CHECK: @"??_7C@@6B@" = linkonce_odr unnamed_addr constant
+// CHECK-SAME: void (%struct.B*, ...)* @"?foo@B@@W7EAAXUIncomplete@@@Z"
+// CHECK-SAME: void (%struct.B*, ...)* @"?bar@B@@W7EAAXU?$DoNotInstantiate@H@@@Z"
+// CHECK-SAME: i32 (i8*, i32)* @"?baz@B@@W7EAAHU?$InstantiateLater@H@@@Z"
+
+// The thunks should have a -8 adjustment.
+
+// CHECK-LABEL: define linkonce_odr dso_local void @"?foo@B@@W7EAAXUIncomplete@@@Z"(%struct.B* %this, ...)
+// CHECK: %[[THIS_ADJ_i8:[^ ]*]] = getelementptr i8, i8* {{.*}}, i32 -8
+// CHECK: %[[THIS_ADJ:[^ ]*]] = bitcast i8* %[[THIS_ADJ_i8]] to %struct.B*
+// CHECK: musttail call void (%struct.B*, ...) {{.*}}@"?foo@B@@UEAAXUIncomplete@@@Z"
+// CHECK-SAME: (%struct.B* %[[THIS_ADJ]], ...)
+// CHECK-NEXT: ret void
+
+// CHECK-LABEL: define linkonce_odr dso_local void @"?bar@B@@W7EAAXU?$DoNotInstantiate@H@@@Z"(%struct.B* %this, ...)
+// CHECK: %[[THIS_ADJ_i8:[^ ]*]] = getelementptr i8, i8* {{.*}}, i32 -8
+// CHECK: %[[THIS_ADJ:[^ ]*]] = bitcast i8* %[[THIS_ADJ_i8]] to %struct.B*
+// CHECK: musttail call void (%struct.B*, ...) {{.*}}@"?bar@B@@UEAAXU?$DoNotInstantiate@H@@@Z"
+// CHECK-SAME: (%struct.B* %[[THIS_ADJ]], ...)
+// CHECK-NEXT: ret void
+
+// If we complete the definition later, things work out.
+template <typename T> struct InstantiateLater { T x; };
+inline int B::baz(InstantiateLater<int> p) { return p.x; }
+
+// CHECK-LABEL: define linkonce_odr dso_local i32 @"?baz@B@@W7EAAHU?$InstantiateLater@H@@@Z"(i8* %this.coerce, i32 %p.coerce)
+// CHECK: = getelementptr i8, i8* {{.*}}, i32 -8
+// CHECK: tail call i32 @"?baz@B@@UEAAHU?$InstantiateLater@H@@@Z"(i8* {{[^,]*}}, i32 {{.*}})
+
+// CHECK-LABEL: define linkonce_odr dso_local i32 @"?baz@B@@UEAAHU?$InstantiateLater@H@@@Z"(i8* %this.coerce, i32 %p.coerce)
OpenPOWER on IntegriCloud