[clang] 6385c1d - [clang] Add experimental option to omit the RTTI component from the vtable when -fno-rtti is used

Leonard Chan via cfe-commits cfe-commits at lists.llvm.org
Wed Sep 13 16:18:16 PDT 2023


Author: Leonard Chan
Date: 2023-09-13T23:15:50Z
New Revision: 6385c1df919f237d4149fabf542a158f61010bf8

URL: https://github.com/llvm/llvm-project/commit/6385c1df919f237d4149fabf542a158f61010bf8
DIFF: https://github.com/llvm/llvm-project/commit/6385c1df919f237d4149fabf542a158f61010bf8.diff

LOG: [clang] Add experimental option to omit the RTTI component from the vtable when -fno-rtti is used

For programs that don't use RTTI, the rtti component is just replaced with a
zero. This way, vtables that don't use RTTI can still cooperate with vtables
that use RTTI since offset calculations on the ABI level would still work.
However, if throughout your whole program you don't use RTTI at all (such as
the embedded case), then this is just an unused pointer-sized component that's
wasting space. This adds an experimental option for removing the RTTI component
from the vtable.

Some notes:
- This is only allowed when RTTI is disabled, so we don't have to worry about
  things like `typeid` or `dynamic_cast`.
- This is a "use at your own risk" since, similar to relative vtables, everything
  must be compiled with this since it's an ABI breakage. That is, a program compiled
  with this is not guaranteed to work with a program compiled without this, even
  if RTTI is disabled for both programs.

Note that this is a completely different ABI flavor orthogonal to the
relative-vtables ABI. That is, they can be enabled/disabled independently.

Differential Revision: https://reviews.llvm.org/D152405

Added: 
    clang/test/CodeGenCXX/OmitRTTIComponentABI/simple-vtable-definition.cpp
    clang/test/CodeGenCXX/OmitRTTIComponentABI/vbase-offset.cpp
    clang/test/CodeGenCXX/OmitRTTIComponentABI/vtable-layout.cpp
    clang/test/Driver/omit-rtti-component-flag.cpp
    clang/test/Driver/omit-rtti-component-without-no-rtti.cpp

Modified: 
    clang/include/clang/Basic/DiagnosticDriverKinds.td
    clang/include/clang/Basic/LangOptions.def
    clang/include/clang/Driver/Options.td
    clang/lib/AST/VTableBuilder.cpp
    clang/lib/Driver/ToolChains/Clang.cpp
    clang/lib/Frontend/CompilerInvocation.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index 86567267cfb437a..9349ff85ca8a1d3 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -674,6 +674,9 @@ def err_cc1_round_trip_mismatch : Error<
 def err_cc1_unbounded_vscale_min : Error<
   "minimum vscale must be an unsigned integer greater than 0">;
 
+def err_drv_using_omit_rtti_component_without_no_rtti : Error<
+  "-fexperimental-omit-vtable-rtti call only be used with -fno-rtti">;
+
 def err_drv_ssp_missing_offset_argument : Error<
   "'%0' is used without '-mstack-protector-guard-offset', and there is no default">;
 

diff  --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 102209ce899d70c..e18b5b80a34e718 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -450,6 +450,9 @@ LANGOPT(SpeculativeLoadHardening, 1, 0, "Speculative load hardening enabled")
 LANGOPT(RelativeCXXABIVTables, 1, 0,
         "Use an ABI-incompatible v-table layout that uses relative references")
 
+LANGOPT(OmitVTableRTTI, 1, 0,
+        "Use an ABI-incompatible v-table layout that omits the RTTI component")
+
 LANGOPT(VScaleMin, 32, 0, "Minimum vscale value")
 LANGOPT(VScaleMax, 32, 0, "Maximum vscale value")
 

diff  --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 2d42d05859bc1d3..553c7928c4f949e 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2671,6 +2671,12 @@ def fno_experimental_relative_cxx_abi_vtables :
   Group<f_clang_Group>, Visibility<[ClangOption, CC1Option]>,
   HelpText<"Do not use the experimental C++ class ABI for classes with virtual tables">;
 
+defm experimental_omit_vtable_rtti : BoolFOption<"experimental-omit-vtable-rtti",
+  LangOpts<"OmitVTableRTTI">, DefaultFalse,
+  PosFlag<SetTrue, [], [CC1Option], "Omit">,
+  NegFlag<SetFalse, [], [CC1Option], "Do not omit">,
+  BothFlags<[], [CC1Option], " the RTTI component from virtual tables">>;
+
 def fcxx_abi_EQ : Joined<["-"], "fc++-abi=">,
                   Group<f_clang_Group>, Visibility<[ClangOption, CC1Option]>,
                   HelpText<"C++ ABI to use. This will override the target C++ ABI.">;

