-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Introduce -Zvirtual-function-elimination
codegen flag
#96285
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
def3fd8
20f597f
d55787a
e1c1d0f
e96e6e2
996c6b7
a93ea7e
195f208
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -586,9 +586,21 @@ pub(crate) fn run_pass_manager( | |
// LTO-specific optimization passes that LLVM provides. | ||
// | ||
// This code is based off the code found in llvm's LTO code generator: | ||
// tools/lto/LTOCodeGenerator.cpp | ||
// llvm/lib/LTO/LTOCodeGenerator.cpp | ||
debug!("running the pass manager"); | ||
unsafe { | ||
if !llvm::LLVMRustHasModuleFlag( | ||
module.module_llvm.llmod(), | ||
"LTOPostLink".as_ptr().cast(), | ||
11, | ||
) { | ||
llvm::LLVMRustAddModuleFlag( | ||
module.module_llvm.llmod(), | ||
llvm::LLVMModFlagBehavior::Error, | ||
"LTOPostLink\0".as_ptr().cast(), | ||
1, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be 11? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe the In that context, it would be great if this code had a comment with context as to why this is being done. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the mono repo, I found the value 1. The LTOGenerator only adds the flag. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The code here is based on the file tschuett linked above. In the LLVM repo that code is responsible to set up LTO. Adding this flag was missing from the Rust code that should emulate the code in The |
||
); | ||
} | ||
if llvm_util::should_use_new_llvm_pass_manager( | ||
&config.new_llvm_pass_manager, | ||
&cgcx.target_arch, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,20 +30,21 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE}; | |
use rustc_index::vec::{Idx, IndexVec}; | ||
use rustc_middle::bug; | ||
use rustc_middle::mir::{self, GeneratorLayout}; | ||
use rustc_middle::ty::layout::LayoutOf; | ||
use rustc_middle::ty::layout::TyAndLayout; | ||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; | ||
use rustc_middle::ty::subst::GenericArgKind; | ||
use rustc_middle::ty::{self, AdtKind, Instance, ParamEnv, Ty, TyCtxt}; | ||
use rustc_session::config::{self, DebugInfo}; | ||
use rustc_middle::ty::{ | ||
self, AdtKind, Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt, Visibility, | ||
}; | ||
use rustc_session::config::{self, DebugInfo, Lto}; | ||
use rustc_span::symbol::Symbol; | ||
use rustc_span::FileName; | ||
use rustc_span::FileNameDisplayPreference; | ||
use rustc_span::{self, SourceFile}; | ||
use rustc_span::{self, FileNameDisplayPreference, SourceFile}; | ||
use rustc_symbol_mangling::typeid_for_trait_ref; | ||
use rustc_target::abi::{Align, Size}; | ||
use smallvec::smallvec; | ||
use tracing::debug; | ||
|
||
use libc::{c_longlong, c_uint}; | ||
use libc::{c_char, c_longlong, c_uint}; | ||
use std::borrow::Cow; | ||
use std::fmt::{self, Write}; | ||
use std::hash::{Hash, Hasher}; | ||
|
@@ -1468,6 +1469,84 @@ fn build_vtable_type_di_node<'ll, 'tcx>( | |
.di_node | ||
} | ||
|
||
fn vcall_visibility_metadata<'ll, 'tcx>( | ||
cx: &CodegenCx<'ll, 'tcx>, | ||
ty: Ty<'tcx>, | ||
trait_ref: Option<PolyExistentialTraitRef<'tcx>>, | ||
vtable: &'ll Value, | ||
) { | ||
enum VCallVisibility { | ||
Public = 0, | ||
LinkageUnit = 1, | ||
TranslationUnit = 2, | ||
} | ||
|
||
let Some(trait_ref) = trait_ref else { return }; | ||
|
||
let trait_ref_self = trait_ref.with_self_ty(cx.tcx, ty); | ||
let trait_ref_self = cx.tcx.erase_regions(trait_ref_self); | ||
let trait_def_id = trait_ref_self.def_id(); | ||
let trait_vis = cx.tcx.visibility(trait_def_id); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that the closest existing concept similar to Example with an incorrectly optimized vtables showing why visibility is insufficienttrait Foo { fn foo(&self) { println!("foo") } }
trait Bar { fn bar(&self) { println!("bar") } }
trait Baz { fn baz(&self) { println!("baz") } }
impl Foo for usize {}
impl Bar for usize {}
impl Baz for usize {}
pub struct FooBox(Box<dyn Foo>);
pub struct BarBox(Box<dyn Bar>);
pub struct BazBox(Box<dyn Baz>);
pub fn make_foo() -> FooBox { FooBox(Box::new(0)) }
pub fn make_bar() -> BarBox { BarBox(Box::new(0)) }
pub fn make_baz() -> BazBox { BazBox(Box::new(0)) }
#[inline]
pub fn f(a: FooBox) { a.0.foo() }
pub fn g<T>(b: BarBox) { b.0.bar() }
#[inline]
fn h(c: BazBox) { c.0.baz() }
pub const H: fn(BazBox) = h; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for pointing this out. I don't really have a solution for this right now. I tried looking at the reachability of the I guess I would somehow have to check the reachability for the Do you have ideas on how to best address this issue? |
||
|
||
let cgus = cx.sess().codegen_units(); | ||
let single_cgu = cgus == 1; | ||
|
||
let lto = cx.sess().lto(); | ||
|
||
// Since LLVM requires full LTO for the virtual function elimination optimization to apply, | ||
// only the `Lto::Fat` cases are relevant currently. | ||
let vcall_visibility = match (lto, trait_vis, single_cgu) { | ||
// If there is not LTO and the visibility in public, we have to assume that the vtable can | ||
// be seen from anywhere. With multiple CGUs, the vtable is quasi-public. | ||
(Lto::No | Lto::ThinLocal, Visibility::Public, _) | ||
| (Lto::No, Visibility::Restricted(_) | Visibility::Invisible, false) => { | ||
VCallVisibility::Public | ||
} | ||
// With LTO and a quasi-public visibility, the usages of the functions of the vtable are | ||
// all known by the `LinkageUnit`. | ||
// FIXME: LLVM only supports this optimization for `Lto::Fat` currently. Once it also | ||
// supports `Lto::Thin` the `VCallVisibility` may have to be adjusted for those. | ||
(Lto::Fat | Lto::Thin, Visibility::Public, _) | ||
| ( | ||
Lto::ThinLocal | Lto::Thin | Lto::Fat, | ||
Visibility::Restricted(_) | Visibility::Invisible, | ||
false, | ||
) => VCallVisibility::LinkageUnit, | ||
// If there is only one CGU, private vtables can only be seen by that CGU/translation unit | ||
// and therefore we know of all usages of functions in the vtable. | ||
(_, Visibility::Restricted(_) | Visibility::Invisible, true) => { | ||
VCallVisibility::TranslationUnit | ||
} | ||
}; | ||
|
||
let trait_ref_typeid = typeid_for_trait_ref(cx.tcx, trait_ref); | ||
|
||
unsafe { | ||
let typeid = llvm::LLVMMDStringInContext( | ||
cx.llcx, | ||
trait_ref_typeid.as_ptr() as *const c_char, | ||
trait_ref_typeid.as_bytes().len() as c_uint, | ||
); | ||
let v = [cx.const_usize(0), typeid]; | ||
llvm::LLVMRustGlobalAddMetadata( | ||
vtable, | ||
llvm::MD_type as c_uint, | ||
llvm::LLVMValueAsMetadata(llvm::LLVMMDNodeInContext( | ||
cx.llcx, | ||
v.as_ptr(), | ||
v.len() as c_uint, | ||
)), | ||
); | ||
let vcall_visibility = llvm::LLVMValueAsMetadata(cx.const_u64(vcall_visibility as u64)); | ||
let vcall_visibility_metadata = llvm::LLVMMDNodeInContext2(cx.llcx, &vcall_visibility, 1); | ||
llvm::LLVMGlobalSetMetadata( | ||
vtable, | ||
llvm::MetadataType::MD_vcall_visibility as c_uint, | ||
vcall_visibility_metadata, | ||
); | ||
} | ||
} | ||
|
||
/// Creates debug information for the given vtable, which is for the | ||
/// given type. | ||
/// | ||
|
@@ -1478,6 +1557,12 @@ pub fn create_vtable_di_node<'ll, 'tcx>( | |
poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, | ||
vtable: &'ll Value, | ||
) { | ||
// FIXME(flip1995): The virtual function elimination optimization only works with full LTO in | ||
// LLVM at the moment. | ||
if cx.sess().opts.debugging_opts.virtual_function_elimination && cx.sess().lto() == Lto::Fat { | ||
vcall_visibility_metadata(cx, ty, poly_trait_ref, vtable); | ||
} | ||
|
||
if cx.dbg_cx.is_none() { | ||
return; | ||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.