From 71d35abc9b982c44e22b1a219cef832470c5b4b4 Mon Sep 17 00:00:00 2001 From: alecmocatta Date: Sun, 3 Nov 2019 22:47:37 +0000 Subject: [PATCH 1/4] Make an array of pointers to vtables accessible on tier-1 + other platforms --- src/librustc_codegen_llvm/consts.rs | 35 ++++++++++++ src/librustc_codegen_ssa/meth.rs | 2 + src/librustc_codegen_ssa/traits/statics.rs | 1 + src/test/ui/vtables/vtable-list.rs | 65 ++++++++++++++++++++++ 4 files changed, 103 insertions(+) create mode 100644 src/test/ui/vtables/vtable-list.rs diff --git a/src/librustc_codegen_llvm/consts.rs b/src/librustc_codegen_llvm/consts.rs index fd7054a5a0ada..553f485f22902 100644 --- a/src/librustc_codegen_llvm/consts.rs +++ b/src/librustc_codegen_llvm/consts.rs @@ -356,6 +356,41 @@ impl StaticMethods for CodegenCx<'ll, 'tcx> { gv } + fn create_vtable_symbol( + &self, + vtable: &'ll Value, + align: Align, + ) { + unsafe { + // members of llvm.used must be named + let name = self.generate_local_symbol_name("vtable"); + let gv = self.define_global(&name[..], self.val_ty(vtable)).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", name); + }); + llvm::LLVMRustSetLinkage(gv, llvm::Linkage::PrivateLinkage); + llvm::LLVMSetInitializer(gv, vtable); + set_global_alignment(&self, gv, align); + SetUnnamedAddr(gv, true); + llvm::LLVMSetGlobalConstant(gv, True); + let sect_name: Option<&[u8]> = if self.tcx.sess.target.target.options.is_like_windows { + Some(b".rdata.__rust_vtables$B\0") + } else if self.tcx.sess.target.target.options.is_like_osx { + Some(b"__DATA,__rust_vtables\0") + } else if self.tcx.sess.opts.target_triple.triple().contains("-linux-") { + Some(b"__rust_vtables\0") + } else { + None + }; + let sect_name = sect_name.map(|name| CStr::from_bytes_with_nul_unchecked(name)); + if let Some(sect_name) = sect_name { + llvm::LLVMSetSection(gv, sect_name.as_ptr()); + } + // This static will be stored in the llvm.used variable which is an array of i8* + let cast = llvm::LLVMConstPointerCast(gv, self.type_i8p()); + self.used_statics.borrow_mut().push(cast); + } + } + fn codegen_static( &self, def_id: DefId, diff --git a/src/librustc_codegen_ssa/meth.rs b/src/librustc_codegen_ssa/meth.rs index 266d2e5b18d22..d5b8762e83b73 100644 --- a/src/librustc_codegen_ssa/meth.rs +++ b/src/librustc_codegen_ssa/meth.rs @@ -117,6 +117,8 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( let align = cx.data_layout().pointer_align.abi; let vtable = cx.static_addr_of(vtable_const, align, Some("vtable")); + cx.create_vtable_symbol(vtable, align); + cx.create_vtable_metadata(ty, vtable); cx.vtables().borrow_mut().insert((ty, trait_ref), vtable); diff --git a/src/librustc_codegen_ssa/traits/statics.rs b/src/librustc_codegen_ssa/traits/statics.rs index 5c108f9fa6cc5..0b3e4c6f09daa 100644 --- a/src/librustc_codegen_ssa/traits/statics.rs +++ b/src/librustc_codegen_ssa/traits/statics.rs @@ -4,6 +4,7 @@ use rustc::ty::layout::Align; pub trait StaticMethods: BackendTypes { fn static_addr_of(&self, cv: Self::Value, align: Align, kind: Option<&str>) -> Self::Value; + fn create_vtable_symbol(&self, cv: Self::Value, align: Align); fn codegen_static(&self, def_id: DefId, is_mutable: bool); } diff --git a/src/test/ui/vtables/vtable-list.rs b/src/test/ui/vtables/vtable-list.rs new file mode 100644 index 0000000000000..2af6ff6fddc48 --- /dev/null +++ b/src/test/ui/vtables/vtable-list.rs @@ -0,0 +1,65 @@ +// For windows, linux, android, macos and ios only + +// run-pass + +// ignore-cloudabi +// ignore-dragonfly +// ignore-emscripten +// ignore-freebsd +// ignore-haiku +// ignore-netbsd +// ignore-openbsd +// ignore-solaris +// ignore-sgx + +#![feature(ptr_offset_from)] +#![feature(raw)] + +use std::{mem::transmute, raw::TraitObject, slice}; + +#[allow(improper_ctypes)] +fn vtables() -> &'static [&'static ()] { + #[cfg(any(target_os = "linux", target_os = "android", target_os = "macos", target_os = "ios"))] + extern "C" { + #[cfg_attr( + any(target_os = "linux", target_os = "android"), + link_name = "__start___rust_vtables" + )] + #[cfg_attr( + any(target_os = "macos", target_os = "ios"), + link_name = "\u{1}section$start$__DATA$__rust_vtables" + )] + static START: [&'static (); 0]; + #[cfg_attr( + any(target_os = "linux", target_os = "android"), + link_name = "__stop___rust_vtables" + )] + #[cfg_attr( + any(target_os = "macos", target_os = "ios"), + link_name = "\u{1}section$end$__DATA$__rust_vtables" + )] + static END: [&'static (); 0]; + } + + #[cfg(target_os = "windows")] + { + #[link_section = ".rdata.__rust_vtables$A"] + static START: [&'static (); 0] = []; + #[link_section = ".rdata.__rust_vtables$C"] + static END: [&'static (); 0] = []; + } + + unsafe { + let (start_ptr, end_ptr) = (&START as *const &'static (), &END as *const &'static ()); + slice::from_raw_parts(start_ptr, end_ptr.offset_from(start_ptr) as usize) + } +} + +trait Trait {} +struct Foo; +impl Trait for Foo {} + +fn main() { + let vtable: &'static () = unsafe { &*transmute::<&dyn Trait, TraitObject>(&Foo).vtable }; + vtables().iter().find(|&&x| x as *const () == vtable as *const ()).unwrap(); +} From e41d2022e34ab955447eebff29619feb936e5ee5 Mon Sep 17 00:00:00 2001 From: alecmocatta Date: Mon, 4 Nov 2019 14:52:12 +0000 Subject: [PATCH 2/4] Use llvm.compiler.used to let linker remove dead data --- src/librustc_codegen_llvm/consts.rs | 63 +++++++++++++++------- src/librustc_codegen_ssa/meth.rs | 2 +- src/librustc_codegen_ssa/traits/statics.rs | 2 +- src/test/ui/vtables/vtable-list.rs | 2 +- 4 files changed, 48 insertions(+), 21 deletions(-) diff --git a/src/librustc_codegen_llvm/consts.rs b/src/librustc_codegen_llvm/consts.rs index 553f485f22902..b30c6dbfa8d22 100644 --- a/src/librustc_codegen_llvm/consts.rs +++ b/src/librustc_codegen_llvm/consts.rs @@ -356,14 +356,43 @@ impl StaticMethods for CodegenCx<'ll, 'tcx> { gv } - fn create_vtable_symbol( + fn append_vtable_pointer( &self, vtable: &'ll Value, align: Align, ) { + // Add a pointer to the vtable to a special section. The linker can provide pointers to the + // start and end of this section, enabling user code to retrieve an array of all + // materializable vtable pointers. + // + // On Windows, the contents of sections is sorted by the string after the $. Static + // variables with link_sections .rdata.__rust_vtables$A and .rdata.__rust_vtables$C can be + // used as the start and end pointers. Unreferenced data elimination (rustc enables this + // with /OPT:REF) removes the section if it's not used. + // + // On Mac and iOS, the linker-defined symbols section$start$__DATA$__rust_vtables and + // section$end$__DATA$__rust_vtables are the start and end pointers. Mac aggressively strips + // ostensibly-dead symbols (llvm and rustc enable this with .subsections_via_symbols and + // -dead_strip). live_support inverts the live-marking process: symbols that reference live + // symbols are themselves marked live. + // + // On Linux and Android, the linker-defined symbols __start___rust_vtables and + // __stop___rust_vtables are the start and end pointers. Section garbage collection (rustc + // enables this with --gc-sections) removes the section if it's not used. + // + // A few other platforms work much the same way as Linux here, but for now just do nothing. + let sect_name = if self.tcx.sess.target.target.options.is_like_windows { + const_cstr!(".rdata.__rust_vtables$B") + } else if self.tcx.sess.target.target.options.is_like_osx { + const_cstr!("__DATA,__rust_vtables,regular,live_support") + } else if self.tcx.sess.opts.target_triple.triple().contains("-linux-") { + const_cstr!("__rust_vtables") + } else { + return; + }; unsafe { - // members of llvm.used must be named - let name = self.generate_local_symbol_name("vtable"); + // members of llvm.compiler.used must be named + let name = self.generate_local_symbol_name("vtable_ptr"); let gv = self.define_global(&name[..], self.val_ty(vtable)).unwrap_or_else(|| { bug!("symbol `{}` is already defined", name); }); @@ -372,22 +401,20 @@ impl StaticMethods for CodegenCx<'ll, 'tcx> { set_global_alignment(&self, gv, align); SetUnnamedAddr(gv, true); llvm::LLVMSetGlobalConstant(gv, True); - let sect_name: Option<&[u8]> = if self.tcx.sess.target.target.options.is_like_windows { - Some(b".rdata.__rust_vtables$B\0") - } else if self.tcx.sess.target.target.options.is_like_osx { - Some(b"__DATA,__rust_vtables\0") - } else if self.tcx.sess.opts.target_triple.triple().contains("-linux-") { - Some(b"__rust_vtables\0") - } else { - None - }; - let sect_name = sect_name.map(|name| CStr::from_bytes_with_nul_unchecked(name)); - if let Some(sect_name) = sect_name { - llvm::LLVMSetSection(gv, sect_name.as_ptr()); - } - // This static will be stored in the llvm.used variable which is an array of i8* + llvm::LLVMSetSection(gv, sect_name.as_ptr()); + + // Add this static to the special llvm.compiler.used variable, which is an array of i8* + // that prevents LLVM from optimizing away referenced values. Use this rather than + // llvm.used so that the linker can optimize away the section if it is unused. let cast = llvm::LLVMConstPointerCast(gv, self.type_i8p()); - self.used_statics.borrow_mut().push(cast); + let name = const_cstr!("llvm.compiler.used"); + let section = const_cstr!("llvm.metadata"); + let array = self.const_array(&self.type_ptr_to(self.type_i8()), &[cast]); + + let gv = llvm::LLVMAddGlobal(self.llmod, self.val_ty(array), name.as_ptr()); + llvm::LLVMSetInitializer(gv, array); + llvm::LLVMRustSetLinkage(gv, llvm::Linkage::AppendingLinkage); + llvm::LLVMSetSection(gv, section.as_ptr()); } } diff --git a/src/librustc_codegen_ssa/meth.rs b/src/librustc_codegen_ssa/meth.rs index d5b8762e83b73..6d08ddeefce5a 100644 --- a/src/librustc_codegen_ssa/meth.rs +++ b/src/librustc_codegen_ssa/meth.rs @@ -117,7 +117,7 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( let align = cx.data_layout().pointer_align.abi; let vtable = cx.static_addr_of(vtable_const, align, Some("vtable")); - cx.create_vtable_symbol(vtable, align); + cx.append_vtable_pointer(vtable, align); cx.create_vtable_metadata(ty, vtable); diff --git a/src/librustc_codegen_ssa/traits/statics.rs b/src/librustc_codegen_ssa/traits/statics.rs index 0b3e4c6f09daa..fa8ae97573e85 100644 --- a/src/librustc_codegen_ssa/traits/statics.rs +++ b/src/librustc_codegen_ssa/traits/statics.rs @@ -4,7 +4,7 @@ use rustc::ty::layout::Align; pub trait StaticMethods: BackendTypes { fn static_addr_of(&self, cv: Self::Value, align: Align, kind: Option<&str>) -> Self::Value; - fn create_vtable_symbol(&self, cv: Self::Value, align: Align); + fn append_vtable_pointer(&self, cv: Self::Value, align: Align); fn codegen_static(&self, def_id: DefId, is_mutable: bool); } diff --git a/src/test/ui/vtables/vtable-list.rs b/src/test/ui/vtables/vtable-list.rs index 2af6ff6fddc48..83223d792ecfb 100644 --- a/src/test/ui/vtables/vtable-list.rs +++ b/src/test/ui/vtables/vtable-list.rs @@ -1,4 +1,4 @@ -// For windows, linux, android, macos and ios only +// Only Windows, Linux, Android, macOS and iOS have this implemented for now. // run-pass From 1aca4db7db8d151898deecc229257fc977e33e71 Mon Sep 17 00:00:00 2001 From: alecmocatta Date: Mon, 4 Nov 2019 19:11:00 +0000 Subject: [PATCH 3/4] add type_id to the lookup table --- src/librustc_codegen_llvm/consts.rs | 8 ++-- src/librustc_codegen_ssa/base.rs | 2 +- src/librustc_codegen_ssa/meth.rs | 6 ++- src/librustc_codegen_ssa/traits/statics.rs | 2 +- src/librustc_mir/interpret/cast.rs | 2 +- src/librustc_mir/interpret/traits.rs | 1 + src/test/ui/vtables/vtable-list.rs | 52 +++++++++++++++++----- 7 files changed, 53 insertions(+), 20 deletions(-) diff --git a/src/librustc_codegen_llvm/consts.rs b/src/librustc_codegen_llvm/consts.rs index b30c6dbfa8d22..f06964dfe1a99 100644 --- a/src/librustc_codegen_llvm/consts.rs +++ b/src/librustc_codegen_llvm/consts.rs @@ -356,9 +356,9 @@ impl StaticMethods for CodegenCx<'ll, 'tcx> { gv } - fn append_vtable_pointer( + fn append_vtable_lookup( &self, - vtable: &'ll Value, + vtable_lookup: &'ll Value, align: Align, ) { // Add a pointer to the vtable to a special section. The linker can provide pointers to the @@ -393,11 +393,11 @@ impl StaticMethods for CodegenCx<'ll, 'tcx> { unsafe { // members of llvm.compiler.used must be named let name = self.generate_local_symbol_name("vtable_ptr"); - let gv = self.define_global(&name[..], self.val_ty(vtable)).unwrap_or_else(|| { + let gv = self.define_global(&name[..], self.val_ty(vtable_lookup)).unwrap_or_else(|| { bug!("symbol `{}` is already defined", name); }); llvm::LLVMRustSetLinkage(gv, llvm::Linkage::PrivateLinkage); - llvm::LLVMSetInitializer(gv, vtable); + llvm::LLVMSetInitializer(gv, vtable_lookup); set_global_alignment(&self, gv, align); SetUnnamedAddr(gv, true); llvm::LLVMSetGlobalConstant(gv, True); diff --git a/src/librustc_codegen_ssa/base.rs b/src/librustc_codegen_ssa/base.rs index ee4ec7fb41eac..92249f14f11e5 100644 --- a/src/librustc_codegen_ssa/base.rs +++ b/src/librustc_codegen_ssa/base.rs @@ -140,7 +140,7 @@ pub fn unsized_info<'tcx, Cx: CodegenMethods<'tcx>>( (_, &ty::Dynamic(ref data, ..)) => { let vtable_ptr = cx.layout_of(cx.tcx().mk_mut_ptr(target)) .field(cx, FAT_PTR_EXTRA); - cx.const_ptrcast(meth::get_vtable(cx, source, data.principal()), + cx.const_ptrcast(meth::get_vtable(cx, source, target, data.principal()), cx.backend_type(vtable_ptr)) } _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", diff --git a/src/librustc_codegen_ssa/meth.rs b/src/librustc_codegen_ssa/meth.rs index 6d08ddeefce5a..2067ddce13fa3 100644 --- a/src/librustc_codegen_ssa/meth.rs +++ b/src/librustc_codegen_ssa/meth.rs @@ -67,6 +67,7 @@ impl<'a, 'tcx> VirtualIndex { pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( cx: &Cx, ty: Ty<'tcx>, + trait_ty: Ty<'tcx>, trait_ref: Option>, ) -> Cx::Value { let tcx = cx.tcx(); @@ -117,7 +118,10 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( let align = cx.data_layout().pointer_align.abi; let vtable = cx.static_addr_of(vtable_const, align, Some("vtable")); - cx.append_vtable_pointer(vtable, align); + let type_id = tcx.type_id_hash(trait_ty); + let type_id = cx.const_u64(type_id); + let vtable_lookup_const = cx.const_struct(&[type_id, vtable], true); + cx.append_vtable_lookup(vtable_lookup_const, align); cx.create_vtable_metadata(ty, vtable); diff --git a/src/librustc_codegen_ssa/traits/statics.rs b/src/librustc_codegen_ssa/traits/statics.rs index fa8ae97573e85..00a39df8c5260 100644 --- a/src/librustc_codegen_ssa/traits/statics.rs +++ b/src/librustc_codegen_ssa/traits/statics.rs @@ -4,7 +4,7 @@ use rustc::ty::layout::Align; pub trait StaticMethods: BackendTypes { fn static_addr_of(&self, cv: Self::Value, align: Align, kind: Option<&str>) -> Self::Value; - fn append_vtable_pointer(&self, cv: Self::Value, align: Align); + fn append_vtable_lookup(&self, cv: Self::Value, align: Align); fn codegen_static(&self, def_id: DefId, is_mutable: bool); } diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 9ab347957f97a..0e589fdcb654a 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -278,7 +278,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } (_, &ty::Dynamic(ref data, _)) => { // Initial cast from sized to dyn trait - let vtable = self.get_vtable(src_pointee_ty, data.principal())?; + let vtable = self.get_vtable(src_pointee_ty, dest_pointee_ty, data.principal())?; let ptr = self.read_immediate(src)?.to_scalar_ptr()?; let val = Immediate::new_dyn_trait(ptr, vtable); self.write_immediate(val, dest) diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index 10b767ebba191..018f15c92ee1b 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -14,6 +14,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn get_vtable( &mut self, ty: Ty<'tcx>, + _poly_trait_ty: Ty<'tcx>, poly_trait_ref: Option>, ) -> InterpResult<'tcx, Pointer> { trace!("get_vtable(trait_ref={:?})", poly_trait_ref); diff --git a/src/test/ui/vtables/vtable-list.rs b/src/test/ui/vtables/vtable-list.rs index 83223d792ecfb..6125ac96b1be2 100644 --- a/src/test/ui/vtables/vtable-list.rs +++ b/src/test/ui/vtables/vtable-list.rs @@ -12,14 +12,26 @@ // ignore-solaris // ignore-sgx +#![feature(core_intrinsics)] #![feature(ptr_offset_from)] #![feature(raw)] +#![feature(test)] -use std::{mem::transmute, raw::TraitObject, slice}; +use std::intrinsics::type_id; +use std::mem::transmute; +use std::raw::TraitObject; +use std::slice; -#[allow(improper_ctypes)] -fn vtables() -> &'static [&'static ()] { +#[derive(Copy, Clone)] +#[repr(C, packed)] +struct Record { + type_id: u64, + vtable: &'static (), +} + +fn vtables() -> &'static [Record] { #[cfg(any(target_os = "linux", target_os = "android", target_os = "macos", target_os = "ios"))] + #[allow(improper_ctypes)] extern "C" { #[cfg_attr( any(target_os = "linux", target_os = "android"), @@ -29,7 +41,7 @@ fn vtables() -> &'static [&'static ()] { any(target_os = "macos", target_os = "ios"), link_name = "\u{1}section$start$__DATA$__rust_vtables" )] - static START: [&'static (); 0]; + static START: [Record; 0]; #[cfg_attr( any(target_os = "linux", target_os = "android"), link_name = "__stop___rust_vtables" @@ -38,28 +50,44 @@ fn vtables() -> &'static [&'static ()] { any(target_os = "macos", target_os = "ios"), link_name = "\u{1}section$end$__DATA$__rust_vtables" )] - static END: [&'static (); 0]; + static END: [Record; 0]; } #[cfg(target_os = "windows")] { #[link_section = ".rdata.__rust_vtables$A"] - static START: [&'static (); 0] = []; + static START: [Record; 0] = []; #[link_section = ".rdata.__rust_vtables$C"] - static END: [&'static (); 0] = []; + static END: [Record; 0] = []; } unsafe { - let (start_ptr, end_ptr) = (&START as *const &'static (), &END as *const &'static ()); + let (start_ptr, end_ptr) = (&START as *const Record, &END as *const Record); slice::from_raw_parts(start_ptr, end_ptr.offset_from(start_ptr) as usize) } } trait Trait {} -struct Foo; -impl Trait for Foo {} +struct Struct; +impl Trait for Struct {} + +#[inline(never)] +fn foo() { + let a: &dyn Trait = &Struct; + let b: &(dyn Trait + Send) = &Struct; + std::hint::black_box((a, b)); +} fn main() { - let vtable: &'static () = unsafe { &*transmute::<&dyn Trait, TraitObject>(&Foo).vtable }; - vtables().iter().find(|&&x| x as *const () == vtable as *const ()).unwrap(); + let vtable: &'static () = unsafe { &*transmute::<&dyn Trait, TraitObject>(&Struct).vtable }; + let type_id = unsafe { type_id::() }; + let count = vtables().iter().filter(|&&record| record.type_id == type_id).count(); + assert_ne!(count, 0, "The vtable record for dyn Trait is missing"); + assert_eq!(count, 1, "Duplicate vtable records found for dyn Trait"); + let record = vtables().iter().find(|&&record| record.vtable as *const () == vtable).unwrap(); + assert_eq!( + record.vtable as *const (), vtable, + "The vtable for Struct as dyn Trait is incorrect" + ); + foo(); } From 0dd29cfed23a9356367dfa223616a8282f06054a Mon Sep 17 00:00:00 2001 From: alecmocatta Date: Mon, 4 Nov 2019 21:08:33 +0000 Subject: [PATCH 4/4] refactor to use compiler_used_statics --- src/librustc_codegen_llvm/base.rs | 7 +++-- src/librustc_codegen_llvm/consts.rs | 23 ++++++---------- src/librustc_codegen_llvm/context.rs | 26 +++++++++++++++++++ src/librustc_codegen_ssa/meth.rs | 4 +-- src/librustc_codegen_ssa/traits/misc.rs | 2 ++ .../ui/vtables/{vtable-list.rs => lookup.rs} | 11 +++++--- 6 files changed, 50 insertions(+), 23 deletions(-) rename src/test/ui/vtables/{vtable-list.rs => lookup.rs} (85%) diff --git a/src/librustc_codegen_llvm/base.rs b/src/librustc_codegen_llvm/base.rs index edd34b52eade7..7200ed8eeb440 100644 --- a/src/librustc_codegen_llvm/base.rs +++ b/src/librustc_codegen_llvm/base.rs @@ -162,11 +162,14 @@ pub fn compile_codegen_unit( } } - // Create the llvm.used variable - // This variable has type [N x i8*] and is stored in the llvm.metadata section + // Create the llvm.used and llvm.compiler.used variables + // These have type [N x i8*] and are stored in the llvm.metadata section if !cx.used_statics().borrow().is_empty() { cx.create_used_variable() } + if !cx.compiler_used_statics().borrow().is_empty() { + cx.create_compiler_used_variable() + } // Finalize debuginfo if cx.sess().opts.debuginfo != DebugInfo::None { diff --git a/src/librustc_codegen_llvm/consts.rs b/src/librustc_codegen_llvm/consts.rs index f06964dfe1a99..f534f12500c13 100644 --- a/src/librustc_codegen_llvm/consts.rs +++ b/src/librustc_codegen_llvm/consts.rs @@ -358,7 +358,7 @@ impl StaticMethods for CodegenCx<'ll, 'tcx> { fn append_vtable_lookup( &self, - vtable_lookup: &'ll Value, + vtable_record: &'ll Value, align: Align, ) { // Add a pointer to the vtable to a special section. The linker can provide pointers to the @@ -390,14 +390,14 @@ impl StaticMethods for CodegenCx<'ll, 'tcx> { } else { return; }; + // members of llvm.compiler.used must be named + let name = self.generate_local_symbol_name("vtable_record"); + let gv = self.define_global(&name[..], self.val_ty(vtable_record)).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", name); + }); unsafe { - // members of llvm.compiler.used must be named - let name = self.generate_local_symbol_name("vtable_ptr"); - let gv = self.define_global(&name[..], self.val_ty(vtable_lookup)).unwrap_or_else(|| { - bug!("symbol `{}` is already defined", name); - }); llvm::LLVMRustSetLinkage(gv, llvm::Linkage::PrivateLinkage); - llvm::LLVMSetInitializer(gv, vtable_lookup); + llvm::LLVMSetInitializer(gv, vtable_record); set_global_alignment(&self, gv, align); SetUnnamedAddr(gv, true); llvm::LLVMSetGlobalConstant(gv, True); @@ -407,14 +407,7 @@ impl StaticMethods for CodegenCx<'ll, 'tcx> { // that prevents LLVM from optimizing away referenced values. Use this rather than // llvm.used so that the linker can optimize away the section if it is unused. let cast = llvm::LLVMConstPointerCast(gv, self.type_i8p()); - let name = const_cstr!("llvm.compiler.used"); - let section = const_cstr!("llvm.metadata"); - let array = self.const_array(&self.type_ptr_to(self.type_i8()), &[cast]); - - let gv = llvm::LLVMAddGlobal(self.llmod, self.val_ty(array), name.as_ptr()); - llvm::LLVMSetInitializer(gv, array); - llvm::LLVMRustSetLinkage(gv, llvm::Linkage::AppendingLinkage); - llvm::LLVMSetSection(gv, section.as_ptr()); + self.compiler_used_statics.borrow_mut().push(cast); } } diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs index 2da9387717214..0c2343edd6acc 100644 --- a/src/librustc_codegen_llvm/context.rs +++ b/src/librustc_codegen_llvm/context.rs @@ -75,6 +75,9 @@ pub struct CodegenCx<'ll, 'tcx> { /// Statics that will be placed in the llvm.used variable /// See for details pub used_statics: RefCell>, + /// Statics that will be placed in the llvm.compiler.used variable + /// see for details + pub compiler_used_statics: RefCell>, pub lltypes: RefCell, Option), &'ll Type>>, pub scalar_lltypes: RefCell, &'ll Type>>, @@ -301,6 +304,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { const_globals: Default::default(), statics_to_rauw: RefCell::new(Vec::new()), used_statics: RefCell::new(Vec::new()), + compiler_used_statics: RefCell::new(Vec::new()), lltypes: Default::default(), scalar_lltypes: Default::default(), pointee_infos: Default::default(), @@ -438,6 +442,10 @@ impl MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> { &self.used_statics } + fn compiler_used_statics(&self) -> &RefCell> { + &self.compiler_used_statics + } + fn set_frame_pointer_elimination(&self, llfn: &'ll Value) { attributes::set_frame_pointer_elimination(self, llfn) } @@ -463,6 +471,24 @@ impl MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> { llvm::LLVMSetSection(g, section.as_ptr()); } } + + fn create_compiler_used_variable(&self) { + let name = const_cstr!("llvm.compiler.used"); + let section = const_cstr!("llvm.metadata"); + let array = self.const_array( + &self.type_ptr_to(self.type_i8()), + &*self.compiler_used_statics.borrow() + ); + + unsafe { + let g = llvm::LLVMAddGlobal(self.llmod, + self.val_ty(array), + name.as_ptr()); + llvm::LLVMSetInitializer(g, array); + llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage); + llvm::LLVMSetSection(g, section.as_ptr()); + } + } } impl CodegenCx<'b, 'tcx> { diff --git a/src/librustc_codegen_ssa/meth.rs b/src/librustc_codegen_ssa/meth.rs index 2067ddce13fa3..1d545f40e977a 100644 --- a/src/librustc_codegen_ssa/meth.rs +++ b/src/librustc_codegen_ssa/meth.rs @@ -120,8 +120,8 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( let type_id = tcx.type_id_hash(trait_ty); let type_id = cx.const_u64(type_id); - let vtable_lookup_const = cx.const_struct(&[type_id, vtable], true); - cx.append_vtable_lookup(vtable_lookup_const, align); + let vtable_record = cx.const_struct(&[type_id, vtable], true); + cx.append_vtable_lookup(vtable_record, align); cx.create_vtable_metadata(ty, vtable); diff --git a/src/librustc_codegen_ssa/traits/misc.rs b/src/librustc_codegen_ssa/traits/misc.rs index 658ddd0028076..8ae4c88e9a994 100644 --- a/src/librustc_codegen_ssa/traits/misc.rs +++ b/src/librustc_codegen_ssa/traits/misc.rs @@ -18,7 +18,9 @@ pub trait MiscMethods<'tcx>: BackendTypes { fn sess(&self) -> &Session; fn codegen_unit(&self) -> &Arc>; fn used_statics(&self) -> &RefCell>; + fn compiler_used_statics(&self) -> &RefCell>; fn set_frame_pointer_elimination(&self, llfn: Self::Function); fn apply_target_cpu_attr(&self, llfn: Self::Function); fn create_used_variable(&self); + fn create_compiler_used_variable(&self); } diff --git a/src/test/ui/vtables/vtable-list.rs b/src/test/ui/vtables/lookup.rs similarity index 85% rename from src/test/ui/vtables/vtable-list.rs rename to src/test/ui/vtables/lookup.rs index 6125ac96b1be2..70e697d58cae3 100644 --- a/src/test/ui/vtables/vtable-list.rs +++ b/src/test/ui/vtables/lookup.rs @@ -30,6 +30,9 @@ struct Record { } fn vtables() -> &'static [Record] { + // This must be kept in sync with append_vtable_lookup in src/librustc_codegen_llvm/consts.rs + // + // \u{1} is used for the apple link_names to tell llvm not to prefix with an underscore #[cfg(any(target_os = "linux", target_os = "android", target_os = "macos", target_os = "ios"))] #[allow(improper_ctypes)] extern "C" { @@ -72,7 +75,7 @@ struct Struct; impl Trait for Struct {} #[inline(never)] -fn foo() { +fn multiple_upcasts() { let a: &dyn Trait = &Struct; let b: &(dyn Trait + Send) = &Struct; std::hint::black_box((a, b)); @@ -81,13 +84,13 @@ fn foo() { fn main() { let vtable: &'static () = unsafe { &*transmute::<&dyn Trait, TraitObject>(&Struct).vtable }; let type_id = unsafe { type_id::() }; - let count = vtables().iter().filter(|&&record| record.type_id == type_id).count(); + let count = vtables().iter().filter(|&record| record.type_id == type_id).count(); assert_ne!(count, 0, "The vtable record for dyn Trait is missing"); assert_eq!(count, 1, "Duplicate vtable records found for dyn Trait"); - let record = vtables().iter().find(|&&record| record.vtable as *const () == vtable).unwrap(); + let record = vtables().iter().find(|&record| record.vtable as *const () == vtable).unwrap(); assert_eq!( record.vtable as *const (), vtable, "The vtable for Struct as dyn Trait is incorrect" ); - foo(); + multiple_upcasts(); }