diff  --git a/clang/lib/AST/VTableBuilder.cpp b/clang/lib/AST/VTableBuilder.cpp
index a587f9bdc758529..cce0a507e8077d8 100644
--- a/clang/lib/AST/VTableBuilder.cpp
+++ b/clang/lib/AST/VTableBuilder.cpp
@@ -665,7 +665,11 @@ CharUnits VCallAndVBaseOffsetBuilder::getCurrentOffsetOffset() const {
   // vtable address point. (We subtract 3 to account for the information just
   // above the address point, the RTTI info, the offset to top, and the
   // vcall offset itself).
-  int64_t OffsetIndex = -(int64_t)(3 + Components.size());
+  size_t NumComponentsAboveAddrPoint = 3;
+  if (Context.getLangOpts().OmitVTableRTTI)
+    NumComponentsAboveAddrPoint--;
+  int64_t OffsetIndex =
+      -(int64_t)(NumComponentsAboveAddrPoint + Components.size());
 
   // Under the relative ABI, the offset widths are 32-bit ints instead of
   // pointer widths.
@@ -1669,7 +1673,8 @@ void ItaniumVTableBuilder::LayoutPrimaryAndSecondaryVTables(
   Components.push_back(VTableComponent::MakeOffsetToTop(OffsetToTop));
 
   // Next, add the RTTI.
-  Components.push_back(VTableComponent::MakeRTTI(MostDerivedClass));
+  if (!Context.getLangOpts().OmitVTableRTTI)
+    Components.push_back(VTableComponent::MakeRTTI(MostDerivedClass));
 
   uint64_t AddressPoint = Components.size();
 

diff  --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 531677e92f73252..40e60585a8b8d6e 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5526,6 +5526,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
   Args.AddLastArg(CmdArgs, options::OPT_fexperimental_relative_cxx_abi_vtables,
                   options::OPT_fno_experimental_relative_cxx_abi_vtables);
 
+  Args.AddLastArg(CmdArgs, options::OPT_fexperimental_omit_vtable_rtti,
+                  options::OPT_fno_experimental_omit_vtable_rtti);
+
   // Handle segmented stacks.
   Args.addOptInFlag(CmdArgs, options::OPT_fsplit_stack,
                     options::OPT_fno_split_stack);
@@ -6007,6 +6010,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
   Args.AddLastArg(CmdArgs, options::OPT_fexperimental_relative_cxx_abi_vtables,
                   options::OPT_fno_experimental_relative_cxx_abi_vtables);
 
+  Args.AddLastArg(CmdArgs, options::OPT_fexperimental_omit_vtable_rtti,
+                  options::OPT_fno_experimental_omit_vtable_rtti);
+
   if (Arg *A = Args.getLastArg(options::OPT_ffuchsia_api_level_EQ))
     A->render(Args, CmdArgs);
 

diff  --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 730db8e394f66f1..2dd299b5d10322d 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -4109,6 +4109,14 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args,
                    options::OPT_fno_experimental_relative_cxx_abi_vtables,
                    TargetCXXABI::usesRelativeVTables(T));
 
+  // RTTI is on by default.
+  bool HasRTTI = Args.hasFlag(options::OPT_frtti, options::OPT_fno_rtti, true);
+  Opts.OmitVTableRTTI =
+      Args.hasFlag(options::OPT_fexperimental_omit_vtable_rtti,
+                   options::OPT_fno_experimental_omit_vtable_rtti, false);
+  if (Opts.OmitVTableRTTI && HasRTTI)
+    Diags.Report(diag::err_drv_using_omit_rtti_component_without_no_rtti);
+
   for (const auto &A : Args.getAllArgValues(OPT_fmacro_prefix_map_EQ)) {
     auto Split = StringRef(A).split('=');
     Opts.MacroPrefixMap.insert(

diff  --git a/clang/test/CodeGenCXX/OmitRTTIComponentABI/simple-vtable-definition.cpp b/clang/test/CodeGenCXX/OmitRTTIComponentABI/simple-vtable-definition.cpp
new file mode 100644
index 000000000000000..99395ba0e05ec0b
--- /dev/null
+++ b/clang/test/CodeGenCXX/OmitRTTIComponentABI/simple-vtable-definition.cpp
@@ -0,0 +1,32 @@
+/// Check that -fexperimental-omit-vtable-rtti omits the RTTI component from
+/// the vtable.
+
+// RUN: %clang_cc1 %s -triple=aarch64-unknown-linux-gnu -fno-rtti -fexperimental-omit-vtable-rtti -S -o - -emit-llvm | FileCheck -check-prefixes=POINTER,RTTI %s
+// RUN: %clang_cc1 %s -triple=aarch64-unknown-linux-gnu -fexperimental-relative-c++-abi-vtables -fno-rtti -fexperimental-omit-vtable-rtti -S -o - -emit-llvm | FileCheck -check-prefixes=RELATIVE,RTTI %s
+
+/// Normally, the vtable would contain at least three components:
+/// - An offset to top
+/// - A pointer to the RTTI struct
+/// - A virtual function
+///
+/// Now vtables should have just two components.
+// POINTER: @_ZTV1A = unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr null, ptr @_ZN1A3fooEv] }, align 8
+// RELATIVE: @_ZTV1A.local = private unnamed_addr constant { [2 x i32] } { [2 x i32] [i32 0, i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @_ZN1A3fooEv to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [2 x i32] }, ptr @_ZTV1A.local, i32 0, i32 0, i32 1) to i64)) to i32)] }, align 4
+// RELATIVE: @_ZTV1A = unnamed_addr alias { [2 x i32] }, ptr @_ZTV1A.local
+
+/// None of these supplementary symbols should be emitted with -fno-rtti, but
+/// as a sanity check lets make sure they're not emitted also.
+// RTTI-NOT: @_ZTVN10__cxxabiv117__class_type_infoE
+// RTTI-NOT: @_ZTS1A
+// RTTI-NOT: @_ZTI1A
+
+class A {
+public:
+  virtual void foo();
+};
+
+void A::foo() {}
+
+void A_foo(A *a) {
+  a->foo();
+}

diff  --git a/clang/test/CodeGenCXX/OmitRTTIComponentABI/vbase-offset.cpp b/clang/test/CodeGenCXX/OmitRTTIComponentABI/vbase-offset.cpp
new file mode 100644
index 000000000000000..d490cc2dbebe52e
--- /dev/null
+++ b/clang/test/CodeGenCXX/OmitRTTIComponentABI/vbase-offset.cpp
@@ -0,0 +1,51 @@
+/// Check that the offset to top calculation is adjusted to account for the
+/// omitted RTTI entry.
+
+// RUN: %clang_cc1 %s -triple=aarch64-unknown-linux-gnu -fexperimental-omit-vtable-rtti -fno-rtti -S -o - -emit-llvm | FileCheck -check-prefixes=POINTER %s
+// RUN: %clang_cc1 %s -triple=aarch64-unknown-linux-gnu -fexperimental-relative-c++-abi-vtables -fexperimental-omit-vtable-rtti -fno-rtti -S -o - -emit-llvm | FileCheck -check-prefixes=RELATIVE %s
+
+/// Some important things to check:
+/// - The n16 here represents the virtual thunk size. Normally this would be 24
+///   to represent 3 components (offset to top, RTTI component, vcall offset),
+///   but since one 8-byte component is removed, this is now 16.
+// POINTER-LABEL: @_ZTv0_n16_N7Derived1fEi(
+// POINTER-NEXT:  entry:
+// POINTER:        [[vtable:%.+]] = load ptr, ptr %this1, align 8
+
+/// Same here - When getting the vbase offset, we subtract 2 pointer sizes
+/// instead of 3.
+// POINTER-NEXT:   [[vbase_offset_ptr:%.+]] = getelementptr inbounds i8, ptr [[vtable]], i64 -16
+// POINTER-NEXT:   [[vbase_offset:%.+]] = load i64, ptr [[vbase_offset_ptr]], align 8
+// POINTER-NEXT:   [[adj_this:%.+]] = getelementptr inbounds i8, ptr %this1, i64 [[vbase_offset]]
+// POINTER:   [[call:%.+]] = tail call noundef i32 @_ZN7Derived1fEi(ptr noundef{{[^,]*}} [[adj_this]], i32 noundef {{.*}})
+// POINTER:   ret i32 [[call]]
+
+/// For relative vtables, it's almost the same except the offset sizes are
+/// halved.
+// RELATIVE-LABEL: @_ZTv0_n8_N7Derived1fEi(
+// RELATIVE-NEXT:  entry:
+// RELATIVE:        [[vtable:%.+]] = load ptr, ptr %this1, align 8
+// RELATIVE-NEXT:   [[vbase_offset_ptr:%.+]] = getelementptr inbounds i8, ptr [[vtable]], i64 -8
+// RELATIVE-NEXT:   [[vbase_offset:%.+]] = load i32, ptr [[vbase_offset_ptr]], align 4
+// RELATIVE-NEXT:   [[adj_this:%.+]] = getelementptr inbounds i8, ptr %this1, i32 [[vbase_offset]]
+// RELATIVE:        [[call:%.+]] = tail call noundef i32 @_ZN7Derived1fEi(ptr noundef{{[^,]*}} [[adj_this]], i32 noundef {{.*}})
+// RELATIVE:        ret i32 [[call]]
+
+class Base {
+public:
+  virtual int f(int x);
+
+private:
+  long x;
+};
+
+class Derived : public virtual Base {
+public:
+  virtual int f(int x);
+
+private:
+  long y;
+};
+
+int Base::f(int x) { return x + 1; }
+int Derived::f(int x) { return x + 2; }

diff  --git a/clang/test/CodeGenCXX/OmitRTTIComponentABI/vtable-layout.cpp b/clang/test/CodeGenCXX/OmitRTTIComponentABI/vtable-layout.cpp
new file mode 100644
index 000000000000000..bcc9264f5e5b84f
--- /dev/null
+++ b/clang/test/CodeGenCXX/OmitRTTIComponentABI/vtable-layout.cpp
@@ -0,0 +1,19 @@
+/// Ensure -fdump-vtable-layout omits the rtti component when passed -fexperimental-omit-vtable-rtti.
+
+// RUN: %clang_cc1 %s -triple=aarch64-unknown-linux-gnu -fno-rtti -fexperimental-omit-vtable-rtti -emit-llvm-only -fdump-vtable-layouts | FileCheck %s
+
+// CHECK:      Vtable for 'A' (2 entries).
+// CHECK-NEXT:    0 | offset_to_top (0)
+// CHECK-NEXT:        -- (A, 0) vtable address --
+// CHECK-NEXT:    1 | void A::foo()
+
+class A {
+public:
+  virtual void foo();
+};
+
+void A::foo() {}
+
+void A_foo(A *a) {
+  a->foo();
+}

diff  --git a/clang/test/Driver/omit-rtti-component-flag.cpp b/clang/test/Driver/omit-rtti-component-flag.cpp
new file mode 100644
index 000000000000000..54b88a8775ef3d2
--- /dev/null
+++ b/clang/test/Driver/omit-rtti-component-flag.cpp
@@ -0,0 +1,5 @@
+// RUN: %clangxx --target=aarch64-unknown-linux -fno-rtti -Xclang -fexperimental-omit-vtable-rtti -c %s -### 2>&1 | FileCheck %s --check-prefix=OMIT
+// RUN: %clangxx --target=aarch64-unknown-linux -fno-rtti -Xclang -fno-experimental-omit-vtable-rtti -c %s -### 2>&1 | FileCheck %s --check-prefix=NO-OMIT
+
+// OMIT: "-fexperimental-omit-vtable-rtti"
+// NO-OMIT-NOT: "-fexperimental-omit-vtable-rtti"

diff  --git a/clang/test/Driver/omit-rtti-component-without-no-rtti.cpp b/clang/test/Driver/omit-rtti-component-without-no-rtti.cpp
new file mode 100644
index 000000000000000..031094f91b3afd0
--- /dev/null
+++ b/clang/test/Driver/omit-rtti-component-without-no-rtti.cpp
@@ -0,0 +1,13 @@
+/// Ensure that -fexperimental-omit-vtable-rtti is only allowed if rtti is
+/// disabled.
+
+// RUN: not %clang -c -Xclang -fexperimental-omit-vtable-rtti %s 2>&1 | FileCheck -check-prefix=ERROR %s
+// RUN: not %clang -c -Xclang -fexperimental-omit-vtable-rtti -frtti %s 2>&1 | FileCheck -check-prefix=ERROR %s
+// RUN: not %clang -c -Xclang -fexperimental-omit-vtable-rtti -fno-rtti -frtti %s 2>&1 | FileCheck -check-prefix=ERROR %s
+
+// RUN: %clang -c -Xclang -fexperimental-omit-vtable-rtti -fno-rtti %s 2>&1 | FileCheck -check-prefix=NO-ERROR %s --allow-empty
+// RUN: %clang -c -Xclang -fno-experimental-omit-vtable-rtti -frtti %s 2>&1 | FileCheck -check-prefix=NO-ERROR %s --allow-empty
+// RUN: %clang -c -Xclang -fexperimental-omit-vtable-rtti -Xclang -fno-experimental-omit-vtable-rtti -frtti %s 2>&1 | FileCheck -check-prefix=NO-ERROR %s --allow-empty
+
+// ERROR: -fexperimental-omit-vtable-rtti call only be used with -fno-rtti
+// NO-ERROR-NOT: -fexperimental-omit-vtable-rtti call only be used with -fno-rtti


        


More information about the cfe-commits mailing list