diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index 86567267cfb43..9349ff85ca8a1 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 102209ce899d7..e18b5b80a34e7 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 2d42d05859bc1..553c7928c4f94 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, 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, + NegFlag, + BothFlags<[], [CC1Option], " the RTTI component from virtual tables">>; + def fcxx_abi_EQ : Joined<["-"], "fc++-abi=">, 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 a587f9bdc7585..cce0a507e8077 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 531677e92f732..40e60585a8b8d 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 730db8e394f66..2dd299b5d1032 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 0000000000000..99395ba0e05ec --- /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 0000000000000..d490cc2dbebe5 --- /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 0000000000000..bcc9264f5e5b8 --- /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 0000000000000..54b88a8775ef3 --- /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 0000000000000..0c2006bcb7163 --- /dev/null +++ b/clang/test/Driver/omit-rtti-component-without-no-rtti.cpp @@ -0,0 +1,15 @@ +/// Ensure that -fexperimental-omit-vtable-rtti is only allowed if rtti is +/// disabled. + +// UNSUPPORTED: system-windows + +// 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