[PATCH] D22296: CodeGen: Attach !splitpoint metadata to vtables under Itanium ABI.

Peter Collingbourne via cfe-commits cfe-commits at lists.llvm.org
Tue Jul 12 19:47:34 PDT 2016


pcc created this revision.
pcc added reviewers: rsmith, eugenis.
pcc added subscribers: cfe-commits, krasin.
pcc added a dependency: D22295: Introduce !splitpoint metadata and GlobalSplit pass..

This metadata can be used to annotate global variables with split points.
Each split point specifies a byte offset within the global, and acts as a
promise that no pointer derived from a constant-offset reference to the global
will cross the boundary of a split point. The metadata allows the optimizer
to split the virtual table at split points if possible and beneficial.

A virtual table split is possible if it has local linkage. This guarantees
that nothing outside of the module can reference the virtual table group
directly. Typically, when LTO is used, the linker is able to internalize
most virtual tables.

A split is beneficial if either of the whole-program devirtualization or
control flow integrity features are being used. In the former case, under
virtual constant propagation we are able to place propagated constants
directly in front of virtual tables of classes with multiple bases. In the
latter case, we can arrange virtual tables with multiple bases in a more
hierarchical order, which reduces the required amount of runtime data and
simplifies the required checks. In one recent experiment, enabling vtable
splitting reduced code size overhead of CFI in Chromium from 5% to 3.5%.

Under the Itanium C++ ABI, Clang attaches split points at each boundary
between virtual tables in a virtual table group, because the virtual tables
are independent entities as far as a user of a virtual table within an object
is concerned, provided that the dynamic type is unknown. A compiler cannot
legally generate code to adjust a virtual table pointer to point to another
virtual table in the virtual table group, as the primary virtual table will
have an unknown number of virtual functions.

Although a compiler could in principle adjust a virtual table pointer
stored in an object to another virtual table in the virtual table group if
the dynamic type is known, there is probably no benefit in doing so as the
identity of the virtual function would also be known, so the compiler could
just emit a direct call to the virtual function. According to an experiment
carried out at godbolt.org [1], all major compilers (gcc, clang and icc)
will either compile such code to a direct call or call a virtual function
pointer loaded from the virtual table pointer stored in the correct base.

An amendment to the Itanium ABI requiring that a conforming program may not
adjust a virtual table pointer loaded from an object to another virtual table
in the same virtual table group would seem to be all that would be required
to guarantee that this scheme will be ABI compatible with future compilers,
and I'd be happy to drive such an amendment.

[1] https://godbolt.org/g/7eG8A1

Depends on D22295

http://reviews.llvm.org/D22296

Files:
  lib/CodeGen/CGVTables.cpp
  test/CodeGenCXX/splitpoint.cpp

Index: test/CodeGenCXX/splitpoint.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/splitpoint.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -flto -triple x86_64-unknown-linux -fvisibility hidden -emit-llvm -o - %s | FileCheck %s
+
+// CHECK: @_ZTV1A =
+// CHECK-NOT: splitpoint
+struct A {
+  virtual void f();
+};
+
+// CHECK: @_ZTV1B =
+// CHECK-NOT: splitpoint
+struct B {
+  virtual void g();
+};
+
+// CHECK: @_ZTV1C = {{.*}}, !splitpoint [[SP:![0-9]+]]
+// CHECK: [[SP]] = !{i64 24}
+struct C : A, B {
+  virtual void f();
+};
+
+void A::f() {}
+void B::g() {}
+void C::f() {}
Index: lib/CodeGen/CGVTables.cpp
===================================================================
--- lib/CodeGen/CGVTables.cpp
+++ lib/CodeGen/CGVTables.cpp
@@ -945,6 +945,25 @@
   CharUnits PointerWidth =
       Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0));
 
+  // Create split points. In the Itanium ABI, vtable groups may be split at
+  // boundaries between vtables in that group, which occurs at the transition
+  // from a non-function pointer to a function pointer.
+  uint64_t Offset = 0;
+  bool LastWasFunctionPointer = false;
+  for (const VTableComponent &Comp : VTLayout.vtable_components()) {
+    bool IsFunctionPointer = Comp.isFunctionPointerKind();
+    if (LastWasFunctionPointer && !IsFunctionPointer)
+      VTable->addMetadata(
+          llvm::LLVMContext::MD_splitpoint,
+          *llvm::MDTuple::get(
+              getLLVMContext(),
+              {llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
+                  Int64Ty, Offset * PointerWidth.getQuantity()))}));
+
+    LastWasFunctionPointer = IsFunctionPointer;
+    ++Offset;
+  }
+
   typedef std::pair<const CXXRecordDecl *, unsigned> BSEntry;
   std::vector<BSEntry> BitsetEntries;
   // Create a bit set entry for each address point.


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D22296.63774.patch
Type: text/x-patch
Size: 1895 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20160713/9f55c479/attachment.bin>


More information about the cfe-commits mailing list