diff options
Diffstat (limited to 'src/librustc_codegen_llvm')
39 files changed, 1069 insertions, 2546 deletions
| diff --git a/src/librustc_codegen_llvm/abi.rs b/src/librustc_codegen_llvm/abi.rs index 7b93d3e795e..03b0b04d401 100644 --- a/src/librustc_codegen_llvm/abi.rs +++ b/src/librustc_codegen_llvm/abi.rs @@ -11,7 +11,7 @@ use llvm::{self, AttributePlace}; use base; use builder::{Builder, MemFlags}; -use common::{ty_fn_sig, C_usize}; +use common::C_usize; use context::CodegenCx; use mir::place::PlaceRef; use mir::operand::OperandValue; @@ -19,7 +19,7 @@ use type_::Type; use type_of::{LayoutLlvmExt, PointerKind}; use value::Value; -use rustc_target::abi::{LayoutOf, Size, TyLayout}; +use rustc_target::abi::{HasDataLayout, LayoutOf, Size, TyLayout, Abi as LayoutAbi}; use rustc::ty::{self, Ty}; use rustc::ty::layout; @@ -225,9 +225,10 @@ impl ArgTypeExt<'ll, 'tcx> for ArgType<'tcx, Ty<'tcx>> { // ...and then memcpy it to the intended destination. base::call_memcpy(bx, bx.pointercast(dst.llval, Type::i8p(cx)), + self.layout.align, bx.pointercast(llscratch, Type::i8p(cx)), + scratch_align, C_usize(cx, self.layout.size.bytes()), - self.layout.align.min(scratch_align), MemFlags::empty()); bx.lifetime_end(llscratch, scratch_size); @@ -276,6 +277,7 @@ pub trait FnTypeExt<'tcx> { cx: &CodegenCx<'ll, 'tcx>, abi: Abi); fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type; + fn ptr_to_llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type; fn llvm_cconv(&self) -> llvm::CallConv; fn apply_attrs_llfn(&self, llfn: &'ll Value); fn apply_attrs_callsite(&self, bx: &Builder<'a, 'll, 'tcx>, callsite: &'ll Value); @@ -283,8 +285,7 @@ pub trait FnTypeExt<'tcx> { impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> { fn of_instance(cx: &CodegenCx<'ll, 'tcx>, instance: &ty::Instance<'tcx>) -> Self { - let fn_ty = instance.ty(cx.tcx); - let sig = ty_fn_sig(cx, fn_ty); + let sig = instance.fn_sig(cx.tcx); let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); FnType::new(cx, sig, &[]) } @@ -303,21 +304,49 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> { FnType::new_internal(cx, sig, extra_args, |ty, arg_idx| { let mut layout = cx.layout_of(ty); // Don't pass the vtable, it's not an argument of the virtual fn. - // Instead, pass just the (thin pointer) first field of `*dyn Trait`. + // Instead, pass just the data pointer, but give it the type `*const/mut dyn Trait` + // or `&/&mut dyn Trait` because this is special-cased elsewhere in codegen if arg_idx == Some(0) { - if layout.is_unsized() { - unimplemented!("by-value trait object is not \ - yet implemented in #![feature(unsized_locals)]"); - } - // FIXME(eddyb) `layout.field(cx, 0)` is not enough because e.g. - // `Box<dyn Trait>` has a few newtype wrappers around the raw - // pointer, so we'd have to "dig down" to find `*dyn Trait`. - let pointee = layout.ty.builtin_deref(true) - .unwrap_or_else(|| { - bug!("FnType::new_vtable: non-pointer self {:?}", layout) - }).ty; - let fat_ptr_ty = cx.tcx.mk_mut_ptr(pointee); - layout = cx.layout_of(fat_ptr_ty).field(cx, 0); + let fat_pointer_ty = if layout.is_unsized() { + // unsized `self` is passed as a pointer to `self` + // FIXME (mikeyhew) change this to use &own if it is ever added to the language + cx.tcx.mk_mut_ptr(layout.ty) + } else { + match layout.abi { + LayoutAbi::ScalarPair(..) => (), + _ => bug!("receiver type has unsupported layout: {:?}", layout) + } + + // In the case of Rc<Self>, we need to explicitly pass a *mut RcBox<Self> + // with a Scalar (not ScalarPair) ABI. This is a hack that is understood + // elsewhere in the compiler as a method on a `dyn Trait`. + // To get the type `*mut RcBox<Self>`, we just keep unwrapping newtypes until we + // get a built-in pointer type + let mut fat_pointer_layout = layout; + 'descend_newtypes: while !fat_pointer_layout.ty.is_unsafe_ptr() + && !fat_pointer_layout.ty.is_region_ptr() + { + 'iter_fields: for i in 0..fat_pointer_layout.fields.count() { + let field_layout = fat_pointer_layout.field(cx, i); + + if !field_layout.is_zst() { + fat_pointer_layout = field_layout; + continue 'descend_newtypes + } + } + + bug!("receiver has no non-zero-sized fields {:?}", fat_pointer_layout); + } + + fat_pointer_layout.ty + }; + + // we now have a type like `*mut RcBox<dyn Trait>` + // change its layout to that of `*mut ()`, a thin pointer, but keep the same type + // this is understood as a special case elsewhere in the compiler + let unit_pointer_ty = cx.tcx.mk_mut_ptr(cx.tcx.mk_unit()); + layout = cx.layout_of(unit_pointer_ty); + layout.ty = fat_pointer_ty; } ArgType::new(layout) }) @@ -630,6 +659,13 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> { } } + fn ptr_to_llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type { + unsafe { + llvm::LLVMPointerType(self.llvm_type(cx), + cx.data_layout().instruction_address_space as c_uint) + } + } + fn llvm_cconv(&self) -> llvm::CallConv { match self.conv { Conv::C => llvm::CCallConv, diff --git a/src/librustc_codegen_llvm/attributes.rs b/src/librustc_codegen_llvm/attributes.rs index 90ba103ca4c..f45b3728bc1 100644 --- a/src/librustc_codegen_llvm/attributes.rs +++ b/src/librustc_codegen_llvm/attributes.rs @@ -154,7 +154,7 @@ pub fn from_fn_attrs( id: Option<DefId>, ) { let codegen_fn_attrs = id.map(|id| cx.tcx.codegen_fn_attrs(id)) - .unwrap_or(CodegenFnAttrs::new()); + .unwrap_or_else(|| CodegenFnAttrs::new()); inline(cx, llfn, codegen_fn_attrs.inline); @@ -297,7 +297,7 @@ pub fn provide_extern(providers: &mut Providers) { } ).collect::<FxHashMap<_, _>>(); - let mut ret = FxHashMap(); + let mut ret = FxHashMap::default(); for lib in tcx.foreign_modules(cnum).iter() { let module = def_id_to_native_lib .get(&lib.def_id) diff --git a/src/librustc_codegen_llvm/back/archive.rs b/src/librustc_codegen_llvm/back/archive.rs index af9efc6d7c4..54245a36017 100644 --- a/src/librustc_codegen_llvm/back/archive.rs +++ b/src/librustc_codegen_llvm/back/archive.rs @@ -52,28 +52,6 @@ enum Addition { }, } -pub fn find_library(name: &str, search_paths: &[PathBuf], sess: &Session) - -> PathBuf { - // On Windows, static libraries sometimes show up as libfoo.a and other - // times show up as foo.lib - let oslibname = format!("{}{}{}", - sess.target.target.options.staticlib_prefix, - name, - sess.target.target.options.staticlib_suffix); - let unixlibname = format!("lib{}.a", name); - - for path in search_paths { - debug!("looking for {} inside {:?}", name, path); - let test = path.join(&oslibname); - if test.exists() { return test } - if oslibname != unixlibname { - let test = path.join(&unixlibname); - if test.exists() { return test } - } - } - sess.fatal(&format!("could not find native static library `{}`, \ - perhaps an -L flag is missing?", name)); -} fn is_relevant_child(c: &Child) -> bool { match c.name() { @@ -105,15 +83,16 @@ impl<'a> ArchiveBuilder<'a> { if self.src_archive().is_none() { return Vec::new() } + let archive = self.src_archive.as_ref().unwrap().as_ref().unwrap(); - let ret = archive.iter() - .filter_map(|child| child.ok()) - .filter(is_relevant_child) - .filter_map(|child| child.name()) - .filter(|name| !self.removals.iter().any(|x| x == name)) - .map(|name| name.to_string()) - .collect(); - return ret; + + archive.iter() + .filter_map(|child| child.ok()) + .filter(is_relevant_child) + .filter_map(|child| child.name()) + .filter(|name| !self.removals.iter().any(|x| x == name)) + .map(|name| name.to_owned()) + .collect() } fn src_archive(&mut self) -> Option<&ArchiveRO> { @@ -128,7 +107,7 @@ impl<'a> ArchiveBuilder<'a> { /// Adds all of the contents of a native library to this archive. This will /// search in the relevant locations for a library named `name`. pub fn add_native_library(&mut self, name: &str) { - let location = find_library(name, &self.config.lib_search_paths, + let location = ::rustc_codegen_utils::find_library(name, &self.config.lib_search_paths, self.config.sess); self.add_archive(&location, |_| false).unwrap_or_else(|e| { self.config.sess.fatal(&format!("failed to add native library {}: {}", @@ -193,7 +172,7 @@ impl<'a> ArchiveBuilder<'a> { let name = file.file_name().unwrap().to_str().unwrap(); self.additions.push(Addition::File { path: file.to_path_buf(), - name_in_archive: name.to_string(), + name_in_archive: name.to_owned(), }); } @@ -206,13 +185,8 @@ impl<'a> ArchiveBuilder<'a> { /// Combine the provided files, rlibs, and native libraries into a single /// `Archive`. pub fn build(&mut self) { - let kind = match self.llvm_archive_kind() { - Ok(kind) => kind, - Err(kind) => { - self.config.sess.fatal(&format!("Don't know how to build archive of type: {}", - kind)); - } - }; + let kind = self.llvm_archive_kind().unwrap_or_else(|kind| + self.config.sess.fatal(&format!("Don't know how to build archive of type: {}", kind))); if let Err(e) = self.build_with_llvm(kind) { self.config.sess.fatal(&format!("failed to build archive: {}", e)); @@ -303,10 +277,9 @@ impl<'a> ArchiveBuilder<'a> { let ret = if r.into_result().is_err() { let err = llvm::LLVMRustGetLastError(); let msg = if err.is_null() { - "failed to write archive".to_string() + "failed to write archive".into() } else { String::from_utf8_lossy(CStr::from_ptr(err).to_bytes()) - .into_owned() }; Err(io::Error::new(io::ErrorKind::Other, msg)) } else { @@ -315,7 +288,7 @@ impl<'a> ArchiveBuilder<'a> { for member in members { llvm::LLVMRustArchiveMemberFree(member); } - return ret + ret } } } diff --git a/src/librustc_codegen_llvm/back/bytecode.rs b/src/librustc_codegen_llvm/back/bytecode.rs index 9a3dd9d2f88..0b264de18c1 100644 --- a/src/librustc_codegen_llvm/back/bytecode.rs +++ b/src/librustc_codegen_llvm/back/bytecode.rs @@ -42,7 +42,7 @@ use flate2::write::DeflateEncoder; // This is the "magic number" expected at the beginning of a LLVM bytecode // object in an rlib. -pub const RLIB_BYTECODE_OBJECT_MAGIC: &'static [u8] = b"RUST_OBJECT"; +pub const RLIB_BYTECODE_OBJECT_MAGIC: &[u8] = b"RUST_OBJECT"; // The version number this compiler will write to bytecode objects in rlibs pub const RLIB_BYTECODE_OBJECT_VERSION: u8 = 2; @@ -106,39 +106,39 @@ pub struct DecodedBytecode<'a> { } impl<'a> DecodedBytecode<'a> { - pub fn new(data: &'a [u8]) -> Result<DecodedBytecode<'a>, String> { + pub fn new(data: &'a [u8]) -> Result<DecodedBytecode<'a>, &'static str> { if !data.starts_with(RLIB_BYTECODE_OBJECT_MAGIC) { - return Err("magic bytecode prefix not found".to_string()) + return Err("magic bytecode prefix not found") } let data = &data[RLIB_BYTECODE_OBJECT_MAGIC.len()..]; if !data.starts_with(&[RLIB_BYTECODE_OBJECT_VERSION, 0, 0, 0]) { - return Err("wrong version prefix found in bytecode".to_string()) + return Err("wrong version prefix found in bytecode") } let data = &data[4..]; if data.len() < 4 { - return Err("bytecode corrupted".to_string()) + return Err("bytecode corrupted") } let identifier_len = unsafe { u32::from_le(ptr::read_unaligned(data.as_ptr() as *const u32)) as usize }; let data = &data[4..]; if data.len() < identifier_len { - return Err("bytecode corrupted".to_string()) + return Err("bytecode corrupted") } let identifier = match str::from_utf8(&data[..identifier_len]) { Ok(s) => s, - Err(_) => return Err("bytecode corrupted".to_string()) + Err(_) => return Err("bytecode corrupted") }; let data = &data[identifier_len..]; if data.len() < 8 { - return Err("bytecode corrupted".to_string()) + return Err("bytecode corrupted") } let bytecode_len = unsafe { u64::from_le(ptr::read_unaligned(data.as_ptr() as *const u64)) as usize }; let data = &data[8..]; if data.len() < bytecode_len { - return Err("bytecode corrupted".to_string()) + return Err("bytecode corrupted") } let encoded_bytecode = &data[..bytecode_len]; diff --git a/src/librustc_codegen_llvm/back/command.rs b/src/librustc_codegen_llvm/back/command.rs deleted file mode 100644 index 9ebbdd7c3c9..00000000000 --- a/src/librustc_codegen_llvm/back/command.rs +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A thin wrapper around `Command` in the standard library which allows us to -//! read the arguments that are built up. - -use std::ffi::{OsStr, OsString}; -use std::fmt; -use std::io; -use std::mem; -use std::process::{self, Output}; - -use rustc_target::spec::LldFlavor; - -#[derive(Clone)] -pub struct Command { - program: Program, - args: Vec<OsString>, - env: Vec<(OsString, OsString)>, -} - -#[derive(Clone)] -enum Program { - Normal(OsString), - CmdBatScript(OsString), - Lld(OsString, LldFlavor) -} - -impl Command { - pub fn new<P: AsRef<OsStr>>(program: P) -> Command { - Command::_new(Program::Normal(program.as_ref().to_owned())) - } - - pub fn bat_script<P: AsRef<OsStr>>(program: P) -> Command { - Command::_new(Program::CmdBatScript(program.as_ref().to_owned())) - } - - pub fn lld<P: AsRef<OsStr>>(program: P, flavor: LldFlavor) -> Command { - Command::_new(Program::Lld(program.as_ref().to_owned(), flavor)) - } - - fn _new(program: Program) -> Command { - Command { - program, - args: Vec::new(), - env: Vec::new(), - } - } - - pub fn arg<P: AsRef<OsStr>>(&mut self, arg: P) -> &mut Command { - self._arg(arg.as_ref()); - self - } - - pub fn args<I>(&mut self, args: I) -> &mut Command - where I: IntoIterator, - I::Item: AsRef<OsStr>, - { - for arg in args { - self._arg(arg.as_ref()); - } - self - } - - fn _arg(&mut self, arg: &OsStr) { - self.args.push(arg.to_owned()); - } - - pub fn env<K, V>(&mut self, key: K, value: V) -> &mut Command - where K: AsRef<OsStr>, - V: AsRef<OsStr> - { - self._env(key.as_ref(), value.as_ref()); - self - } - - fn _env(&mut self, key: &OsStr, value: &OsStr) { - self.env.push((key.to_owned(), value.to_owned())); - } - - pub fn output(&mut self) -> io::Result<Output> { - self.command().output() - } - - pub fn command(&self) -> process::Command { - let mut ret = match self.program { - Program::Normal(ref p) => process::Command::new(p), - Program::CmdBatScript(ref p) => { - let mut c = process::Command::new("cmd"); - c.arg("/c").arg(p); - c - } - Program::Lld(ref p, flavor) => { - let mut c = process::Command::new(p); - c.arg("-flavor").arg(match flavor { - LldFlavor::Wasm => "wasm", - LldFlavor::Ld => "gnu", - LldFlavor::Link => "link", - LldFlavor::Ld64 => "darwin", - }); - c - } - }; - ret.args(&self.args); - ret.envs(self.env.clone()); - return ret - } - - // extensions - - pub fn get_args(&self) -> &[OsString] { - &self.args - } - - pub fn take_args(&mut self) -> Vec<OsString> { - mem::replace(&mut self.args, Vec::new()) - } - - /// Returns a `true` if we're pretty sure that this'll blow OS spawn limits, - /// or `false` if we should attempt to spawn and see what the OS says. - pub fn very_likely_to_exceed_some_spawn_limit(&self) -> bool { - // We mostly only care about Windows in this method, on Unix the limits - // can be gargantuan anyway so we're pretty unlikely to hit them - if cfg!(unix) { - return false - } - - // Right now LLD doesn't support the `@` syntax of passing an argument - // through files, so regardless of the platform we try to go to the OS - // on this one. - if let Program::Lld(..) = self.program { - return false - } - - // Ok so on Windows to spawn a process is 32,768 characters in its - // command line [1]. Unfortunately we don't actually have access to that - // as it's calculated just before spawning. Instead we perform a - // poor-man's guess as to how long our command line will be. We're - // assuming here that we don't have to escape every character... - // - // Turns out though that `cmd.exe` has even smaller limits, 8192 - // characters [2]. Linkers can often be batch scripts (for example - // Emscripten, Gecko's current build system) which means that we're - // running through batch scripts. These linkers often just forward - // arguments elsewhere (and maybe tack on more), so if we blow 8192 - // bytes we'll typically cause them to blow as well. - // - // Basically as a result just perform an inflated estimate of what our - // command line will look like and test if it's > 8192 (we actually - // test against 6k to artificially inflate our estimate). If all else - // fails we'll fall back to the normal unix logic of testing the OS - // error code if we fail to spawn and automatically re-spawning the - // linker with smaller arguments. - // - // [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx - // [2]: https://blogs.msdn.microsoft.com/oldnewthing/20031210-00/?p=41553 - - let estimated_command_line_len = - self.args.iter().map(|a| a.len()).sum::<usize>(); - estimated_command_line_len > 1024 * 6 - } -} - -impl fmt::Debug for Command { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.command().fmt(f) - } -} diff --git a/src/librustc_codegen_llvm/back/link.rs b/src/librustc_codegen_llvm/back/link.rs index 9fac343b846..111637b6aa9 100644 --- a/src/librustc_codegen_llvm/back/link.rs +++ b/src/librustc_codegen_llvm/back/link.rs @@ -12,8 +12,6 @@ use back::wasm; use cc::windows_registry; use super::archive::{ArchiveBuilder, ArchiveConfig}; use super::bytecode::RLIB_BYTECODE_EXTENSION; -use super::linker::Linker; -use super::command::Command; use super::rpath::RPathConfig; use super::rpath; use metadata::METADATA_FILENAME; @@ -31,6 +29,8 @@ use rustc::hir::def_id::CrateNum; use tempfile::{Builder as TempFileBuilder, TempDir}; use rustc_target::spec::{PanicStrategy, RelroLevel, LinkerFlavor}; use rustc_data_structures::fx::FxHashSet; +use rustc_codegen_utils::linker::Linker; +use rustc_codegen_utils::command::Command; use context::get_reloc_model; use llvm; @@ -47,8 +47,8 @@ use std::str; use syntax::attr; pub use rustc_codegen_utils::link::{find_crate_name, filename_for_input, default_output_for_target, - invalid_output_for_target, out_filename, check_file_is_writeable, - filename_for_metadata}; + invalid_output_for_target, filename_for_metadata, + out_filename, check_file_is_writeable}; // The third parameter is for env vars, used on windows to set up the // path for MSVC to find its DLLs, and gcc to find its bundled @@ -107,13 +107,10 @@ pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> (PathB } pub fn remove(sess: &Session, path: &Path) { - match fs::remove_file(path) { - Ok(..) => {} - Err(e) => { - sess.err(&format!("failed to remove {}: {}", - path.display(), - e)); - } + if let Err(e) = fs::remove_file(path) { + sess.err(&format!("failed to remove {}: {}", + path.display(), + e)); } } @@ -147,9 +144,7 @@ pub(crate) fn link_binary(sess: &Session, // Remove the temporary object file and metadata if we aren't saving temps if !sess.opts.cg.save_temps { - if sess.opts.output_types.should_codegen() && - !preserve_objects_for_their_debuginfo(sess) - { + if sess.opts.output_types.should_codegen() && !preserve_objects_for_their_debuginfo(sess) { for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { remove(sess, obj); } @@ -186,7 +181,7 @@ fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool { // the objects as they're losslessly contained inside the archives. let output_linked = sess.crate_types.borrow() .iter() - .any(|x| *x != config::CrateType::Rlib && *x != config::CrateType::Staticlib); + .any(|&x| x != config::CrateType::Rlib && x != config::CrateType::Staticlib); if !output_linked { return false } @@ -270,7 +265,7 @@ pub(crate) fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) // crates providing these functions don't participate in LTO (e.g. // no_builtins or compiler builtins crates). !sess.target.target.options.no_builtins && - (info.is_no_builtins.contains(&cnum) || info.compiler_builtins == Some(cnum)) + (info.compiler_builtins == Some(cnum) || info.is_no_builtins.contains(&cnum)) } fn link_binary_output(sess: &Session, @@ -291,13 +286,10 @@ fn link_binary_output(sess: &Session, // final destination, with a `fs::rename` call. In order for the rename to // always succeed, the temporary file needs to be on the same filesystem, // which is why we create it inside the output directory specifically. - let metadata_tmpdir = match TempFileBuilder::new() + let metadata_tmpdir = TempFileBuilder::new() .prefix("rmeta") .tempdir_in(out_filename.parent().unwrap()) - { - Ok(tmpdir) => tmpdir, - Err(err) => sess.fatal(&format!("couldn't create a temp dir: {}", err)), - }; + .unwrap_or_else(|err| sess.fatal(&format!("couldn't create a temp dir: {}", err))); let metadata = emit_metadata(sess, codegen_results, &metadata_tmpdir); if let Err(e) = fs::rename(metadata, &out_filename) { sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); @@ -305,10 +297,8 @@ fn link_binary_output(sess: &Session, out_filenames.push(out_filename); } - let tmpdir = match TempFileBuilder::new().prefix("rustc").tempdir() { - Ok(tmpdir) => tmpdir, - Err(err) => sess.fatal(&format!("couldn't create a temp dir: {}", err)), - }; + let tmpdir = TempFileBuilder::new().prefix("rustc").tempdir().unwrap_or_else(|err| + sess.fatal(&format!("couldn't create a temp dir: {}", err))); if outputs.outputs.should_codegen() { let out_filename = out_filename(sess, crate_type, outputs, crate_name); @@ -342,7 +332,8 @@ fn archive_search_paths(sess: &Session) -> Vec<PathBuf> { sess.target_filesearch(PathKind::Native).for_each_lib_search_path(|path, _| { search.push(path.to_path_buf()); }); - return search; + + search } fn archive_config<'a>(sess: &'a Session, @@ -701,7 +692,8 @@ fn link_natively(sess: &Session, } { - let mut linker = codegen_results.linker_info.to_linker(cmd, &sess, flavor); + let target_cpu = ::llvm_util::target_cpu(sess); + let mut linker = codegen_results.linker_info.to_linker(cmd, &sess, flavor, target_cpu); link_args(&mut *linker, flavor, sess, crate_type, tmpdir, out_filename, codegen_results); cmd = linker.finalize(); @@ -813,8 +805,8 @@ fn link_natively(sess: &Session, .unwrap_or_else(|_| { let mut x = "Non-UTF-8 output: ".to_string(); x.extend(s.iter() - .flat_map(|&b| ascii::escape_default(b)) - .map(|b| char::from_u32(b as u32).unwrap())); + .flat_map(|&b| ascii::escape_default(b)) + .map(char::from)); x }) } @@ -869,9 +861,8 @@ fn link_natively(sess: &Session, sess.opts.debuginfo != DebugInfo::None && !preserve_objects_for_their_debuginfo(sess) { - match Command::new("dsymutil").arg(out_filename).output() { - Ok(..) => {} - Err(e) => sess.fatal(&format!("failed to run dsymutil: {}", e)), + if let Err(e) = Command::new("dsymutil").arg(out_filename).output() { + sess.fatal(&format!("failed to run dsymutil: {}", e)) } } @@ -1011,8 +1002,7 @@ fn exec_linker(sess: &Session, cmd: &mut Command, out_filename: &Path, tmpdir: & // ensure the line is interpreted as one whole argument. for c in self.arg.chars() { match c { - '\\' | - ' ' => write!(f, "\\{}", c)?, + '\\' | ' ' => write!(f, "\\{}", c)?, c => write!(f, "{}", c)?, } } @@ -1313,7 +1303,7 @@ fn add_upstream_rust_crates(cmd: &mut dyn Linker, // for the current implementation of the standard library. let mut group_end = None; let mut group_start = None; - let mut end_with = FxHashSet(); + let mut end_with = FxHashSet::default(); let info = &codegen_results.crate_info; for &(cnum, _) in deps.iter().rev() { if let Some(missing) = info.missing_lang_items.get(&cnum) { @@ -1425,7 +1415,6 @@ fn add_upstream_rust_crates(cmd: &mut dyn Linker, for f in archive.src_files() { if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME { archive.remove_file(&f); - continue } } diff --git a/src/librustc_codegen_llvm/back/linker.rs b/src/librustc_codegen_llvm/back/linker.rs deleted file mode 100644 index e18c8b9dec4..00000000000 --- a/src/librustc_codegen_llvm/back/linker.rs +++ /dev/null @@ -1,1095 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use rustc_data_structures::fx::FxHashMap; -use std::ffi::{OsStr, OsString}; -use std::fs::{self, File}; -use std::io::prelude::*; -use std::io::{self, BufWriter}; -use std::path::{Path, PathBuf}; - -use back::archive; -use back::command::Command; -use back::symbol_export; -use rustc::hir::def_id::{LOCAL_CRATE, CrateNum}; -use rustc::middle::dependency_format::Linkage; -use rustc::session::Session; -use rustc::session::config::{self, CrateType, OptLevel, DebugInfo, - CrossLangLto}; -use rustc::ty::TyCtxt; -use rustc_target::spec::{LinkerFlavor, LldFlavor}; -use serialize::{json, Encoder}; -use llvm_util; - -/// For all the linkers we support, and information they might -/// need out of the shared crate context before we get rid of it. -pub struct LinkerInfo { - exports: FxHashMap<CrateType, Vec<String>>, -} - -impl LinkerInfo { - pub fn new(tcx: TyCtxt) -> LinkerInfo { - LinkerInfo { - exports: tcx.sess.crate_types.borrow().iter().map(|&c| { - (c, exported_symbols(tcx, c)) - }).collect(), - } - } - - pub fn to_linker<'a>(&'a self, - cmd: Command, - sess: &'a Session, - flavor: LinkerFlavor) -> Box<dyn Linker+'a> { - match flavor { - LinkerFlavor::Lld(LldFlavor::Link) | - LinkerFlavor::Msvc => { - Box::new(MsvcLinker { - cmd, - sess, - info: self - }) as Box<dyn Linker> - } - LinkerFlavor::Em => { - Box::new(EmLinker { - cmd, - sess, - info: self - }) as Box<dyn Linker> - } - LinkerFlavor::Gcc => { - Box::new(GccLinker { - cmd, - sess, - info: self, - hinted_static: false, - is_ld: false, - }) as Box<dyn Linker> - } - - LinkerFlavor::Lld(LldFlavor::Ld) | - LinkerFlavor::Lld(LldFlavor::Ld64) | - LinkerFlavor::Ld => { - Box::new(GccLinker { - cmd, - sess, - info: self, - hinted_static: false, - is_ld: true, - }) as Box<dyn Linker> - } - - LinkerFlavor::Lld(LldFlavor::Wasm) => { - Box::new(WasmLd { - cmd, - sess, - info: self - }) as Box<dyn Linker> - } - } - } -} - -/// Linker abstraction used by back::link to build up the command to invoke a -/// linker. -/// -/// This trait is the total list of requirements needed by `back::link` and -/// represents the meaning of each option being passed down. This trait is then -/// used to dispatch on whether a GNU-like linker (generally `ld.exe`) or an -/// MSVC linker (e.g. `link.exe`) is being used. -pub trait Linker { - fn link_dylib(&mut self, lib: &str); - fn link_rust_dylib(&mut self, lib: &str, path: &Path); - fn link_framework(&mut self, framework: &str); - fn link_staticlib(&mut self, lib: &str); - fn link_rlib(&mut self, lib: &Path); - fn link_whole_rlib(&mut self, lib: &Path); - fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]); - fn include_path(&mut self, path: &Path); - fn framework_path(&mut self, path: &Path); - fn output_filename(&mut self, path: &Path); - fn add_object(&mut self, path: &Path); - fn gc_sections(&mut self, keep_metadata: bool); - fn position_independent_executable(&mut self); - fn no_position_independent_executable(&mut self); - fn full_relro(&mut self); - fn partial_relro(&mut self); - fn no_relro(&mut self); - fn optimize(&mut self); - fn pgo_gen(&mut self); - fn debuginfo(&mut self); - fn no_default_libraries(&mut self); - fn build_dylib(&mut self, out_filename: &Path); - fn build_static_executable(&mut self); - fn args(&mut self, args: &[String]); - fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType); - fn subsystem(&mut self, subsystem: &str); - fn group_start(&mut self); - fn group_end(&mut self); - fn cross_lang_lto(&mut self); - // Should have been finalize(self), but we don't support self-by-value on trait objects (yet?). - fn finalize(&mut self) -> Command; -} - -pub struct GccLinker<'a> { - cmd: Command, - sess: &'a Session, - info: &'a LinkerInfo, - hinted_static: bool, // Keeps track of the current hinting mode. - // Link as ld - is_ld: bool, -} - -impl<'a> GccLinker<'a> { - /// Argument that must be passed *directly* to the linker - /// - /// These arguments need to be prepended with '-Wl,' when a gcc-style linker is used - fn linker_arg<S>(&mut self, arg: S) -> &mut Self - where S: AsRef<OsStr> - { - if !self.is_ld { - let mut os = OsString::from("-Wl,"); - os.push(arg.as_ref()); - self.cmd.arg(os); - } else { - self.cmd.arg(arg); - } - self - } - - fn takes_hints(&self) -> bool { - !self.sess.target.target.options.is_like_osx - } - - // Some platforms take hints about whether a library is static or dynamic. - // For those that support this, we ensure we pass the option if the library - // was flagged "static" (most defaults are dynamic) to ensure that if - // libfoo.a and libfoo.so both exist that the right one is chosen. - fn hint_static(&mut self) { - if !self.takes_hints() { return } - if !self.hinted_static { - self.linker_arg("-Bstatic"); - self.hinted_static = true; - } - } - - fn hint_dynamic(&mut self) { - if !self.takes_hints() { return } - if self.hinted_static { - self.linker_arg("-Bdynamic"); - self.hinted_static = false; - } - } - - fn push_cross_lang_lto_args(&mut self, plugin_path: Option<&OsStr>) { - if let Some(plugin_path) = plugin_path { - let mut arg = OsString::from("-plugin="); - arg.push(plugin_path); - self.linker_arg(&arg); - } - - let opt_level = match self.sess.opts.optimize { - config::OptLevel::No => "O0", - config::OptLevel::Less => "O1", - config::OptLevel::Default => "O2", - config::OptLevel::Aggressive => "O3", - config::OptLevel::Size => "Os", - config::OptLevel::SizeMin => "Oz", - }; - - self.linker_arg(&format!("-plugin-opt={}", opt_level)); - self.linker_arg(&format!("-plugin-opt=mcpu={}", llvm_util::target_cpu(self.sess))); - - match self.sess.lto() { - config::Lto::Thin | - config::Lto::ThinLocal => { - self.linker_arg("-plugin-opt=thin"); - } - config::Lto::Fat | - config::Lto::No => { - // default to regular LTO - } - } - } -} - -impl<'a> Linker for GccLinker<'a> { - fn link_dylib(&mut self, lib: &str) { self.hint_dynamic(); self.cmd.arg(format!("-l{}",lib)); } - fn link_staticlib(&mut self, lib: &str) { - self.hint_static(); self.cmd.arg(format!("-l{}",lib)); - } - fn link_rlib(&mut self, lib: &Path) { self.hint_static(); self.cmd.arg(lib); } - fn include_path(&mut self, path: &Path) { self.cmd.arg("-L").arg(path); } - fn framework_path(&mut self, path: &Path) { self.cmd.arg("-F").arg(path); } - fn output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); } - fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } - fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); } - fn no_position_independent_executable(&mut self) { self.cmd.arg("-no-pie"); } - fn full_relro(&mut self) { self.linker_arg("-zrelro"); self.linker_arg("-znow"); } - fn partial_relro(&mut self) { self.linker_arg("-zrelro"); } - fn no_relro(&mut self) { self.linker_arg("-znorelro"); } - fn build_static_executable(&mut self) { self.cmd.arg("-static"); } - fn args(&mut self, args: &[String]) { self.cmd.args(args); } - - fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { - self.hint_dynamic(); - self.cmd.arg(format!("-l{}",lib)); - } - - fn link_framework(&mut self, framework: &str) { - self.hint_dynamic(); - self.cmd.arg("-framework").arg(framework); - } - - // Here we explicitly ask that the entire archive is included into the - // result artifact. For more details see #15460, but the gist is that - // the linker will strip away any unused objects in the archive if we - // don't otherwise explicitly reference them. This can occur for - // libraries which are just providing bindings, libraries with generic - // functions, etc. - fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]) { - self.hint_static(); - let target = &self.sess.target.target; - if !target.options.is_like_osx { - self.linker_arg("--whole-archive").cmd.arg(format!("-l{}",lib)); - self.linker_arg("--no-whole-archive"); - } else { - // -force_load is the macOS equivalent of --whole-archive, but it - // involves passing the full path to the library to link. - self.linker_arg("-force_load"); - let lib = archive::find_library(lib, search_path, &self.sess); - self.linker_arg(&lib); - } - } - - fn link_whole_rlib(&mut self, lib: &Path) { - self.hint_static(); - if self.sess.target.target.options.is_like_osx { - self.linker_arg("-force_load"); - self.linker_arg(&lib); - } else { - self.linker_arg("--whole-archive").cmd.arg(lib); - self.linker_arg("--no-whole-archive"); - } - } - - fn gc_sections(&mut self, keep_metadata: bool) { - // The dead_strip option to the linker specifies that functions and data - // unreachable by the entry point will be removed. This is quite useful - // with Rust's compilation model of compiling libraries at a time into - // one object file. For example, this brings hello world from 1.7MB to - // 458K. - // - // Note that this is done for both executables and dynamic libraries. We - // won't get much benefit from dylibs because LLVM will have already - // stripped away as much as it could. This has not been seen to impact - // link times negatively. - // - // -dead_strip can't be part of the pre_link_args because it's also used - // for partial linking when using multiple codegen units (-r). So we - // insert it here. - if self.sess.target.target.options.is_like_osx { - self.linker_arg("-dead_strip"); - } else if self.sess.target.target.options.is_like_solaris { - self.linker_arg("-zignore"); - - // If we're building a dylib, we don't use --gc-sections because LLVM - // has already done the best it can do, and we also don't want to - // eliminate the metadata. If we're building an executable, however, - // --gc-sections drops the size of hello world from 1.8MB to 597K, a 67% - // reduction. - } else if !keep_metadata { - self.linker_arg("--gc-sections"); - } - } - - fn optimize(&mut self) { - if !self.sess.target.target.options.linker_is_gnu { return } - - // GNU-style linkers support optimization with -O. GNU ld doesn't - // need a numeric argument, but other linkers do. - if self.sess.opts.optimize == config::OptLevel::Default || - self.sess.opts.optimize == config::OptLevel::Aggressive { - self.linker_arg("-O1"); - } - } - - fn pgo_gen(&mut self) { - if !self.sess.target.target.options.linker_is_gnu { return } - - // If we're doing PGO generation stuff and on a GNU-like linker, use the - // "-u" flag to properly pull in the profiler runtime bits. - // - // This is because LLVM otherwise won't add the needed initialization - // for us on Linux (though the extra flag should be harmless if it - // does). - // - // See https://reviews.llvm.org/D14033 and https://reviews.llvm.org/D14030. - // - // Though it may be worth to try to revert those changes upstream, since - // the overhead of the initialization should be minor. - self.cmd.arg("-u"); - self.cmd.arg("__llvm_profile_runtime"); - } - - fn debuginfo(&mut self) { - match self.sess.opts.debuginfo { - DebugInfo::None => { - // If we are building without debuginfo enabled and we were called with - // `-Zstrip-debuginfo-if-disabled=yes`, tell the linker to strip any debuginfo - // found when linking to get rid of symbols from libstd. - match self.sess.opts.debugging_opts.strip_debuginfo_if_disabled { - Some(true) => { self.linker_arg("-S"); }, - _ => {}, - } - }, - _ => {}, - }; - } - - fn no_default_libraries(&mut self) { - if !self.is_ld { - self.cmd.arg("-nodefaultlibs"); - } - } - - fn build_dylib(&mut self, out_filename: &Path) { - // On mac we need to tell the linker to let this library be rpathed - if self.sess.target.target.options.is_like_osx { - self.cmd.arg("-dynamiclib"); - self.linker_arg("-dylib"); - - // Note that the `osx_rpath_install_name` option here is a hack - // purely to support rustbuild right now, we should get a more - // principled solution at some point to force the compiler to pass - // the right `-Wl,-install_name` with an `@rpath` in it. - if self.sess.opts.cg.rpath || - self.sess.opts.debugging_opts.osx_rpath_install_name { - self.linker_arg("-install_name"); - let mut v = OsString::from("@rpath/"); - v.push(out_filename.file_name().unwrap()); - self.linker_arg(&v); - } - } else { - self.cmd.arg("-shared"); - } - } - - fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType) { - // If we're compiling a dylib, then we let symbol visibility in object - // files to take care of whether they're exported or not. - // - // If we're compiling a cdylib, however, we manually create a list of - // exported symbols to ensure we don't expose any more. The object files - // have far more public symbols than we actually want to export, so we - // hide them all here. - if crate_type == CrateType::Dylib || - crate_type == CrateType::ProcMacro { - return - } - - let mut arg = OsString::new(); - let path = tmpdir.join("list"); - - debug!("EXPORTED SYMBOLS:"); - - if self.sess.target.target.options.is_like_osx { - // Write a plain, newline-separated list of symbols - let res = (|| -> io::Result<()> { - let mut f = BufWriter::new(File::create(&path)?); - for sym in self.info.exports[&crate_type].iter() { - debug!(" _{}", sym); - writeln!(f, "_{}", sym)?; - } - Ok(()) - })(); - if let Err(e) = res { - self.sess.fatal(&format!("failed to write lib.def file: {}", e)); - } - } else { - // Write an LD version script - let res = (|| -> io::Result<()> { - let mut f = BufWriter::new(File::create(&path)?); - writeln!(f, "{{\n global:")?; - for sym in self.info.exports[&crate_type].iter() { - debug!(" {};", sym); - writeln!(f, " {};", sym)?; - } - writeln!(f, "\n local:\n *;\n}};")?; - Ok(()) - })(); - if let Err(e) = res { - self.sess.fatal(&format!("failed to write version script: {}", e)); - } - } - - if self.sess.target.target.options.is_like_osx { - if !self.is_ld { - arg.push("-Wl,") - } - arg.push("-exported_symbols_list,"); - } else if self.sess.target.target.options.is_like_solaris { - if !self.is_ld { - arg.push("-Wl,") - } - arg.push("-M,"); - } else { - if !self.is_ld { - arg.push("-Wl,") - } - arg.push("--version-script="); - } - - arg.push(&path); - self.cmd.arg(arg); - } - - fn subsystem(&mut self, subsystem: &str) { - self.linker_arg("--subsystem"); - self.linker_arg(&subsystem); - } - - fn finalize(&mut self) -> Command { - self.hint_dynamic(); // Reset to default before returning the composed command line. - let mut cmd = Command::new(""); - ::std::mem::swap(&mut cmd, &mut self.cmd); - cmd - } - - fn group_start(&mut self) { - if !self.sess.target.target.options.is_like_osx { - self.linker_arg("--start-group"); - } - } - - fn group_end(&mut self) { - if !self.sess.target.target.options.is_like_osx { - self.linker_arg("--end-group"); - } - } - - fn cross_lang_lto(&mut self) { - match self.sess.opts.debugging_opts.cross_lang_lto { - CrossLangLto::Disabled => { - // Nothing to do - } - CrossLangLto::LinkerPluginAuto => { - self.push_cross_lang_lto_args(None); - } - CrossLangLto::LinkerPlugin(ref path) => { - self.push_cross_lang_lto_args(Some(path.as_os_str())); - } - } - } -} - -pub struct MsvcLinker<'a> { - cmd: Command, - sess: &'a Session, - info: &'a LinkerInfo -} - -impl<'a> Linker for MsvcLinker<'a> { - fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); } - fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } - fn args(&mut self, args: &[String]) { self.cmd.args(args); } - - fn build_dylib(&mut self, out_filename: &Path) { - self.cmd.arg("/DLL"); - let mut arg: OsString = "/IMPLIB:".into(); - arg.push(out_filename.with_extension("dll.lib")); - self.cmd.arg(arg); - } - - fn build_static_executable(&mut self) { - // noop - } - - fn gc_sections(&mut self, _keep_metadata: bool) { - // MSVC's ICF (Identical COMDAT Folding) link optimization is - // slow for Rust and thus we disable it by default when not in - // optimization build. - if self.sess.opts.optimize != config::OptLevel::No { - self.cmd.arg("/OPT:REF,ICF"); - } else { - // It is necessary to specify NOICF here, because /OPT:REF - // implies ICF by default. - self.cmd.arg("/OPT:REF,NOICF"); - } - } - - fn link_dylib(&mut self, lib: &str) { - self.cmd.arg(&format!("{}.lib", lib)); - } - - fn link_rust_dylib(&mut self, lib: &str, path: &Path) { - // When producing a dll, the MSVC linker may not actually emit a - // `foo.lib` file if the dll doesn't actually export any symbols, so we - // check to see if the file is there and just omit linking to it if it's - // not present. - let name = format!("{}.dll.lib", lib); - if fs::metadata(&path.join(&name)).is_ok() { - self.cmd.arg(name); - } - } - - fn link_staticlib(&mut self, lib: &str) { - self.cmd.arg(&format!("{}.lib", lib)); - } - - fn position_independent_executable(&mut self) { - // noop - } - - fn no_position_independent_executable(&mut self) { - // noop - } - - fn full_relro(&mut self) { - // noop - } - - fn partial_relro(&mut self) { - // noop - } - - fn no_relro(&mut self) { - // noop - } - - fn no_default_libraries(&mut self) { - // Currently we don't pass the /NODEFAULTLIB flag to the linker on MSVC - // as there's been trouble in the past of linking the C++ standard - // library required by LLVM. This likely needs to happen one day, but - // in general Windows is also a more controlled environment than - // Unix, so it's not necessarily as critical that this be implemented. - // - // Note that there are also some licensing worries about statically - // linking some libraries which require a specific agreement, so it may - // not ever be possible for us to pass this flag. - } - - fn include_path(&mut self, path: &Path) { - let mut arg = OsString::from("/LIBPATH:"); - arg.push(path); - self.cmd.arg(&arg); - } - - fn output_filename(&mut self, path: &Path) { - let mut arg = OsString::from("/OUT:"); - arg.push(path); - self.cmd.arg(&arg); - } - - fn framework_path(&mut self, _path: &Path) { - bug!("frameworks are not supported on windows") - } - fn link_framework(&mut self, _framework: &str) { - bug!("frameworks are not supported on windows") - } - - fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) { - // not supported? - self.link_staticlib(lib); - } - fn link_whole_rlib(&mut self, path: &Path) { - // not supported? - self.link_rlib(path); - } - fn optimize(&mut self) { - // Needs more investigation of `/OPT` arguments - } - - fn pgo_gen(&mut self) { - // Nothing needed here. - } - - fn debuginfo(&mut self) { - // This will cause the Microsoft linker to generate a PDB file - // from the CodeView line tables in the object files. - self.cmd.arg("/DEBUG"); - - // This will cause the Microsoft linker to embed .natvis info into the the PDB file - let sysroot = self.sess.sysroot(); - let natvis_dir_path = sysroot.join("lib\\rustlib\\etc"); - if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) { - // LLVM 5.0.0's lld-link frontend doesn't yet recognize, and chokes - // on, the /NATVIS:... flags. LLVM 6 (or earlier) should at worst ignore - // them, eventually mooting this workaround, per this landed patch: - // https://github.com/llvm-mirror/lld/commit/27b9c4285364d8d76bb43839daa100 - if let Some(ref linker_path) = self.sess.opts.cg.linker { - if let Some(linker_name) = Path::new(&linker_path).file_stem() { - if linker_name.to_str().unwrap().to_lowercase() == "lld-link" { - self.sess.warn("not embedding natvis: lld-link may not support the flag"); - return; - } - } - } - for entry in natvis_dir { - match entry { - Ok(entry) => { - let path = entry.path(); - if path.extension() == Some("natvis".as_ref()) { - let mut arg = OsString::from("/NATVIS:"); - arg.push(path); - self.cmd.arg(arg); - } - }, - Err(err) => { - self.sess.warn(&format!("error enumerating natvis directory: {}", err)); - }, - } - } - } - } - - // Currently the compiler doesn't use `dllexport` (an LLVM attribute) to - // export symbols from a dynamic library. When building a dynamic library, - // however, we're going to want some symbols exported, so this function - // generates a DEF file which lists all the symbols. - // - // The linker will read this `*.def` file and export all the symbols from - // the dynamic library. Note that this is not as simple as just exporting - // all the symbols in the current crate (as specified by `codegen.reachable`) - // but rather we also need to possibly export the symbols of upstream - // crates. Upstream rlibs may be linked statically to this dynamic library, - // in which case they may continue to transitively be used and hence need - // their symbols exported. - fn export_symbols(&mut self, - tmpdir: &Path, - crate_type: CrateType) { - let path = tmpdir.join("lib.def"); - let res = (|| -> io::Result<()> { - let mut f = BufWriter::new(File::create(&path)?); - - // Start off with the standard module name header and then go - // straight to exports. - writeln!(f, "LIBRARY")?; - writeln!(f, "EXPORTS")?; - for symbol in self.info.exports[&crate_type].iter() { - debug!(" _{}", symbol); - writeln!(f, " {}", symbol)?; - } - Ok(()) - })(); - if let Err(e) = res { - self.sess.fatal(&format!("failed to write lib.def file: {}", e)); - } - let mut arg = OsString::from("/DEF:"); - arg.push(path); - self.cmd.arg(&arg); - } - - fn subsystem(&mut self, subsystem: &str) { - // Note that previous passes of the compiler validated this subsystem, - // so we just blindly pass it to the linker. - self.cmd.arg(&format!("/SUBSYSTEM:{}", subsystem)); - - // Windows has two subsystems we're interested in right now, the console - // and windows subsystems. These both implicitly have different entry - // points (starting symbols). The console entry point starts with - // `mainCRTStartup` and the windows entry point starts with - // `WinMainCRTStartup`. These entry points, defined in system libraries, - // will then later probe for either `main` or `WinMain`, respectively to - // start the application. - // - // In Rust we just always generate a `main` function so we want control - // to always start there, so we force the entry point on the windows - // subsystem to be `mainCRTStartup` to get everything booted up - // correctly. - // - // For more information see RFC #1665 - if subsystem == "windows" { - self.cmd.arg("/ENTRY:mainCRTStartup"); - } - } - - fn finalize(&mut self) -> Command { - let mut cmd = Command::new(""); - ::std::mem::swap(&mut cmd, &mut self.cmd); - cmd - } - - // MSVC doesn't need group indicators - fn group_start(&mut self) {} - fn group_end(&mut self) {} - - fn cross_lang_lto(&mut self) { - // Do nothing - } -} - -pub struct EmLinker<'a> { - cmd: Command, - sess: &'a Session, - info: &'a LinkerInfo -} - -impl<'a> Linker for EmLinker<'a> { - fn include_path(&mut self, path: &Path) { - self.cmd.arg("-L").arg(path); - } - - fn link_staticlib(&mut self, lib: &str) { - self.cmd.arg("-l").arg(lib); - } - - fn output_filename(&mut self, path: &Path) { - self.cmd.arg("-o").arg(path); - } - - fn add_object(&mut self, path: &Path) { - self.cmd.arg(path); - } - - fn link_dylib(&mut self, lib: &str) { - // Emscripten always links statically - self.link_staticlib(lib); - } - - fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) { - // not supported? - self.link_staticlib(lib); - } - - fn link_whole_rlib(&mut self, lib: &Path) { - // not supported? - self.link_rlib(lib); - } - - fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { - self.link_dylib(lib); - } - - fn link_rlib(&mut self, lib: &Path) { - self.add_object(lib); - } - - fn position_independent_executable(&mut self) { - // noop - } - - fn no_position_independent_executable(&mut self) { - // noop - } - - fn full_relro(&mut self) { - // noop - } - - fn partial_relro(&mut self) { - // noop - } - - fn no_relro(&mut self) { - // noop - } - - fn args(&mut self, args: &[String]) { - self.cmd.args(args); - } - - fn framework_path(&mut self, _path: &Path) { - bug!("frameworks are not supported on Emscripten") - } - - fn link_framework(&mut self, _framework: &str) { - bug!("frameworks are not supported on Emscripten") - } - - fn gc_sections(&mut self, _keep_metadata: bool) { - // noop - } - - fn optimize(&mut self) { - // Emscripten performs own optimizations - self.cmd.arg(match self.sess.opts.optimize { - OptLevel::No => "-O0", - OptLevel::Less => "-O1", - OptLevel::Default => "-O2", - OptLevel::Aggressive => "-O3", - OptLevel::Size => "-Os", - OptLevel::SizeMin => "-Oz" - }); - // Unusable until https://github.com/rust-lang/rust/issues/38454 is resolved - self.cmd.args(&["--memory-init-file", "0"]); - } - - fn pgo_gen(&mut self) { - // noop, but maybe we need something like the gnu linker? - } - - fn debuginfo(&mut self) { - // Preserve names or generate source maps depending on debug info - self.cmd.arg(match self.sess.opts.debuginfo { - DebugInfo::None => "-g0", - DebugInfo::Limited => "-g3", - DebugInfo::Full => "-g4" - }); - } - - fn no_default_libraries(&mut self) { - self.cmd.args(&["-s", "DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[]"]); - } - - fn build_dylib(&mut self, _out_filename: &Path) { - bug!("building dynamic library is unsupported on Emscripten") - } - - fn build_static_executable(&mut self) { - // noop - } - - fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) { - let symbols = &self.info.exports[&crate_type]; - - debug!("EXPORTED SYMBOLS:"); - - self.cmd.arg("-s"); - - let mut arg = OsString::from("EXPORTED_FUNCTIONS="); - let mut encoded = String::new(); - - { - let mut encoder = json::Encoder::new(&mut encoded); - let res = encoder.emit_seq(symbols.len(), |encoder| { - for (i, sym) in symbols.iter().enumerate() { - encoder.emit_seq_elt(i, |encoder| { - encoder.emit_str(&("_".to_string() + sym)) - })?; - } - Ok(()) - }); - if let Err(e) = res { - self.sess.fatal(&format!("failed to encode exported symbols: {}", e)); - } - } - debug!("{}", encoded); - arg.push(encoded); - - self.cmd.arg(arg); - } - - fn subsystem(&mut self, _subsystem: &str) { - // noop - } - - fn finalize(&mut self) -> Command { - let mut cmd = Command::new(""); - ::std::mem::swap(&mut cmd, &mut self.cmd); - cmd - } - - // Appears not necessary on Emscripten - fn group_start(&mut self) {} - fn group_end(&mut self) {} - - fn cross_lang_lto(&mut self) { - // Do nothing - } -} - -fn exported_symbols(tcx: TyCtxt, crate_type: CrateType) -> Vec<String> { - let mut symbols = Vec::new(); - - let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); - for &(symbol, level) in tcx.exported_symbols(LOCAL_CRATE).iter() { - if level.is_below_threshold(export_threshold) { - symbols.push(symbol.symbol_name(tcx).to_string()); - } - } - - let formats = tcx.sess.dependency_formats.borrow(); - let deps = formats[&crate_type].iter(); - - for (index, dep_format) in deps.enumerate() { - let cnum = CrateNum::new(index + 1); - // For each dependency that we are linking to statically ... - if *dep_format == Linkage::Static { - // ... we add its symbol list to our export list. - for &(symbol, level) in tcx.exported_symbols(cnum).iter() { - if level.is_below_threshold(export_threshold) { - symbols.push(symbol.symbol_name(tcx).to_string()); - } - } - } - } - - symbols -} - -pub struct WasmLd<'a> { - cmd: Command, - sess: &'a Session, - info: &'a LinkerInfo, -} - -impl<'a> Linker for WasmLd<'a> { - fn link_dylib(&mut self, lib: &str) { - self.cmd.arg("-l").arg(lib); - } - - fn link_staticlib(&mut self, lib: &str) { - self.cmd.arg("-l").arg(lib); - } - - fn link_rlib(&mut self, lib: &Path) { - self.cmd.arg(lib); - } - - fn include_path(&mut self, path: &Path) { - self.cmd.arg("-L").arg(path); - } - - fn framework_path(&mut self, _path: &Path) { - panic!("frameworks not supported") - } - - fn output_filename(&mut self, path: &Path) { - self.cmd.arg("-o").arg(path); - } - - fn add_object(&mut self, path: &Path) { - self.cmd.arg(path); - } - - fn position_independent_executable(&mut self) { - } - - fn full_relro(&mut self) { - } - - fn partial_relro(&mut self) { - } - - fn no_relro(&mut self) { - } - - fn build_static_executable(&mut self) { - } - - fn args(&mut self, args: &[String]) { - self.cmd.args(args); - } - - fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { - self.cmd.arg("-l").arg(lib); - } - - fn link_framework(&mut self, _framework: &str) { - panic!("frameworks not supported") - } - - fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) { - self.cmd.arg("-l").arg(lib); - } - - fn link_whole_rlib(&mut self, lib: &Path) { - self.cmd.arg(lib); - } - - fn gc_sections(&mut self, _keep_metadata: bool) { - self.cmd.arg("--gc-sections"); - } - - fn optimize(&mut self) { - self.cmd.arg(match self.sess.opts.optimize { - OptLevel::No => "-O0", - OptLevel::Less => "-O1", - OptLevel::Default => "-O2", - OptLevel::Aggressive => "-O3", - // Currently LLD doesn't support `Os` and `Oz`, so pass through `O2` - // instead. - OptLevel::Size => "-O2", - OptLevel::SizeMin => "-O2" - }); - } - - fn pgo_gen(&mut self) { - } - - fn debuginfo(&mut self) { - } - - fn no_default_libraries(&mut self) { - } - - fn build_dylib(&mut self, _out_filename: &Path) { - } - - fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) { - for sym in self.info.exports[&crate_type].iter() { - self.cmd.arg("--export").arg(&sym); - } - } - - fn subsystem(&mut self, _subsystem: &str) { - } - - fn no_position_independent_executable(&mut self) { - } - - fn finalize(&mut self) -> Command { - // There have been reports in the wild (rustwasm/wasm-bindgen#119) of - // using threads causing weird hangs and bugs. Disable it entirely as - // this isn't yet the bottleneck of compilation at all anyway. - self.cmd.arg("--no-threads"); - - // By default LLD only gives us one page of stack (64k) which is a - // little small. Default to a larger stack closer to other PC platforms - // (1MB) and users can always inject their own link-args to override this. - self.cmd.arg("-z").arg("stack-size=1048576"); - - // By default LLD's memory layout is: - // - // 1. First, a blank page - // 2. Next, all static data - // 3. Finally, the main stack (which grows down) - // - // This has the unfortunate consequence that on stack overflows you - // corrupt static data and can cause some exceedingly weird bugs. To - // help detect this a little sooner we instead request that the stack is - // placed before static data. - // - // This means that we'll generate slightly larger binaries as references - // to static data will take more bytes in the ULEB128 encoding, but - // stack overflow will be guaranteed to trap as it underflows instead of - // corrupting static data. - self.cmd.arg("--stack-first"); - - // FIXME we probably shouldn't pass this but instead pass an explicit - // whitelist of symbols we'll allow to be undefined. Unfortunately - // though we can't handle symbols like `log10` that LLVM injects at a - // super late date without actually parsing object files. For now let's - // stick to this and hopefully fix it before stabilization happens. - self.cmd.arg("--allow-undefined"); - - // For now we just never have an entry symbol - self.cmd.arg("--no-entry"); - - // Make the default table accessible - self.cmd.arg("--export-table"); - - // Rust code should never have warnings, and warnings are often - // indicative of bugs, let's prevent them. - self.cmd.arg("--fatal-warnings"); - - let mut cmd = Command::new(""); - ::std::mem::swap(&mut cmd, &mut self.cmd); - cmd - } - - // Not needed for now with LLD - fn group_start(&mut self) {} - fn group_end(&mut self) {} - - fn cross_lang_lto(&mut self) { - // Do nothing for now - } -} diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs index 3ac22f4eaef..60b06c579cb 100644 --- a/src/librustc_codegen_llvm/back/lto.rs +++ b/src/librustc_codegen_llvm/back/lto.rs @@ -9,7 +9,6 @@ // except according to those terms. use back::bytecode::{DecodedBytecode, RLIB_BYTECODE_EXTENSION}; -use back::symbol_export; use back::write::{ModuleConfig, with_llvm_pmb, CodegenContext}; use back::write::{self, DiagnosticHandlers, pre_lto_bitcode_filename}; use errors::{FatalError, Handler}; @@ -24,6 +23,7 @@ use rustc::middle::exported_symbols::SymbolExportLevel; use rustc::session::config::{self, Lto}; use rustc::util::common::time_ext; use rustc_data_structures::fx::FxHashMap; +use rustc_codegen_utils::symbol_export; use time_graph::Timeline; use {ModuleCodegen, ModuleLlvm, ModuleKind}; @@ -205,11 +205,11 @@ pub(crate) fn run(cgcx: &CodegenContext, Lto::Fat => { assert!(cached_modules.is_empty()); let opt_jobs = fat_lto(cgcx, - &diag_handler, - modules, - upstream_modules, - &symbol_white_list, - timeline); + &diag_handler, + modules, + upstream_modules, + &symbol_white_list, + timeline); opt_jobs.map(|opt_jobs| (opt_jobs, vec![])) } Lto::Thin | @@ -296,7 +296,7 @@ fn fat_lto(cgcx: &CodegenContext, let data = bc_decoded.data(); linker.add(&data).map_err(|()| { let msg = format!("failed to load bc of {:?}", name); - write::llvm_err(&diag_handler, msg) + write::llvm_err(&diag_handler, &msg) }) })?; timeline.record(&format!("link {:?}", name)); @@ -310,8 +310,8 @@ fn fat_lto(cgcx: &CodegenContext, unsafe { let ptr = symbol_white_list.as_ptr(); llvm::LLVMRustRunRestrictionPass(llmod, - ptr as *const *const libc::c_char, - symbol_white_list.len() as libc::size_t); + ptr as *const *const libc::c_char, + symbol_white_list.len() as libc::size_t); cgcx.save_temp_bitcode(&module, "lto.after-restriction"); } @@ -490,7 +490,7 @@ fn thin_lto(cgcx: &CodegenContext, symbol_white_list.as_ptr(), symbol_white_list.len() as u32, ).ok_or_else(|| { - write::llvm_err(&diag_handler, "failed to prepare thin LTO context".to_string()) + write::llvm_err(&diag_handler, "failed to prepare thin LTO context") })?; info!("thin LTO data created"); @@ -502,7 +502,7 @@ fn thin_lto(cgcx: &CodegenContext, // If we don't compile incrementally, we don't need to load the // import data from LLVM. assert!(green_modules.is_empty()); - ThinLTOImports::new() + ThinLTOImports::default() }; info!("thin LTO import map loaded"); timeline.record("import-map-loaded"); @@ -595,9 +595,7 @@ fn run_pass_manager(cgcx: &CodegenContext, }; with_llvm_pmb(llmod, config, opt_level, false, &mut |b| { if thin { - if !llvm::LLVMRustPassManagerBuilderPopulateThinLTOPassManager(b, pm) { - panic!("this version of LLVM does not support ThinLTO"); - } + llvm::LLVMRustPassManagerBuilderPopulateThinLTOPassManager(b, pm); } else { llvm::LLVMPassManagerBuilderPopulateLTOPassManager(b, pm, /* Internalize = */ False, @@ -605,13 +603,19 @@ fn run_pass_manager(cgcx: &CodegenContext, } }); + // We always generate bitcode through ThinLTOBuffers, + // which do not support anonymous globals + if config.bitcode_needed() { + let pass = llvm::LLVMRustFindAndCreatePass("name-anon-globals\0".as_ptr() as *const _); + llvm::LLVMRustAddPass(pm, pass.unwrap()); + } + if config.verify_llvm_ir { let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _); llvm::LLVMRustAddPass(pm, pass.unwrap()); } - time_ext(cgcx.time_passes, None, "LTO passes", || - llvm::LLVMRunPassManager(pm, llmod)); + time_ext(cgcx.time_passes, None, "LTO passes", || llvm::LLVMRunPassManager(pm, llmod)); llvm::LLVMDisposePassManager(pm); } @@ -740,7 +744,7 @@ impl ThinModule { { let diag_handler = cgcx.create_diag_handler(); let tm = (cgcx.tm_factory)().map_err(|e| { - write::llvm_err(&diag_handler, e) + write::llvm_err(&diag_handler, &e) })?; // Right now the implementation we've got only works over serialized @@ -755,7 +759,7 @@ impl ThinModule { self.data().len(), self.shared.module_names[self.idx].as_ptr(), ).ok_or_else(|| { - let msg = "failed to parse bitcode for thin LTO module".to_string(); + let msg = "failed to parse bitcode for thin LTO module"; write::llvm_err(&diag_handler, msg) })? as *const _; let module = ModuleCodegen { @@ -779,7 +783,7 @@ impl ThinModule { let mut cu2 = ptr::null_mut(); llvm::LLVMRustThinLTOGetDICompileUnit(llmod, &mut cu1, &mut cu2); if !cu2.is_null() { - let msg = "multiple source DICompileUnits found".to_string(); + let msg = "multiple source DICompileUnits found"; return Err(write::llvm_err(&diag_handler, msg)) } @@ -800,25 +804,25 @@ impl ThinModule { // You can find some more comments about these functions in the LLVM // bindings we've got (currently `PassWrapper.cpp`) if !llvm::LLVMRustPrepareThinLTORename(self.shared.data.0, llmod) { - let msg = "failed to prepare thin LTO module".to_string(); + let msg = "failed to prepare thin LTO module"; return Err(write::llvm_err(&diag_handler, msg)) } cgcx.save_temp_bitcode(&module, "thin-lto-after-rename"); timeline.record("rename"); if !llvm::LLVMRustPrepareThinLTOResolveWeak(self.shared.data.0, llmod) { - let msg = "failed to prepare thin LTO module".to_string(); + let msg = "failed to prepare thin LTO module"; return Err(write::llvm_err(&diag_handler, msg)) } cgcx.save_temp_bitcode(&module, "thin-lto-after-resolve"); timeline.record("resolve"); if !llvm::LLVMRustPrepareThinLTOInternalize(self.shared.data.0, llmod) { - let msg = "failed to prepare thin LTO module".to_string(); + let msg = "failed to prepare thin LTO module"; return Err(write::llvm_err(&diag_handler, msg)) } cgcx.save_temp_bitcode(&module, "thin-lto-after-internalize"); timeline.record("internalize"); if !llvm::LLVMRustPrepareThinLTOImport(self.shared.data.0, llmod) { - let msg = "failed to prepare thin LTO module".to_string(); + let msg = "failed to prepare thin LTO module"; return Err(write::llvm_err(&diag_handler, msg)) } cgcx.save_temp_bitcode(&module, "thin-lto-after-import"); @@ -873,19 +877,13 @@ impl ThinModule { } } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct ThinLTOImports { // key = llvm name of importing module, value = list of modules it imports from imports: FxHashMap<String, Vec<String>>, } impl ThinLTOImports { - fn new() -> ThinLTOImports { - ThinLTOImports { - imports: FxHashMap(), - } - } - fn modules_imported_by(&self, llvm_module_name: &str) -> &[String] { self.imports.get(llvm_module_name).map(|v| &v[..]).unwrap_or(&[]) } @@ -910,9 +908,7 @@ impl ThinLTOImports { .unwrap() .push(imported_module_name.to_owned()); } - let mut map = ThinLTOImports { - imports: FxHashMap(), - }; + let mut map = ThinLTOImports::default(); llvm::LLVMRustGetThinLTOModuleImports(data, imported_module_callback, &mut map as *mut _ as *mut libc::c_void); @@ -921,12 +917,6 @@ impl ThinLTOImports { } fn module_name_to_str(c_str: &CStr) -> &str { - match c_str.to_str() { - Ok(s) => s, - Err(e) => { - bug!("Encountered non-utf8 LLVM module name `{}`: {}", - c_str.to_string_lossy(), - e) - } - } + c_str.to_str().unwrap_or_else(|e| + bug!("Encountered non-utf8 LLVM module name `{}`: {}", c_str.to_string_lossy(), e)) } diff --git a/src/librustc_codegen_llvm/back/rpath.rs b/src/librustc_codegen_llvm/back/rpath.rs index aa4f7688b0f..ee4a9b30a1a 100644 --- a/src/librustc_codegen_llvm/back/rpath.rs +++ b/src/librustc_codegen_llvm/back/rpath.rs @@ -42,7 +42,7 @@ pub fn get_rpath_flags(config: &mut RPathConfig) -> Vec<String> { // Use DT_RUNPATH instead of DT_RPATH if available if config.linker_is_gnu { - flags.push("-Wl,--enable-new-dtags".to_string()); + flags.push("-Wl,--enable-new-dtags".to_owned()); } flags @@ -59,7 +59,8 @@ fn rpaths_to_flags(rpaths: &[String]) -> Vec<String> { ret.push(format!("-Wl,-rpath,{}", &(*rpath))); } } - return ret; + + ret } fn get_rpaths(config: &mut RPathConfig, libs: &[PathBuf]) -> Vec<String> { @@ -92,7 +93,8 @@ fn get_rpaths(config: &mut RPathConfig, libs: &[PathBuf]) -> Vec<String> { // Remove duplicates let rpaths = minimize_rpaths(&rpaths); - return rpaths; + + rpaths } fn get_rpaths_relative_to_output(config: &mut RPathConfig, @@ -109,7 +111,7 @@ fn get_rpath_relative_to_output(config: &mut RPathConfig, lib: &Path) -> String }; let cwd = env::current_dir().unwrap(); - let mut lib = fs::canonicalize(&cwd.join(lib)).unwrap_or(cwd.join(lib)); + let mut lib = fs::canonicalize(&cwd.join(lib)).unwrap_or_else(|_| cwd.join(lib)); lib.pop(); let mut output = cwd.join(&config.out_filename); output.pop(); @@ -117,8 +119,7 @@ fn get_rpath_relative_to_output(config: &mut RPathConfig, lib: &Path) -> String let relative = path_relative_from(&lib, &output).unwrap_or_else(|| panic!("couldn't create relative path from {:?} to {:?}", output, lib)); // FIXME (#9639): This needs to handle non-utf8 paths - format!("{}/{}", prefix, - relative.to_str().expect("non-utf8 component in path")) + format!("{}/{}", prefix, relative.to_str().expect("non-utf8 component in path")) } // This routine is adapted from the *old* Path's `path_relative_from` @@ -168,7 +169,7 @@ fn get_install_prefix_rpath(config: &mut RPathConfig) -> String { let path = (config.get_install_prefix_lib_path)(); let path = env::current_dir().unwrap().join(&path); // FIXME (#9639): This needs to handle non-utf8 paths - path.to_str().expect("non-utf8 component in rpath").to_string() + path.to_str().expect("non-utf8 component in rpath").to_owned() } fn minimize_rpaths(rpaths: &[String]) -> Vec<String> { diff --git a/src/librustc_codegen_llvm/back/symbol_export.rs b/src/librustc_codegen_llvm/back/symbol_export.rs deleted file mode 100644 index 6b1b0b94fd9..00000000000 --- a/src/librustc_codegen_llvm/back/symbol_export.rs +++ /dev/null @@ -1,395 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use rustc_data_structures::sync::Lrc; -use std::sync::Arc; - -use monomorphize::Instance; -use rustc::hir; -use rustc::hir::Node; -use rustc::hir::CodegenFnAttrFlags; -use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE, CRATE_DEF_INDEX}; -use rustc_data_structures::fingerprint::Fingerprint; -use rustc::middle::exported_symbols::{SymbolExportLevel, ExportedSymbol, metadata_symbol_name}; -use rustc::session::config; -use rustc::ty::{TyCtxt, SymbolName}; -use rustc::ty::query::Providers; -use rustc::ty::subst::Substs; -use rustc::util::nodemap::{FxHashMap, DefIdMap}; -use rustc_allocator::ALLOCATOR_METHODS; -use rustc_data_structures::indexed_vec::IndexVec; -use std::collections::hash_map::Entry::*; - -pub type ExportedSymbols = FxHashMap< - CrateNum, - Arc<Vec<(String, SymbolExportLevel)>>, ->; - -pub fn threshold(tcx: TyCtxt) -> SymbolExportLevel { - crates_export_threshold(&tcx.sess.crate_types.borrow()) -} - -fn crate_export_threshold(crate_type: config::CrateType) -> SymbolExportLevel { - match crate_type { - config::CrateType::Executable | - config::CrateType::Staticlib | - config::CrateType::ProcMacro | - config::CrateType::Cdylib => SymbolExportLevel::C, - config::CrateType::Rlib | - config::CrateType::Dylib => SymbolExportLevel::Rust, - } -} - -pub fn crates_export_threshold(crate_types: &[config::CrateType]) - -> SymbolExportLevel { - if crate_types.iter().any(|&crate_type| { - crate_export_threshold(crate_type) == SymbolExportLevel::Rust - }) { - SymbolExportLevel::Rust - } else { - SymbolExportLevel::C - } -} - -fn reachable_non_generics_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - cnum: CrateNum) - -> Lrc<DefIdMap<SymbolExportLevel>> -{ - assert_eq!(cnum, LOCAL_CRATE); - - if !tcx.sess.opts.output_types.should_codegen() { - return Lrc::new(DefIdMap()) - } - - // Check to see if this crate is a "special runtime crate". These - // crates, implementation details of the standard library, typically - // have a bunch of `pub extern` and `#[no_mangle]` functions as the - // ABI between them. We don't want their symbols to have a `C` - // export level, however, as they're just implementation details. - // Down below we'll hardwire all of the symbols to the `Rust` export - // level instead. - let special_runtime_crate = tcx.is_panic_runtime(LOCAL_CRATE) || - tcx.is_compiler_builtins(LOCAL_CRATE); - - let mut reachable_non_generics: DefIdMap<_> = tcx.reachable_set(LOCAL_CRATE).0 - .iter() - .filter_map(|&node_id| { - // We want to ignore some FFI functions that are not exposed from - // this crate. Reachable FFI functions can be lumped into two - // categories: - // - // 1. Those that are included statically via a static library - // 2. Those included otherwise (e.g. dynamically or via a framework) - // - // Although our LLVM module is not literally emitting code for the - // statically included symbols, it's an export of our library which - // needs to be passed on to the linker and encoded in the metadata. - // - // As a result, if this id is an FFI item (foreign item) then we only - // let it through if it's included statically. - match tcx.hir.get(node_id) { - Node::ForeignItem(..) => { - let def_id = tcx.hir.local_def_id(node_id); - if tcx.is_statically_included_foreign_item(def_id) { - Some(def_id) - } else { - None - } - } - - // Only consider nodes that actually have exported symbols. - Node::Item(&hir::Item { - node: hir::ItemKind::Static(..), - .. - }) | - Node::Item(&hir::Item { - node: hir::ItemKind::Fn(..), .. - }) | - Node::ImplItem(&hir::ImplItem { - node: hir::ImplItemKind::Method(..), - .. - }) => { - let def_id = tcx.hir.local_def_id(node_id); - let generics = tcx.generics_of(def_id); - if !generics.requires_monomorphization(tcx) && - // Functions marked with #[inline] are only ever codegened - // with "internal" linkage and are never exported. - !Instance::mono(tcx, def_id).def.requires_local(tcx) { - Some(def_id) - } else { - None - } - } - - _ => None - } - }) - .map(|def_id| { - let export_level = if special_runtime_crate { - let name = tcx.symbol_name(Instance::mono(tcx, def_id)).as_str(); - // We can probably do better here by just ensuring that - // it has hidden visibility rather than public - // visibility, as this is primarily here to ensure it's - // not stripped during LTO. - // - // In general though we won't link right if these - // symbols are stripped, and LTO currently strips them. - if &*name == "rust_eh_personality" || - &*name == "rust_eh_register_frames" || - &*name == "rust_eh_unregister_frames" { - SymbolExportLevel::C - } else { - SymbolExportLevel::Rust - } - } else { - symbol_export_level(tcx, def_id) - }; - debug!("EXPORTED SYMBOL (local): {} ({:?})", - tcx.symbol_name(Instance::mono(tcx, def_id)), - export_level); - (def_id, export_level) - }) - .collect(); - - if let Some(id) = *tcx.sess.derive_registrar_fn.get() { - let def_id = tcx.hir.local_def_id(id); - reachable_non_generics.insert(def_id, SymbolExportLevel::C); - } - - if let Some(id) = *tcx.sess.plugin_registrar_fn.get() { - let def_id = tcx.hir.local_def_id(id); - reachable_non_generics.insert(def_id, SymbolExportLevel::C); - } - - Lrc::new(reachable_non_generics) -} - -fn is_reachable_non_generic_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) - -> bool { - let export_threshold = threshold(tcx); - - if let Some(&level) = tcx.reachable_non_generics(def_id.krate).get(&def_id) { - level.is_below_threshold(export_threshold) - } else { - false - } -} - -fn is_reachable_non_generic_provider_extern<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) - -> bool { - tcx.reachable_non_generics(def_id.krate).contains_key(&def_id) -} - -fn exported_symbols_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - cnum: CrateNum) - -> Arc<Vec<(ExportedSymbol<'tcx>, - SymbolExportLevel)>> -{ - assert_eq!(cnum, LOCAL_CRATE); - - if !tcx.sess.opts.output_types.should_codegen() { - return Arc::new(vec![]) - } - - let mut symbols: Vec<_> = tcx.reachable_non_generics(LOCAL_CRATE) - .iter() - .map(|(&def_id, &level)| { - (ExportedSymbol::NonGeneric(def_id), level) - }) - .collect(); - - if tcx.sess.entry_fn.borrow().is_some() { - let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new("main")); - - symbols.push((exported_symbol, SymbolExportLevel::C)); - } - - if tcx.sess.allocator_kind.get().is_some() { - for method in ALLOCATOR_METHODS { - let symbol_name = format!("__rust_{}", method.name); - let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(&symbol_name)); - - symbols.push((exported_symbol, SymbolExportLevel::Rust)); - } - } - - if tcx.sess.opts.debugging_opts.pgo_gen.is_some() { - // These are weak symbols that point to the profile version and the - // profile name, which need to be treated as exported so LTO doesn't nix - // them. - const PROFILER_WEAK_SYMBOLS: [&'static str; 2] = [ - "__llvm_profile_raw_version", - "__llvm_profile_filename", - ]; - for sym in &PROFILER_WEAK_SYMBOLS { - let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(sym)); - symbols.push((exported_symbol, SymbolExportLevel::C)); - } - } - - if tcx.sess.crate_types.borrow().contains(&config::CrateType::Dylib) { - let symbol_name = metadata_symbol_name(tcx); - let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(&symbol_name)); - - symbols.push((exported_symbol, SymbolExportLevel::Rust)); - } - - if tcx.sess.opts.share_generics() && tcx.local_crate_exports_generics() { - use rustc::mir::mono::{Linkage, Visibility, MonoItem}; - use rustc::ty::InstanceDef; - - // Normally, we require that shared monomorphizations are not hidden, - // because if we want to re-use a monomorphization from a Rust dylib, it - // needs to be exported. - // However, on platforms that don't allow for Rust dylibs, having - // external linkage is enough for monomorphization to be linked to. - let need_visibility = tcx.sess.target.target.options.dynamic_linking && - !tcx.sess.target.target.options.only_cdylib; - - let (_, cgus) = tcx.collect_and_partition_mono_items(LOCAL_CRATE); - - for (mono_item, &(linkage, visibility)) in cgus.iter() - .flat_map(|cgu| cgu.items().iter()) { - if linkage != Linkage::External { - // We can only re-use things with external linkage, otherwise - // we'll get a linker error - continue - } - - if need_visibility && visibility == Visibility::Hidden { - // If we potentially share things from Rust dylibs, they must - // not be hidden - continue - } - - if let &MonoItem::Fn(Instance { - def: InstanceDef::Item(def_id), - substs, - }) = mono_item { - if substs.types().next().is_some() { - symbols.push((ExportedSymbol::Generic(def_id, substs), - SymbolExportLevel::Rust)); - } - } - } - } - - // Sort so we get a stable incr. comp. hash. - symbols.sort_unstable_by(|&(ref symbol1, ..), &(ref symbol2, ..)| { - symbol1.compare_stable(tcx, symbol2) - }); - - Arc::new(symbols) -} - -fn upstream_monomorphizations_provider<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - cnum: CrateNum) - -> Lrc<DefIdMap<Lrc<FxHashMap<&'tcx Substs<'tcx>, CrateNum>>>> -{ - debug_assert!(cnum == LOCAL_CRATE); - - let cnums = tcx.all_crate_nums(LOCAL_CRATE); - - let mut instances: DefIdMap<FxHashMap<_, _>> = DefIdMap(); - - let cnum_stable_ids: IndexVec<CrateNum, Fingerprint> = { - let mut cnum_stable_ids = IndexVec::from_elem_n(Fingerprint::ZERO, - cnums.len() + 1); - - for &cnum in cnums.iter() { - cnum_stable_ids[cnum] = tcx.def_path_hash(DefId { - krate: cnum, - index: CRATE_DEF_INDEX, - }).0; - } - - cnum_stable_ids - }; - - for &cnum in cnums.iter() { - for &(ref exported_symbol, _) in tcx.exported_symbols(cnum).iter() { - if let &ExportedSymbol::Generic(def_id, substs) = exported_symbol { - let substs_map = instances.entry(def_id).or_default(); - - match substs_map.entry(substs) { - Occupied(mut e) => { - // If there are multiple monomorphizations available, - // we select one deterministically. - let other_cnum = *e.get(); - if cnum_stable_ids[other_cnum] > cnum_stable_ids[cnum] { - e.insert(cnum); - } - } - Vacant(e) => { - e.insert(cnum); - } - } - } - } - } - - Lrc::new(instances.into_iter() - .map(|(key, value)| (key, Lrc::new(value))) - .collect()) -} - -fn upstream_monomorphizations_for_provider<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) - -> Option<Lrc<FxHashMap<&'tcx Substs<'tcx>, CrateNum>>> -{ - debug_assert!(!def_id.is_local()); - tcx.upstream_monomorphizations(LOCAL_CRATE) - .get(&def_id) - .cloned() -} - -fn is_unreachable_local_definition_provider(tcx: TyCtxt, def_id: DefId) -> bool { - if let Some(node_id) = tcx.hir.as_local_node_id(def_id) { - !tcx.reachable_set(LOCAL_CRATE).0.contains(&node_id) - } else { - bug!("is_unreachable_local_definition called with non-local DefId: {:?}", - def_id) - } -} - -pub fn provide(providers: &mut Providers) { - providers.reachable_non_generics = reachable_non_generics_provider; - providers.is_reachable_non_generic = is_reachable_non_generic_provider_local; - providers.exported_symbols = exported_symbols_provider_local; - providers.upstream_monomorphizations = upstream_monomorphizations_provider; - providers.is_unreachable_local_definition = is_unreachable_local_definition_provider; -} - -pub fn provide_extern(providers: &mut Providers) { - providers.is_reachable_non_generic = is_reachable_non_generic_provider_extern; - providers.upstream_monomorphizations_for = upstream_monomorphizations_for_provider; -} - -fn symbol_export_level(tcx: TyCtxt, sym_def_id: DefId) -> SymbolExportLevel { - // We export anything that's not mangled at the "C" layer as it probably has - // to do with ABI concerns. We do not, however, apply such treatment to - // special symbols in the standard library for various plumbing between - // core/std/allocators/etc. For example symbols used to hook up allocation - // are not considered for export - let codegen_fn_attrs = tcx.codegen_fn_attrs(sym_def_id); - let is_extern = codegen_fn_attrs.contains_extern_indicator(); - let std_internal = - codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL); - - if is_extern && !std_internal { - SymbolExportLevel::C - } else { - SymbolExportLevel::Rust - } -} diff --git a/src/librustc_codegen_llvm/back/wasm.rs b/src/librustc_codegen_llvm/back/wasm.rs index f37854b7bca..7101255173c 100644 --- a/src/librustc_codegen_llvm/back/wasm.rs +++ b/src/librustc_codegen_llvm/back/wasm.rs @@ -42,7 +42,7 @@ const WASM_EXTERNAL_KIND_GLOBAL: u8 = 3; /// https://github.com/llvm-mirror/llvm/commit/0f32e1365, although support still /// needs to be added, tracked at https://bugs.llvm.org/show_bug.cgi?id=37168 pub fn rewrite_imports(path: &Path, import_map: &FxHashMap<String, String>) { - if import_map.len() == 0 { + if import_map.is_empty() { return } @@ -127,7 +127,7 @@ impl<'a> Iterator for WasmSections<'a> { type Item = (u8, &'a [u8]); fn next(&mut self) -> Option<(u8, &'a [u8])> { - if self.0.data.len() == 0 { + if self.0.data.is_empty() { return None } diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs index 95281e2445a..8973852caa8 100644 --- a/src/librustc_codegen_llvm/back/write.rs +++ b/src/librustc_codegen_llvm/back/write.rs @@ -10,11 +10,8 @@ use attributes; use back::bytecode::{self, RLIB_BYTECODE_EXTENSION}; -use back::lto::{self, ModuleBuffer, ThinBuffer, SerializedModule}; +use back::lto::{self, ThinBuffer, SerializedModule}; use back::link::{self, get_linker, remove}; -use back::command::Command; -use back::linker::LinkerInfo; -use back::symbol_export::ExportedSymbols; use base; use consts; use memmap; @@ -38,6 +35,9 @@ use rustc::util::common::{time_ext, time_depth, set_time_depth, print_time_passe use rustc_fs_util::{path2cstr, link_or_copy}; use rustc_data_structures::small_c_str::SmallCStr; use rustc_data_structures::svh::Svh; +use rustc_codegen_utils::command::Command; +use rustc_codegen_utils::linker::LinkerInfo; +use rustc_codegen_utils::symbol_export::ExportedSymbols; use errors::{self, Handler, Level, DiagnosticBuilder, FatalError, DiagnosticId}; use errors::emitter::{Emitter}; use syntax::attr; @@ -64,7 +64,7 @@ use std::time::Instant; use std::thread; use libc::{c_uint, c_void, c_char, size_t}; -pub const RELOC_MODEL_ARGS : [(&'static str, llvm::RelocMode); 7] = [ +pub const RELOC_MODEL_ARGS : [(&str, llvm::RelocMode); 7] = [ ("pic", llvm::RelocMode::PIC), ("static", llvm::RelocMode::Static), ("default", llvm::RelocMode::Default), @@ -81,7 +81,7 @@ pub const CODE_GEN_MODEL_ARGS: &[(&str, llvm::CodeModel)] = &[ ("large", llvm::CodeModel::Large), ]; -pub const TLS_MODEL_ARGS : [(&'static str, llvm::ThreadLocalMode); 4] = [ +pub const TLS_MODEL_ARGS : [(&str, llvm::ThreadLocalMode); 4] = [ ("global-dynamic", llvm::ThreadLocalMode::GeneralDynamic), ("local-dynamic", llvm::ThreadLocalMode::LocalDynamic), ("initial-exec", llvm::ThreadLocalMode::InitialExec), @@ -90,7 +90,7 @@ pub const TLS_MODEL_ARGS : [(&'static str, llvm::ThreadLocalMode); 4] = [ const PRE_THIN_LTO_BC_EXT: &str = "pre-thin-lto.bc"; -pub fn llvm_err(handler: &errors::Handler, msg: String) -> FatalError { +pub fn llvm_err(handler: &errors::Handler, msg: &str) -> FatalError { match llvm::last_error() { Some(err) => handler.fatal(&format!("{}: {}", msg, err)), None => handler.fatal(&msg), @@ -106,11 +106,10 @@ pub fn write_output_file( file_type: llvm::FileType) -> Result<(), FatalError> { unsafe { let output_c = path2cstr(output); - let result = llvm::LLVMRustWriteOutputFile( - target, pm, m, output_c.as_ptr(), file_type); + let result = llvm::LLVMRustWriteOutputFile(target, pm, m, output_c.as_ptr(), file_type); if result.into_result().is_err() { let msg = format!("could not write output to {}", output.display()); - Err(llvm_err(handler, msg)) + Err(llvm_err(handler, &msg)) } else { Ok(()) } @@ -140,7 +139,7 @@ pub fn create_target_machine( find_features: bool, ) -> &'static mut llvm::TargetMachine { target_machine_factory(sess, find_features)().unwrap_or_else(|err| { - llvm_err(sess.diagnostic(), err).raise() + llvm_err(sess.diagnostic(), &err).raise() }) } @@ -337,6 +336,11 @@ impl ModuleConfig { self.merge_functions = sess.opts.optimize == config::OptLevel::Default || sess.opts.optimize == config::OptLevel::Aggressive; } + + pub fn bitcode_needed(&self) -> bool { + self.emit_bc || self.obj_is_bitcode + || self.emit_bc_compressed || self.embed_bitcode + } } /// Assembler name and command used by codegen when no_integrated_as is enabled @@ -451,7 +455,7 @@ impl<'a> Drop for DiagnosticHandlers<'a> { unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext, msg: &'b str, cookie: c_uint) { - cgcx.diag_emitter.inline_asm_error(cookie as u32, msg.to_string()); + cgcx.diag_emitter.inline_asm_error(cookie as u32, msg.to_owned()); } unsafe extern "C" fn inline_asm_handler(diag: &SMDiagnostic, @@ -564,8 +568,7 @@ unsafe fn optimize(cgcx: &CodegenContext, // Some options cause LLVM bitcode to be emitted, which uses ThinLTOBuffers, so we need // to make sure we run LLVM's NameAnonGlobals pass when emitting bitcode; otherwise // we'll get errors in LLVM. - let using_thin_buffers = llvm::LLVMRustThinLTOAvailable() && (config.emit_bc - || config.obj_is_bitcode || config.emit_bc_compressed || config.embed_bitcode); + let using_thin_buffers = config.bitcode_needed(); let mut have_name_anon_globals_pass = false; if !config.no_prepopulate_passes { llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod); @@ -586,8 +589,7 @@ unsafe fn optimize(cgcx: &CodegenContext, for pass in &config.passes { if !addpass(pass) { - diag_handler.warn(&format!("unknown pass `{}`, ignoring", - pass)); + diag_handler.warn(&format!("unknown pass `{}`, ignoring", pass)); } if pass == "name-anon-globals" { have_name_anon_globals_pass = true; @@ -597,8 +599,8 @@ unsafe fn optimize(cgcx: &CodegenContext, for pass in &cgcx.plugin_passes { if !addpass(pass) { diag_handler.err(&format!("a plugin asked for LLVM pass \ - `{}` but LLVM does not \ - recognize it", pass)); + `{}` but LLVM does not \ + recognize it", pass)); } if pass == "name-anon-globals" { have_name_anon_globals_pass = true; @@ -609,12 +611,12 @@ unsafe fn optimize(cgcx: &CodegenContext, // As described above, this will probably cause an error in LLVM if config.no_prepopulate_passes { diag_handler.err("The current compilation is going to use thin LTO buffers \ - without running LLVM's NameAnonGlobals pass. \ - This will likely cause errors in LLVM. Consider adding \ - -C passes=name-anon-globals to the compiler command line."); + without running LLVM's NameAnonGlobals pass. \ + This will likely cause errors in LLVM. Consider adding \ + -C passes=name-anon-globals to the compiler command line."); } else { bug!("We are using thin LTO buffers without running the NameAnonGlobals pass. \ - This will likely cause errors in LLVM and should never happen."); + This will likely cause errors in LLVM and should never happen."); } } } @@ -700,9 +702,9 @@ unsafe fn codegen(cgcx: &CodegenContext, // escape the closure itself, and the manager should only be // used once. unsafe fn with_codegen<'ll, F, R>(tm: &'ll llvm::TargetMachine, - llmod: &'ll llvm::Module, - no_builtins: bool, - f: F) -> R + llmod: &'ll llvm::Module, + no_builtins: bool, + f: F) -> R where F: FnOnce(&'ll mut PassManager<'ll>) -> R, { let cpm = llvm::LLVMCreatePassManager(); @@ -729,15 +731,8 @@ unsafe fn codegen(cgcx: &CodegenContext, if write_bc || config.emit_bc_compressed || config.embed_bitcode { - let thin; - let old; - let data = if llvm::LLVMRustThinLTOAvailable() { - thin = ThinBuffer::new(llmod); - thin.data() - } else { - old = ModuleBuffer::new(llmod); - old.data() - }; + let thin = ThinBuffer::new(llmod); + let data = thin.data(); timeline.record("make-bc"); if write_bc { @@ -821,7 +816,7 @@ unsafe fn codegen(cgcx: &CodegenContext, }; with_codegen(tm, llmod, config.no_builtins, |cpm| { write_output_file(diag_handler, tm, cpm, llmod, &path, - llvm::FileType::AssemblyFile) + llvm::FileType::AssemblyFile) })?; timeline.record("asm"); } @@ -829,7 +824,7 @@ unsafe fn codegen(cgcx: &CodegenContext, if write_obj { with_codegen(tm, llmod, config.no_builtins, |cpm| { write_output_file(diag_handler, tm, cpm, llmod, &obj_out, - llvm::FileType::ObjectFile) + llvm::FileType::ObjectFile) })?; timeline.record("obj"); } else if asm_to_obj { @@ -950,11 +945,11 @@ fn need_pre_thin_lto_bitcode_for_incr_comp(sess: &Session) -> bool { } pub fn start_async_codegen(tcx: TyCtxt, - time_graph: Option<TimeGraph>, - metadata: EncodedMetadata, - coordinator_receive: Receiver<Box<dyn Any + Send>>, - total_cgus: usize) - -> OngoingCodegen { + time_graph: Option<TimeGraph>, + metadata: EncodedMetadata, + coordinator_receive: Receiver<Box<dyn Any + Send>>, + total_cgus: usize) + -> OngoingCodegen { let sess = tcx.sess; let crate_name = tcx.crate_name(LOCAL_CRATE); let crate_hash = tcx.crate_hash(LOCAL_CRATE); @@ -1119,7 +1114,8 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( } if let Some((id, product)) = - copy_cgu_workproducts_to_incr_comp_cache_dir(sess, &module.name, &files) { + copy_cgu_workproducts_to_incr_comp_cache_dir(sess, &module.name, &files) + { work_products.insert(id, product); } } @@ -1385,12 +1381,8 @@ fn execute_optimize_work_item(cgcx: &CodegenContext, // builds we don't actually want to LTO the allocator modules if // it shows up. This is due to various linker shenanigans that // we'll encounter later. - // - // Additionally here's where we also factor in the current LLVM - // version. If it doesn't support ThinLTO we skip this. Lto::ThinLocal => { - module.kind != ModuleKind::Allocator && - unsafe { llvm::LLVMRustThinLTOAvailable() } + module.kind != ModuleKind::Allocator } }; @@ -1448,15 +1440,12 @@ fn execute_copy_from_cache_work_item(cgcx: &CodegenContext, module.name, source_file, obj_out.display()); - match link_or_copy(&source_file, &obj_out) { - Ok(_) => { } - Err(err) => { - let diag_handler = cgcx.create_diag_handler(); - diag_handler.err(&format!("unable to copy {} to {}: {}", - source_file.display(), - obj_out.display(), - err)); - } + if let Err(err) = link_or_copy(&source_file, &obj_out) { + let diag_handler = cgcx.create_diag_handler(); + diag_handler.err(&format!("unable to copy {} to {}: {}", + source_file.display(), + obj_out.display(), + err)); } } @@ -1508,6 +1497,7 @@ enum Message { }, CodegenComplete, CodegenItem, + CodegenAborted, } struct Diagnostic { @@ -1540,7 +1530,7 @@ fn start_executing_work(tcx: TyCtxt, // Compute the set of symbols we need to retain when doing LTO (if we need to) let exported_symbols = { - let mut exported_symbols = FxHashMap(); + let mut exported_symbols = FxHashMap::default(); let copy_symbols = |cnum| { let symbols = tcx.exported_symbols(cnum) @@ -1590,10 +1580,8 @@ fn start_executing_work(tcx: TyCtxt, let (name, mut cmd) = get_linker(sess, &linker, flavor); cmd.args(&sess.target.target.options.asm_args); - Some(Arc::new(AssemblerCommand { - name, - cmd, - })) + + Some(Arc::new(AssemblerCommand { name, cmd })) } else { None }; @@ -1788,6 +1776,7 @@ fn start_executing_work(tcx: TyCtxt, let mut needs_lto = Vec::new(); let mut lto_import_only_modules = Vec::new(); let mut started_lto = false; + let mut codegen_aborted = false; // This flag tracks whether all items have gone through codegens let mut codegen_done = false; @@ -1805,13 +1794,19 @@ fn start_executing_work(tcx: TyCtxt, let mut llvm_start_time = None; // Run the message loop while there's still anything that needs message - // processing: + // processing. Note that as soon as codegen is aborted we simply want to + // wait for all existing work to finish, so many of the conditions here + // only apply if codegen hasn't been aborted as they represent pending + // work to be done. while !codegen_done || - work_items.len() > 0 || running > 0 || - needs_lto.len() > 0 || - lto_import_only_modules.len() > 0 || - main_thread_worker_state != MainThreadWorkerState::Idle { + (!codegen_aborted && ( + work_items.len() > 0 || + needs_lto.len() > 0 || + lto_import_only_modules.len() > 0 || + main_thread_worker_state != MainThreadWorkerState::Idle + )) + { // While there are still CGUs to be codegened, the coordinator has // to decide how to utilize the compiler processes implicit Token: @@ -1840,6 +1835,9 @@ fn start_executing_work(tcx: TyCtxt, spawn_work(cgcx, item); } } + } else if codegen_aborted { + // don't queue up any more work if codegen was aborted, we're + // just waiting for our existing children to finish } else { // If we've finished everything related to normal codegen // then it must be the case that we've got some LTO work to do. @@ -1904,7 +1902,7 @@ fn start_executing_work(tcx: TyCtxt, // Spin up what work we can, only doing this while we've got available // parallelism slots and work left to spawn. - while work_items.len() > 0 && running < tokens.len() { + while !codegen_aborted && work_items.len() > 0 && running < tokens.len() { let (item, _) = work_items.pop().unwrap(); maybe_start_llvm_timer(cgcx.config(item.module_kind()), @@ -1969,6 +1967,7 @@ fn start_executing_work(tcx: TyCtxt, if !cgcx.opts.debugging_opts.no_parallel_llvm { helper.request_token(); } + assert!(!codegen_aborted); assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning); main_thread_worker_state = MainThreadWorkerState::Idle; @@ -1976,11 +1975,26 @@ fn start_executing_work(tcx: TyCtxt, Message::CodegenComplete => { codegen_done = true; + assert!(!codegen_aborted); assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning); main_thread_worker_state = MainThreadWorkerState::Idle; } + // If codegen is aborted that means translation was aborted due + // to some normal-ish compiler error. In this situation we want + // to exit as soon as possible, but we want to make sure all + // existing work has finished. Flag codegen as being done, and + // then conditions above will ensure no more work is spawned but + // we'll keep executing this loop until `running` hits 0. + Message::CodegenAborted => { + assert!(!codegen_aborted); + codegen_done = true; + codegen_aborted = true; + assert_eq!(main_thread_worker_state, + MainThreadWorkerState::Codegenning); + } + // If a thread exits successfully then we drop a token associated // with that worker and update our `running` count. We may later // re-acquire a token to continue running more work. We may also not @@ -2166,9 +2180,9 @@ pub fn run_assembler(cgcx: &CodegenContext, handler: &Handler, assembly: &Path, handler.struct_err(&format!("linking with `{}` failed: {}", pname.display(), prog.status)) - .note(&format!("{:?}", &cmd)) - .note(str::from_utf8(¬e[..]).unwrap()) - .emit(); + .note(&format!("{:?}", &cmd)) + .note(str::from_utf8(¬e[..]).unwrap()) + .emit(); handler.abort_if_errors(); } }, @@ -2430,8 +2444,8 @@ impl OngoingCodegen { } pub(crate) fn submit_pre_codegened_module_to_llvm(&self, - tcx: TyCtxt, - module: ModuleCodegen) { + tcx: TyCtxt, + module: ModuleCodegen) { self.wait_for_signal_to_codegen_item(); self.check_for_errors(tcx.sess); @@ -2446,6 +2460,19 @@ impl OngoingCodegen { drop(self.coordinator_send.send(Box::new(Message::CodegenComplete))); } + /// Consume this context indicating that codegen was entirely aborted, and + /// we need to exit as quickly as possible. + /// + /// This method blocks the current thread until all worker threads have + /// finished, and all worker threads should have exited or be real close to + /// exiting at this point. + pub fn codegen_aborted(self) { + // Signal to the coordinator it should spawn no more work and start + // shutdown. + drop(self.coordinator_send.send(Box::new(Message::CodegenAborted))); + drop(self.future.join()); + } + pub fn check_for_errors(&self, sess: &Session) { self.shared_emitter_main.check(sess, false); } @@ -2464,6 +2491,11 @@ impl OngoingCodegen { } } +// impl Drop for OngoingCodegen { +// fn drop(&mut self) { +// } +// } + pub(crate) fn submit_codegened_module_to_llvm(tcx: TyCtxt, module: ModuleCodegen, cost: u64) { diff --git a/src/librustc_codegen_llvm/base.rs b/src/librustc_codegen_llvm/base.rs index c814ab4ab67..821b074cd68 100644 --- a/src/librustc_codegen_llvm/base.rs +++ b/src/librustc_codegen_llvm/base.rs @@ -39,7 +39,7 @@ use rustc::middle::weak_lang_items; use rustc::mir::mono::{Linkage, Visibility, Stats, CodegenUnitNameBuilder}; use rustc::middle::cstore::{EncodedMetadata}; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::layout::{self, Align, TyLayout, LayoutOf}; +use rustc::ty::layout::{self, Align, TyLayout, LayoutOf, VariantIdx}; use rustc::ty::query::Providers; use rustc::middle::cstore::{self, LinkagePreference}; use rustc::middle::exported_symbols; @@ -53,10 +53,9 @@ use mir::place::PlaceRef; use attributes; use builder::{Builder, MemFlags}; use callee; -use common::{C_bool, C_bytes_in_context, C_i32, C_usize}; -use rustc_mir::monomorphize::collector::{self, MonoItemCollectionMode}; +use common::{C_bool, C_bytes_in_context, C_usize}; use rustc_mir::monomorphize::item::DefPathBasedNames; -use common::{self, C_struct_in_context, C_array, val_ty}; +use common::{C_struct_in_context, C_array, val_ty}; use consts; use context::CodegenCx; use debuginfo; @@ -64,24 +63,24 @@ use declare; use meth; use mir; use monomorphize::Instance; -use monomorphize::partitioning::{self, PartitioningStrategy, CodegenUnit, CodegenUnitExt}; +use monomorphize::partitioning::{CodegenUnit, CodegenUnitExt}; use rustc_codegen_utils::symbol_names_test; use time_graph; -use mono_item::{MonoItem, BaseMonoItemExt, MonoItemExt}; +use mono_item::{MonoItem, MonoItemExt}; use type_::Type; use type_of::LayoutLlvmExt; -use rustc::util::nodemap::{FxHashMap, FxHashSet, DefIdSet}; +use rustc::util::nodemap::FxHashMap; use CrateInfo; use rustc_data_structures::small_c_str::SmallCStr; use rustc_data_structures::sync::Lrc; +use rustc_data_structures::indexed_vec::Idx; use std::any::Any; -use std::ffi::CString; -use std::sync::Arc; -use std::time::{Instant, Duration}; -use std::i32; use std::cmp; +use std::ffi::CString; +use std::ops::{Deref, DerefMut}; use std::sync::mpsc; +use std::time::{Instant, Duration}; use syntax_pos::Span; use syntax_pos::symbol::InternedString; use syntax::attr; @@ -311,7 +310,7 @@ pub fn coerce_unsized_into( (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { assert_eq!(def_a, def_b); - for i in 0..def_a.variants[0].fields.len() { + for i in 0..def_a.variants[VariantIdx::new(0)].fields.len() { let src_f = src.project_field(bx, i); let dst_f = dst.project_field(bx, i); @@ -320,8 +319,8 @@ pub fn coerce_unsized_into( } if src_f.layout.ty == dst_f.layout.ty { - memcpy_ty(bx, dst_f.llval, src_f.llval, src_f.layout, - src_f.align.min(dst_f.align), MemFlags::empty()); + memcpy_ty(bx, dst_f.llval, dst_f.align, src_f.llval, src_f.align, + src_f.layout, MemFlags::empty()); } else { coerce_unsized_into(bx, src_f, dst_f); } @@ -421,36 +420,34 @@ pub fn to_immediate_scalar( pub fn call_memcpy( bx: &Builder<'_, 'll, '_>, dst: &'ll Value, + dst_align: Align, src: &'ll Value, + src_align: Align, n_bytes: &'ll Value, - align: Align, flags: MemFlags, ) { if flags.contains(MemFlags::NONTEMPORAL) { // HACK(nox): This is inefficient but there is no nontemporal memcpy. - let val = bx.load(src, align); + let val = bx.load(src, src_align); let ptr = bx.pointercast(dst, val_ty(val).ptr_to()); - bx.store_with_flags(val, ptr, align, flags); + bx.store_with_flags(val, ptr, dst_align, flags); return; } let cx = bx.cx; - let ptr_width = &cx.sess().target.target.target_pointer_width; - let key = format!("llvm.memcpy.p0i8.p0i8.i{}", ptr_width); - let memcpy = cx.get_intrinsic(&key); let src_ptr = bx.pointercast(src, Type::i8p(cx)); let dst_ptr = bx.pointercast(dst, Type::i8p(cx)); let size = bx.intcast(n_bytes, cx.isize_ty, false); - let align = C_i32(cx, align.abi() as i32); - let volatile = C_bool(cx, flags.contains(MemFlags::VOLATILE)); - bx.call(memcpy, &[dst_ptr, src_ptr, size, align, volatile], None); + let volatile = flags.contains(MemFlags::VOLATILE); + bx.memcpy(dst_ptr, dst_align.abi(), src_ptr, src_align.abi(), size, volatile); } pub fn memcpy_ty( bx: &Builder<'_, 'll, 'tcx>, dst: &'ll Value, + dst_align: Align, src: &'ll Value, + src_align: Align, layout: TyLayout<'tcx>, - align: Align, flags: MemFlags, ) { let size = layout.size.bytes(); @@ -458,7 +455,7 @@ pub fn memcpy_ty( return; } - call_memcpy(bx, dst, src, C_usize(bx.cx, size), align, flags); + call_memcpy(bx, dst, dst_align, src, src_align, C_usize(bx.cx, size), flags); } pub fn call_memset( @@ -491,8 +488,7 @@ pub fn codegen_instance<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, instance: Instance<' // release builds. info!("codegen_instance({})", instance); - let fn_ty = instance.ty(cx.tcx); - let sig = common::ty_fn_sig(cx, fn_ty); + let sig = instance.fn_sig(cx.tcx); let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); let lldecl = cx.instances.borrow().get(&instance).cloned().unwrap_or_else(|| @@ -558,7 +554,7 @@ fn maybe_create_entry_wrapper(cx: &CodegenCx) { // regions must appear in the argument // listing. let main_ret_ty = cx.tcx.erase_regions( - &main_ret_ty.no_late_bound_regions().unwrap(), + &main_ret_ty.no_bound_vars().unwrap(), ); if declare::get_defined_value(cx, "main").is_some() { @@ -739,19 +735,6 @@ pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, { check_for_rustc_errors_attr(tcx); - if let Some(true) = tcx.sess.opts.debugging_opts.thinlto { - if unsafe { !llvm::LLVMRustThinLTOAvailable() } { - tcx.sess.fatal("this compiler's LLVM does not support ThinLTO"); - } - } - - if (tcx.sess.opts.debugging_opts.pgo_gen.is_some() || - !tcx.sess.opts.debugging_opts.pgo_use.is_empty()) && - unsafe { !llvm::LLVMRustPGOAvailable() } - { - tcx.sess.fatal("this compiler's LLVM does not support PGO"); - } - let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx); // Codegen the metadata. @@ -784,7 +767,7 @@ pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, !tcx.sess.opts.output_types.should_codegen() { let ongoing_codegen = write::start_async_codegen( tcx, - time_graph.clone(), + time_graph, metadata, rx, 1); @@ -821,6 +804,7 @@ pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, metadata, rx, codegen_units.len()); + let ongoing_codegen = AbortCodegenOnDrop(Some(ongoing_codegen)); // Codegen an allocator shim, if necessary. // @@ -950,139 +934,64 @@ pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ongoing_codegen.check_for_errors(tcx.sess); assert_and_save_dep_graph(tcx); - ongoing_codegen + ongoing_codegen.into_inner() } -fn assert_and_save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { - time(tcx.sess, - "assert dep graph", - || rustc_incremental::assert_dep_graph(tcx)); - - time(tcx.sess, - "serialize dep graph", - || rustc_incremental::save_dep_graph(tcx)); +/// A curious wrapper structure whose only purpose is to call `codegen_aborted` +/// when it's dropped abnormally. +/// +/// In the process of working on rust-lang/rust#55238 a mysterious segfault was +/// stumbled upon. The segfault was never reproduced locally, but it was +/// suspected to be releated to the fact that codegen worker threads were +/// sticking around by the time the main thread was exiting, causing issues. +/// +/// This structure is an attempt to fix that issue where the `codegen_aborted` +/// message will block until all workers have finished. This should ensure that +/// even if the main codegen thread panics we'll wait for pending work to +/// complete before returning from the main thread, hopefully avoiding +/// segfaults. +/// +/// If you see this comment in the code, then it means that this workaround +/// worked! We may yet one day track down the mysterious cause of that +/// segfault... +struct AbortCodegenOnDrop(Option<OngoingCodegen>); + +impl AbortCodegenOnDrop { + fn into_inner(mut self) -> OngoingCodegen { + self.0.take().unwrap() + } } -fn collect_and_partition_mono_items<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - cnum: CrateNum, -) -> (Arc<DefIdSet>, Arc<Vec<Arc<CodegenUnit<'tcx>>>>) -{ - assert_eq!(cnum, LOCAL_CRATE); - - let collection_mode = match tcx.sess.opts.debugging_opts.print_mono_items { - Some(ref s) => { - let mode_string = s.to_lowercase(); - let mode_string = mode_string.trim(); - if mode_string == "eager" { - MonoItemCollectionMode::Eager - } else { - if mode_string != "lazy" { - let message = format!("Unknown codegen-item collection mode '{}'. \ - Falling back to 'lazy' mode.", - mode_string); - tcx.sess.warn(&message); - } - - MonoItemCollectionMode::Lazy - } - } - None => { - if tcx.sess.opts.cg.link_dead_code { - MonoItemCollectionMode::Eager - } else { - MonoItemCollectionMode::Lazy - } - } - }; - - let (items, inlining_map) = - time(tcx.sess, "monomorphization collection", || { - collector::collect_crate_mono_items(tcx, collection_mode) - }); - - tcx.sess.abort_if_errors(); - - ::rustc_mir::monomorphize::assert_symbols_are_distinct(tcx, items.iter()); - - let strategy = if tcx.sess.opts.incremental.is_some() { - PartitioningStrategy::PerModule - } else { - PartitioningStrategy::FixedUnitCount(tcx.sess.codegen_units()) - }; - - let codegen_units = time(tcx.sess, "codegen unit partitioning", || { - partitioning::partition(tcx, - items.iter().cloned(), - strategy, - &inlining_map) - .into_iter() - .map(Arc::new) - .collect::<Vec<_>>() - }); - - let mono_items: DefIdSet = items.iter().filter_map(|mono_item| { - match *mono_item { - MonoItem::Fn(ref instance) => Some(instance.def_id()), - MonoItem::Static(def_id) => Some(def_id), - _ => None, - } - }).collect(); - - if tcx.sess.opts.debugging_opts.print_mono_items.is_some() { - let mut item_to_cgus: FxHashMap<_, Vec<_>> = FxHashMap(); - - for cgu in &codegen_units { - for (&mono_item, &linkage) in cgu.items() { - item_to_cgus.entry(mono_item) - .or_default() - .push((cgu.name().clone(), linkage)); - } - } +impl Deref for AbortCodegenOnDrop { + type Target = OngoingCodegen; - let mut item_keys: Vec<_> = items - .iter() - .map(|i| { - let mut output = i.to_string(tcx); - output.push_str(" @@"); - let mut empty = Vec::new(); - let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty); - cgus.as_mut_slice().sort_by_key(|&(ref name, _)| name.clone()); - cgus.dedup(); - for &(ref cgu_name, (linkage, _)) in cgus.iter() { - output.push_str(" "); - output.push_str(&cgu_name.as_str()); - - let linkage_abbrev = match linkage { - Linkage::External => "External", - Linkage::AvailableExternally => "Available", - Linkage::LinkOnceAny => "OnceAny", - Linkage::LinkOnceODR => "OnceODR", - Linkage::WeakAny => "WeakAny", - Linkage::WeakODR => "WeakODR", - Linkage::Appending => "Appending", - Linkage::Internal => "Internal", - Linkage::Private => "Private", - Linkage::ExternalWeak => "ExternalWeak", - Linkage::Common => "Common", - }; - - output.push_str("["); - output.push_str(linkage_abbrev); - output.push_str("]"); - } - output - }) - .collect(); + fn deref(&self) -> &OngoingCodegen { + self.0.as_ref().unwrap() + } +} - item_keys.sort(); +impl DerefMut for AbortCodegenOnDrop { + fn deref_mut(&mut self) -> &mut OngoingCodegen { + self.0.as_mut().unwrap() + } +} - for item in item_keys { - println!("MONO_ITEM {}", item); +impl Drop for AbortCodegenOnDrop { + fn drop(&mut self) { + if let Some(codegen) = self.0.take() { + codegen.codegen_aborted(); } } +} - (Arc::new(mono_items), Arc::new(codegen_units)) +fn assert_and_save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { + time(tcx.sess, + "assert dep graph", + || rustc_incremental::assert_dep_graph(tcx)); + + time(tcx.sess, + "serialize dep graph", + || rustc_incremental::save_dep_graph(tcx)); } impl CrateInfo { @@ -1092,17 +1001,17 @@ impl CrateInfo { compiler_builtins: None, profiler_runtime: None, sanitizer_runtime: None, - is_no_builtins: FxHashSet(), - native_libraries: FxHashMap(), + is_no_builtins: Default::default(), + native_libraries: Default::default(), used_libraries: tcx.native_libraries(LOCAL_CRATE), link_args: tcx.link_args(LOCAL_CRATE), - crate_name: FxHashMap(), + crate_name: Default::default(), used_crates_dynamic: cstore::used_crates(tcx, LinkagePreference::RequireDynamic), used_crates_static: cstore::used_crates(tcx, LinkagePreference::RequireStatic), - used_crate_source: FxHashMap(), - wasm_imports: FxHashMap(), - lang_item_to_crate: FxHashMap(), - missing_lang_items: FxHashMap(), + used_crate_source: Default::default(), + wasm_imports: Default::default(), + lang_item_to_crate: Default::default(), + missing_lang_items: Default::default(), }; let lang_items = tcx.lang_items(); @@ -1174,12 +1083,6 @@ impl CrateInfo { } } -fn is_codegened_item(tcx: TyCtxt, id: DefId) -> bool { - let (all_mono_items, _) = - tcx.collect_and_partition_mono_items(LOCAL_CRATE); - all_mono_items.contains(&id) -} - fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, cgu_name: InternedString) -> Stats { @@ -1270,24 +1173,7 @@ fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -pub fn provide(providers: &mut Providers) { - providers.collect_and_partition_mono_items = - collect_and_partition_mono_items; - - providers.is_codegened_item = is_codegened_item; - - providers.codegen_unit = |tcx, name| { - let (_, all) = tcx.collect_and_partition_mono_items(LOCAL_CRATE); - all.iter() - .find(|cgu| *cgu.name() == name) - .cloned() - .unwrap_or_else(|| panic!("failed to find cgu with name {:?}", name)) - }; - - provide_extern(providers); -} - -pub fn provide_extern(providers: &mut Providers) { +pub fn provide_both(providers: &mut Providers) { providers.dllimport_foreign_items = |tcx, krate| { let module_map = tcx.foreign_modules(krate); let module_map = module_map.iter() diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index 169bd9a8466..9db4015013e 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -482,14 +482,12 @@ impl Builder<'a, 'll, 'tcx> { } } - pub fn atomic_load(&self, ptr: &'ll Value, order: AtomicOrdering, align: Align) -> &'ll Value { + pub fn atomic_load(&self, ptr: &'ll Value, order: AtomicOrdering, size: Size) -> &'ll Value { self.count_insn("load.atomic"); unsafe { let load = llvm::LLVMRustBuildAtomicLoad(self.llbuilder, ptr, noname(), order); - // FIXME(eddyb) Isn't it UB to use `pref` instead of `abi` here? - // However, 64-bit atomic loads on `i686-apple-darwin` appear to - // require `___atomic_load` with ABI-alignment, so it's staying. - llvm::LLVMSetAlignment(load, align.pref() as c_uint); + // LLVM requires the alignment of atomic loads to be at least the size of the type. + llvm::LLVMSetAlignment(load, size.bytes() as c_uint); load } } @@ -564,15 +562,14 @@ impl Builder<'a, 'll, 'tcx> { } pub fn atomic_store(&self, val: &'ll Value, ptr: &'ll Value, - order: AtomicOrdering, align: Align) { + order: AtomicOrdering, size: Size) { debug!("Store {:?} -> {:?}", val, ptr); self.count_insn("store.atomic"); let ptr = self.check_store(val, ptr); unsafe { let store = llvm::LLVMRustBuildAtomicStore(self.llbuilder, val, ptr, order); - // FIXME(eddyb) Isn't it UB to use `pref` instead of `abi` here? - // Also see `atomic_load` for more context. - llvm::LLVMSetAlignment(store, align.pref() as c_uint); + // LLVM requires the alignment of atomic stores to be at least the size of the type. + llvm::LLVMSetAlignment(store, size.bytes() as c_uint); } } @@ -761,7 +758,7 @@ impl Builder<'a, 'll, 'tcx> { fty, asm, cons, volatile, alignstack, dia); Some(self.call(v, inputs, None)) } else { - // LLVM has detected an issue with our constaints, bail out + // LLVM has detected an issue with our constraints, bail out None } } @@ -784,6 +781,24 @@ impl Builder<'a, 'll, 'tcx> { } } + pub fn memcpy(&self, dst: &'ll Value, dst_align: u64, + src: &'ll Value, src_align: u64, + size: &'ll Value, is_volatile: bool) -> &'ll Value { + unsafe { + llvm::LLVMRustBuildMemCpy(self.llbuilder, dst, dst_align as c_uint, + src, src_align as c_uint, size, is_volatile) + } + } + + pub fn memmove(&self, dst: &'ll Value, dst_align: u64, + src: &'ll Value, src_align: u64, + size: &'ll Value, is_volatile: bool) -> &'ll Value { + unsafe { + llvm::LLVMRustBuildMemMove(self.llbuilder, dst, dst_align as c_uint, + src, src_align as c_uint, size, is_volatile) + } + } + pub fn minnum(&self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { self.count_insn("minnum"); unsafe { @@ -859,8 +874,7 @@ impl Builder<'a, 'll, 'tcx> { // FIXME: add a non-fast math version once // https://bugs.llvm.org/show_bug.cgi?id=36732 // is fixed. - let instr = llvm::LLVMRustBuildVectorReduceFAdd(self.llbuilder, acc, src) - .expect("LLVMRustBuildVectorReduceFAdd is not available in LLVM version < 5.0"); + let instr = llvm::LLVMRustBuildVectorReduceFAdd(self.llbuilder, acc, src); llvm::LLVMRustSetHasUnsafeAlgebra(instr); instr } @@ -871,66 +885,43 @@ impl Builder<'a, 'll, 'tcx> { // FIXME: add a non-fast math version once // https://bugs.llvm.org/show_bug.cgi?id=36732 // is fixed. - let instr = llvm::LLVMRustBuildVectorReduceFMul(self.llbuilder, acc, src) - .expect("LLVMRustBuildVectorReduceFMul is not available in LLVM version < 5.0"); + let instr = llvm::LLVMRustBuildVectorReduceFMul(self.llbuilder, acc, src); llvm::LLVMRustSetHasUnsafeAlgebra(instr); instr } } pub fn vector_reduce_add(&self, src: &'ll Value) -> &'ll Value { self.count_insn("vector.reduce.add"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceAdd(self.llbuilder, src); - instr.expect("LLVMRustBuildVectorReduceAdd is not available in LLVM version < 5.0") - } + unsafe { llvm::LLVMRustBuildVectorReduceAdd(self.llbuilder, src) } } pub fn vector_reduce_mul(&self, src: &'ll Value) -> &'ll Value { self.count_insn("vector.reduce.mul"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceMul(self.llbuilder, src); - instr.expect("LLVMRustBuildVectorReduceMul is not available in LLVM version < 5.0") - } + unsafe { llvm::LLVMRustBuildVectorReduceMul(self.llbuilder, src) } } pub fn vector_reduce_and(&self, src: &'ll Value) -> &'ll Value { self.count_insn("vector.reduce.and"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceAnd(self.llbuilder, src); - instr.expect("LLVMRustBuildVectorReduceAnd is not available in LLVM version < 5.0") - } + unsafe { llvm::LLVMRustBuildVectorReduceAnd(self.llbuilder, src) } } pub fn vector_reduce_or(&self, src: &'ll Value) -> &'ll Value { self.count_insn("vector.reduce.or"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceOr(self.llbuilder, src); - instr.expect("LLVMRustBuildVectorReduceOr is not available in LLVM version < 5.0") - } + unsafe { llvm::LLVMRustBuildVectorReduceOr(self.llbuilder, src) } } pub fn vector_reduce_xor(&self, src: &'ll Value) -> &'ll Value { self.count_insn("vector.reduce.xor"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceXor(self.llbuilder, src); - instr.expect("LLVMRustBuildVectorReduceXor is not available in LLVM version < 5.0") - } + unsafe { llvm::LLVMRustBuildVectorReduceXor(self.llbuilder, src) } } pub fn vector_reduce_fmin(&self, src: &'ll Value) -> &'ll Value { self.count_insn("vector.reduce.fmin"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceFMin(self.llbuilder, src, /*NoNaNs:*/ false); - instr.expect("LLVMRustBuildVectorReduceFMin is not available in LLVM version < 5.0") - } + unsafe { llvm::LLVMRustBuildVectorReduceFMin(self.llbuilder, src, /*NoNaNs:*/ false) } } pub fn vector_reduce_fmax(&self, src: &'ll Value) -> &'ll Value { self.count_insn("vector.reduce.fmax"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceFMax(self.llbuilder, src, /*NoNaNs:*/ false); - instr.expect("LLVMRustBuildVectorReduceFMax is not available in LLVM version < 5.0") - } + unsafe { llvm::LLVMRustBuildVectorReduceFMax(self.llbuilder, src, /*NoNaNs:*/ false) } } pub fn vector_reduce_fmin_fast(&self, src: &'ll Value) -> &'ll Value { self.count_insn("vector.reduce.fmin_fast"); unsafe { - let instr = llvm::LLVMRustBuildVectorReduceFMin(self.llbuilder, src, /*NoNaNs:*/ true) - .expect("LLVMRustBuildVectorReduceFMin is not available in LLVM version < 5.0"); + let instr = llvm::LLVMRustBuildVectorReduceFMin(self.llbuilder, src, /*NoNaNs:*/ true); llvm::LLVMRustSetHasUnsafeAlgebra(instr); instr } @@ -938,25 +929,18 @@ impl Builder<'a, 'll, 'tcx> { pub fn vector_reduce_fmax_fast(&self, src: &'ll Value) -> &'ll Value { self.count_insn("vector.reduce.fmax_fast"); unsafe { - let instr = llvm::LLVMRustBuildVectorReduceFMax(self.llbuilder, src, /*NoNaNs:*/ true) - .expect("LLVMRustBuildVectorReduceFMax is not available in LLVM version < 5.0"); + let instr = llvm::LLVMRustBuildVectorReduceFMax(self.llbuilder, src, /*NoNaNs:*/ true); llvm::LLVMRustSetHasUnsafeAlgebra(instr); instr } } pub fn vector_reduce_min(&self, src: &'ll Value, is_signed: bool) -> &'ll Value { self.count_insn("vector.reduce.min"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceMin(self.llbuilder, src, is_signed); - instr.expect("LLVMRustBuildVectorReduceMin is not available in LLVM version < 5.0") - } + unsafe { llvm::LLVMRustBuildVectorReduceMin(self.llbuilder, src, is_signed) } } pub fn vector_reduce_max(&self, src: &'ll Value, is_signed: bool) -> &'ll Value { self.count_insn("vector.reduce.max"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceMax(self.llbuilder, src, is_signed); - instr.expect("LLVMRustBuildVectorReduceMax is not available in LLVM version < 5.0") - } + unsafe { llvm::LLVMRustBuildVectorReduceMax(self.llbuilder, src, is_signed) } } pub fn extract_value(&self, agg_val: &'ll Value, idx: u64) -> &'ll Value { diff --git a/src/librustc_codegen_llvm/callee.rs b/src/librustc_codegen_llvm/callee.rs index 4b4ccb3b600..c8c693257d5 100644 --- a/src/librustc_codegen_llvm/callee.rs +++ b/src/librustc_codegen_llvm/callee.rs @@ -44,19 +44,19 @@ pub fn get_fn( debug!("get_fn(instance={:?})", instance); assert!(!instance.substs.needs_infer()); - assert!(!instance.substs.has_escaping_regions()); + assert!(!instance.substs.has_escaping_bound_vars()); assert!(!instance.substs.has_param_types()); - let fn_ty = instance.ty(cx.tcx); + let sig = instance.fn_sig(cx.tcx); if let Some(&llfn) = cx.instances.borrow().get(&instance) { return llfn; } let sym = tcx.symbol_name(instance).as_str(); - debug!("get_fn({:?}: {:?}) => {}", instance, fn_ty, sym); + debug!("get_fn({:?}: {:?}) => {}", instance, sig, sym); // Create a fn pointer with the substituted signature. - let fn_ptr_ty = tcx.mk_fn_ptr(common::ty_fn_sig(cx, fn_ty)); + let fn_ptr_ty = tcx.mk_fn_ptr(sig); let llptrty = cx.layout_of(fn_ptr_ty).llvm_type(cx); let llfn = if let Some(llfn) = declare::get_declared_value(cx, &sym) { @@ -91,7 +91,7 @@ pub fn get_fn( llfn } } else { - let llfn = declare::declare_fn(cx, &sym, fn_ty); + let llfn = declare::declare_fn(cx, &sym, sig); assert_eq!(common::val_ty(llfn), llptrty); debug!("get_fn: not casting pointer!"); @@ -220,3 +220,19 @@ pub fn resolve_and_get_fn( ).unwrap() ) } + +pub fn resolve_and_get_fn_for_vtable( + cx: &CodegenCx<'ll, 'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx>, +) -> &'ll Value { + get_fn( + cx, + ty::Instance::resolve_for_vtable( + cx.tcx, + ty::ParamEnv::reveal_all(), + def_id, + substs + ).unwrap() + ) +} diff --git a/src/librustc_codegen_llvm/common.rs b/src/librustc_codegen_llvm/common.rs index c08937fa9b9..c9b464fd8f3 100644 --- a/src/librustc_codegen_llvm/common.rs +++ b/src/librustc_codegen_llvm/common.rs @@ -30,9 +30,7 @@ use rustc::ty::layout::{HasDataLayout, LayoutOf}; use rustc::hir; use libc::{c_uint, c_char}; -use std::iter; -use rustc_target::spec::abi::Abi; use syntax::symbol::LocalInternedString; use syntax_pos::{Span, DUMMY_SP}; @@ -404,52 +402,3 @@ pub fn shift_mask_val( _ => bug!("shift_mask_val: expected Integer or Vector, found {:?}", kind), } } - -pub fn ty_fn_sig<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - ty: Ty<'tcx>) - -> ty::PolyFnSig<'tcx> -{ - match ty.sty { - ty::FnDef(..) | - // Shims currently have type FnPtr. Not sure this should remain. - ty::FnPtr(_) => ty.fn_sig(cx.tcx), - ty::Closure(def_id, substs) => { - let tcx = cx.tcx; - let sig = substs.closure_sig(def_id, tcx); - - let env_ty = tcx.closure_env_ty(def_id, substs).unwrap(); - sig.map_bound(|sig| tcx.mk_fn_sig( - iter::once(*env_ty.skip_binder()).chain(sig.inputs().iter().cloned()), - sig.output(), - sig.variadic, - sig.unsafety, - sig.abi - )) - } - ty::Generator(def_id, substs, _) => { - let tcx = cx.tcx; - let sig = substs.poly_sig(def_id, cx.tcx); - - let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv); - let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty); - - sig.map_bound(|sig| { - let state_did = tcx.lang_items().gen_state().unwrap(); - let state_adt_ref = tcx.adt_def(state_did); - let state_substs = tcx.intern_substs(&[ - sig.yield_ty.into(), - sig.return_ty.into(), - ]); - let ret_ty = tcx.mk_adt(state_adt_ref, state_substs); - - tcx.mk_fn_sig(iter::once(env_ty), - ret_ty, - false, - hir::Unsafety::Normal, - Abi::Rust - ) - }) - } - _ => bug!("unexpected type {:?} to ty_fn_sig", ty) - } -} diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs index e6197423738..d6fd0690715 100644 --- a/src/librustc_codegen_llvm/context.rs +++ b/src/librustc_codegen_llvm/context.rs @@ -29,7 +29,7 @@ use rustc_data_structures::small_c_str::SmallCStr; use rustc::mir::mono::Stats; use rustc::session::config::{self, DebugInfo}; use rustc::session::Session; -use rustc::ty::layout::{LayoutError, LayoutOf, Size, TyLayout}; +use rustc::ty::layout::{LayoutError, LayoutOf, Size, TyLayout, VariantIdx}; use rustc::ty::{self, Ty, TyCtxt}; use rustc::util::nodemap::FxHashMap; use rustc_target::spec::{HasTargetSpec, Target}; @@ -87,7 +87,7 @@ pub struct CodegenCx<'a, 'tcx: 'a> { /// See http://llvm.org/docs/LangRef.html#the-llvm-used-global-variable for details pub used_statics: RefCell<Vec<&'a Value>>, - pub lltypes: RefCell<FxHashMap<(Ty<'tcx>, Option<usize>), &'a Type>>, + pub lltypes: RefCell<FxHashMap<(Ty<'tcx>, Option<VariantIdx>), &'a Type>>, pub scalar_lltypes: RefCell<FxHashMap<Ty<'tcx>, &'a Type>>, pub pointee_infos: RefCell<FxHashMap<(Ty<'tcx>, Size), Option<PointeeInfo>>>, pub isize_ty: &'a Type, @@ -295,22 +295,22 @@ impl<'a, 'tcx> CodegenCx<'a, 'tcx> { llcx, stats: RefCell::new(Stats::default()), codegen_unit, - instances: RefCell::new(FxHashMap()), - vtables: RefCell::new(FxHashMap()), - const_cstr_cache: RefCell::new(FxHashMap()), - const_unsized: RefCell::new(FxHashMap()), - const_globals: RefCell::new(FxHashMap()), + instances: Default::default(), + vtables: Default::default(), + const_cstr_cache: Default::default(), + const_unsized: Default::default(), + const_globals: Default::default(), statics_to_rauw: RefCell::new(Vec::new()), used_statics: RefCell::new(Vec::new()), - lltypes: RefCell::new(FxHashMap()), - scalar_lltypes: RefCell::new(FxHashMap()), - pointee_infos: RefCell::new(FxHashMap()), + lltypes: Default::default(), + scalar_lltypes: Default::default(), + pointee_infos: Default::default(), isize_ty, dbg_cx, eh_personality: Cell::new(None), eh_unwind_resume: Cell::new(None), rust_try_fn: Cell::new(None), - intrinsics: RefCell::new(FxHashMap()), + intrinsics: Default::default(), local_gen_sym_counter: Cell::new(0), } } @@ -404,15 +404,15 @@ impl<'b, 'tcx> CodegenCx<'b, 'tcx> { return llfn; } - let ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig( + let sig = ty::Binder::bind(tcx.mk_fn_sig( iter::once(tcx.mk_mut_ptr(tcx.types.u8)), tcx.types.never, false, hir::Unsafety::Unsafe, Abi::C - ))); + )); - let llfn = declare::declare_fn(self, "rust_eh_unwind_resume", ty); + let llfn = declare::declare_fn(self, "rust_eh_unwind_resume", sig); attributes::unwind(llfn, true); attributes::apply_target_cpu_attr(self, llfn); unwresume.set(Some(llfn)); @@ -446,29 +446,29 @@ impl<'b, 'tcx> CodegenCx<'b, 'tcx> { } } -impl ty::layout::HasDataLayout for &'a CodegenCx<'ll, 'tcx> { +impl ty::layout::HasDataLayout for CodegenCx<'ll, 'tcx> { fn data_layout(&self) -> &ty::layout::TargetDataLayout { &self.tcx.data_layout } } -impl HasTargetSpec for &'a CodegenCx<'ll, 'tcx> { +impl HasTargetSpec for CodegenCx<'ll, 'tcx> { fn target_spec(&self) -> &Target { &self.tcx.sess.target.target } } -impl ty::layout::HasTyCtxt<'tcx> for &'a CodegenCx<'ll, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> { +impl ty::layout::HasTyCtxt<'tcx> for CodegenCx<'ll, 'tcx> { + fn tcx<'a>(&'a self) -> TyCtxt<'a, 'tcx, 'tcx> { self.tcx } } -impl LayoutOf for &'a CodegenCx<'ll, 'tcx> { +impl LayoutOf for CodegenCx<'ll, 'tcx> { type Ty = Ty<'tcx>; type TyLayout = TyLayout<'tcx>; - fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { + fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout { self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)) .unwrap_or_else(|e| if let LayoutError::SizeOverflow(_) = e { self.sess().fatal(&e.to_string()) @@ -530,12 +530,6 @@ fn declare_intrinsic(cx: &CodegenCx<'ll, '_>, key: &str) -> Option<&'ll Value> { let t_v4f64 = Type::vector(t_f64, 4); let t_v8f64 = Type::vector(t_f64, 8); - ifn!("llvm.memcpy.p0i8.p0i8.i16", fn(i8p, i8p, t_i16, t_i32, i1) -> void); - ifn!("llvm.memcpy.p0i8.p0i8.i32", fn(i8p, i8p, t_i32, t_i32, i1) -> void); - ifn!("llvm.memcpy.p0i8.p0i8.i64", fn(i8p, i8p, t_i64, t_i32, i1) -> void); - ifn!("llvm.memmove.p0i8.p0i8.i16", fn(i8p, i8p, t_i16, t_i32, i1) -> void); - ifn!("llvm.memmove.p0i8.p0i8.i32", fn(i8p, i8p, t_i32, t_i32, i1) -> void); - ifn!("llvm.memmove.p0i8.p0i8.i64", fn(i8p, i8p, t_i64, t_i32, i1) -> void); ifn!("llvm.memset.p0i8.i16", fn(i8p, t_i8, t_i16, t_i32, i1) -> void); ifn!("llvm.memset.p0i8.i32", fn(i8p, t_i8, t_i32, t_i32, i1) -> void); ifn!("llvm.memset.p0i8.i64", fn(i8p, t_i8, t_i64, t_i32, i1) -> void); @@ -726,6 +720,18 @@ fn declare_intrinsic(cx: &CodegenCx<'ll, '_>, key: &str) -> Option<&'ll Value> { ifn!("llvm.bitreverse.i64", fn(t_i64) -> t_i64); ifn!("llvm.bitreverse.i128", fn(t_i128) -> t_i128); + ifn!("llvm.fshl.i8", fn(t_i8, t_i8, t_i8) -> t_i8); + ifn!("llvm.fshl.i16", fn(t_i16, t_i16, t_i16) -> t_i16); + ifn!("llvm.fshl.i32", fn(t_i32, t_i32, t_i32) -> t_i32); + ifn!("llvm.fshl.i64", fn(t_i64, t_i64, t_i64) -> t_i64); + ifn!("llvm.fshl.i128", fn(t_i128, t_i128, t_i128) -> t_i128); + + ifn!("llvm.fshr.i8", fn(t_i8, t_i8, t_i8) -> t_i8); + ifn!("llvm.fshr.i16", fn(t_i16, t_i16, t_i16) -> t_i16); + ifn!("llvm.fshr.i32", fn(t_i32, t_i32, t_i32) -> t_i32); + ifn!("llvm.fshr.i64", fn(t_i64, t_i64, t_i64) -> t_i64); + ifn!("llvm.fshr.i128", fn(t_i128, t_i128, t_i128) -> t_i128); + ifn!("llvm.sadd.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); ifn!("llvm.sadd.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); ifn!("llvm.sadd.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index f5e5287cd42..97a3ae9c9fa 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -23,6 +23,7 @@ use value::Value; use llvm; use llvm::debuginfo::{DIType, DIFile, DIScope, DIDescriptor, DICompositeType, DILexicalBlock, DIFlags}; +use llvm_util; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc::hir::CodegenFnAttrFlags; @@ -31,9 +32,10 @@ use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE}; use rustc::ich::NodeIdHashingMode; use rustc_data_structures::fingerprint::Fingerprint; use rustc::ty::Instance; -use common::CodegenCx; +use common::{CodegenCx, C_u64}; use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt}; -use rustc::ty::layout::{self, Align, LayoutOf, PrimitiveExt, Size, TyLayout}; +use rustc::ty::layout::{self, Align, HasDataLayout, Integer, IntegerExt, LayoutOf, + PrimitiveExt, Size, TyLayout}; use rustc::session::config; use rustc::util::nodemap::FxHashMap; use rustc_fs_util::path2cstr; @@ -96,6 +98,7 @@ pub struct UniqueTypeId(ast::Name); // created so far. The metadata nodes are indexed by UniqueTypeId, and, for // faster lookup, also by Ty. The TypeMap is responsible for creating // UniqueTypeIds. +#[derive(Default)] pub struct TypeMap<'ll, 'tcx> { // The UniqueTypeIds created so far unique_id_interner: Interner, @@ -108,15 +111,6 @@ pub struct TypeMap<'ll, 'tcx> { } impl TypeMap<'ll, 'tcx> { - pub fn new() -> Self { - TypeMap { - unique_id_interner: Interner::new(), - type_to_metadata: FxHashMap(), - unique_id_to_metadata: FxHashMap(), - type_to_unique_id: FxHashMap(), - } - } - // Adds a Ty to metadata mapping to the TypeMap. The method will fail if // the mapping already exists. fn register_type_with_metadata( @@ -213,6 +207,7 @@ enum RecursiveTypeDescription<'ll, 'tcx> { unfinished_type: Ty<'tcx>, unique_type_id: UniqueTypeId, metadata_stub: &'ll DICompositeType, + member_holding_stub: &'ll DICompositeType, member_description_factory: MemberDescriptionFactory<'ll, 'tcx>, }, FinalMetadata(&'ll DICompositeType) @@ -223,6 +218,7 @@ fn create_and_register_recursive_type_forward_declaration( unfinished_type: Ty<'tcx>, unique_type_id: UniqueTypeId, metadata_stub: &'ll DICompositeType, + member_holding_stub: &'ll DICompositeType, member_description_factory: MemberDescriptionFactory<'ll, 'tcx>, ) -> RecursiveTypeDescription<'ll, 'tcx> { @@ -235,6 +231,7 @@ fn create_and_register_recursive_type_forward_declaration( unfinished_type, unique_type_id, metadata_stub, + member_holding_stub, member_description_factory, } } @@ -250,6 +247,7 @@ impl RecursiveTypeDescription<'ll, 'tcx> { unfinished_type, unique_type_id, metadata_stub, + member_holding_stub, ref member_description_factory, } => { // Make sure that we have a forward declaration of the type in @@ -274,7 +272,7 @@ impl RecursiveTypeDescription<'ll, 'tcx> { // ... and attach them to the stub to complete it. set_members_of_composite_type(cx, - metadata_stub, + member_holding_stub, member_descriptions); return MetadataCreationResult::new(metadata_stub, true); } @@ -358,6 +356,7 @@ fn vec_slice_metadata( size: pointer_size, align: pointer_align, flags: DIFlags::FlagZero, + discriminant: None, }, MemberDescription { name: "length".to_owned(), @@ -366,6 +365,7 @@ fn vec_slice_metadata( size: usize_size, align: usize_align, flags: DIFlags::FlagZero, + discriminant: None, }, ]; @@ -466,6 +466,7 @@ fn trait_pointer_metadata( size: data_ptr_field.size, align: data_ptr_field.align, flags: DIFlags::FlagArtificial, + discriminant: None, }, MemberDescription { name: "vtable".to_owned(), @@ -474,6 +475,7 @@ fn trait_pointer_metadata( size: vtable_field.size, align: vtable_field.align, flags: DIFlags::FlagArtificial, + discriminant: None, }, ]; @@ -922,6 +924,7 @@ struct MemberDescription<'ll> { size: Size, align: Align, flags: DIFlags, + discriminant: Option<u64>, } // A factory for MemberDescriptions. It produces a list of member descriptions @@ -989,6 +992,7 @@ impl<'tcx> StructMemberDescriptionFactory<'tcx> { size, align, flags: DIFlags::FlagZero, + discriminant: None, } }).collect() } @@ -1021,6 +1025,7 @@ fn prepare_struct_metadata( struct_type, unique_type_id, struct_metadata_stub, + struct_metadata_stub, StructMDF(StructMemberDescriptionFactory { ty: struct_type, variant, @@ -1053,6 +1058,7 @@ impl<'tcx> TupleMemberDescriptionFactory<'tcx> { size, align, flags: DIFlags::FlagZero, + discriminant: None, } }).collect() } @@ -1067,15 +1073,18 @@ fn prepare_tuple_metadata( ) -> RecursiveTypeDescription<'ll, 'tcx> { let tuple_name = compute_debuginfo_type_name(cx, tuple_type, false); + let struct_stub = create_struct_stub(cx, + tuple_type, + &tuple_name[..], + unique_type_id, + NO_SCOPE_METADATA); + create_and_register_recursive_type_forward_declaration( cx, tuple_type, unique_type_id, - create_struct_stub(cx, - tuple_type, - &tuple_name[..], - unique_type_id, - NO_SCOPE_METADATA), + struct_stub, + struct_stub, TupleMDF(TupleMemberDescriptionFactory { ty: tuple_type, component_types: component_types.to_vec(), @@ -1107,6 +1116,7 @@ impl<'tcx> UnionMemberDescriptionFactory<'tcx> { size, align, flags: DIFlags::FlagZero, + discriminant: None, } }).collect() } @@ -1138,6 +1148,7 @@ fn prepare_union_metadata( union_type, unique_type_id, union_metadata_stub, + union_metadata_stub, UnionMDF(UnionMemberDescriptionFactory { layout: cx.layout_of(union_type), variant, @@ -1150,6 +1161,19 @@ fn prepare_union_metadata( // Enums //=----------------------------------------------------------------------------- +// DWARF variant support is only available starting in LLVM 7. +// Although the earlier enum debug info output did not work properly +// in all situations, it is better for the time being to continue to +// sometimes emit the old style rather than emit something completely +// useless when rust is compiled against LLVM 6 or older. This +// function decides which representation will be emitted. +fn use_enum_fallback(cx: &CodegenCx) -> bool { + // On MSVC we have to use the fallback mode, because LLVM doesn't + // lower variant parts to PDB. + return cx.sess().target.target.options.is_like_msvc + || llvm_util::get_major_version() < 7; +} + // Describes the members of an enum value: An enum is described as a union of // structs in DWARF. This MemberDescriptionFactory provides the description for // the members of this union; so for every variant of the given enum, this @@ -1167,6 +1191,15 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { fn create_member_descriptions(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec<MemberDescription<'ll>> { let adt = &self.enum_type.ty_adt_def().unwrap(); + + // This will always find the metadata in the type map. + let fallback = use_enum_fallback(cx); + let self_metadata = if fallback { + self.containing_scope + } else { + type_metadata(cx, self.enum_type, self.span) + }; + match self.layout.variants { layout::Variants::Single { .. } if adt.variants.is_empty() => vec![], layout::Variants::Single { index } => { @@ -1175,7 +1208,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { self.layout, &adt.variants[index], NoDiscriminant, - self.containing_scope, + self_metadata, self.span); let member_descriptions = @@ -1186,26 +1219,36 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { member_descriptions); vec![ MemberDescription { - name: String::new(), + name: if fallback { + String::new() + } else { + adt.variants[index].name.as_str().to_string() + }, type_metadata: variant_type_metadata, offset: Size::ZERO, size: self.layout.size, align: self.layout.align, - flags: DIFlags::FlagZero + flags: DIFlags::FlagZero, + discriminant: None, } ] } layout::Variants::Tagged { ref variants, .. } => { - let discriminant_info = RegularDiscriminant(self.discriminant_type_metadata - .expect("")); - (0..variants.len()).map(|i| { + let discriminant_info = if fallback { + RegularDiscriminant(self.discriminant_type_metadata + .expect("")) + } else { + // This doesn't matter in this case. + NoDiscriminant + }; + variants.iter_enumerated().map(|(i, _)| { let variant = self.layout.for_variant(cx, i); let (variant_type_metadata, member_desc_factory) = describe_enum_variant(cx, variant, &adt.variants[i], discriminant_info, - self.containing_scope, + self_metadata, self.span); let member_descriptions = member_desc_factory @@ -1215,75 +1258,127 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { variant_type_metadata, member_descriptions); MemberDescription { - name: String::new(), + name: if fallback { + String::new() + } else { + adt.variants[i].name.as_str().to_string() + }, type_metadata: variant_type_metadata, offset: Size::ZERO, - size: variant.size, - align: variant.align, - flags: DIFlags::FlagZero + size: self.layout.size, + align: self.layout.align, + flags: DIFlags::FlagZero, + discriminant: Some(self.layout.ty.ty_adt_def().unwrap() + .discriminant_for_variant(cx.tcx, i) + .val as u64), } }).collect() } - layout::Variants::NicheFilling { dataful_variant, ref niche_variants, .. } => { - let variant = self.layout.for_variant(cx, dataful_variant); - // Create a description of the non-null variant - let (variant_type_metadata, member_description_factory) = - describe_enum_variant(cx, - variant, - &adt.variants[dataful_variant], - OptimizedDiscriminant, - self.containing_scope, - self.span); + layout::Variants::NicheFilling { + ref niche_variants, + niche_start, + ref variants, + dataful_variant, + ref niche, + } => { + if fallback { + let variant = self.layout.for_variant(cx, dataful_variant); + // Create a description of the non-null variant + let (variant_type_metadata, member_description_factory) = + describe_enum_variant(cx, + variant, + &adt.variants[dataful_variant], + OptimizedDiscriminant, + self.containing_scope, + self.span); - let variant_member_descriptions = - member_description_factory.create_member_descriptions(cx); + let variant_member_descriptions = + member_description_factory.create_member_descriptions(cx); - set_members_of_composite_type(cx, - variant_type_metadata, - variant_member_descriptions); - - // Encode the information about the null variant in the union - // member's name. - let mut name = String::from("RUST$ENCODED$ENUM$"); - // HACK(eddyb) the debuggers should just handle offset+size - // of discriminant instead of us having to recover its path. - // Right now it's not even going to work for `niche_start > 0`, - // and for multiple niche variants it only supports the first. - fn compute_field_path<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - name: &mut String, - layout: TyLayout<'tcx>, - offset: Size, - size: Size) { - for i in 0..layout.fields.count() { - let field_offset = layout.fields.offset(i); - if field_offset > offset { - continue; - } - let inner_offset = offset - field_offset; - let field = layout.field(cx, i); - if inner_offset + size <= field.size { - write!(name, "{}$", i).unwrap(); - compute_field_path(cx, name, field, inner_offset, size); + set_members_of_composite_type(cx, + variant_type_metadata, + variant_member_descriptions); + + // Encode the information about the null variant in the union + // member's name. + let mut name = String::from("RUST$ENCODED$ENUM$"); + // Right now it's not even going to work for `niche_start > 0`, + // and for multiple niche variants it only supports the first. + fn compute_field_path<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + name: &mut String, + layout: TyLayout<'tcx>, + offset: Size, + size: Size) { + for i in 0..layout.fields.count() { + let field_offset = layout.fields.offset(i); + if field_offset > offset { + continue; + } + let inner_offset = offset - field_offset; + let field = layout.field(cx, i); + if inner_offset + size <= field.size { + write!(name, "{}$", i).unwrap(); + compute_field_path(cx, name, field, inner_offset, size); + } } } + compute_field_path(cx, &mut name, + self.layout, + self.layout.fields.offset(0), + self.layout.field(cx, 0).size); + name.push_str(&adt.variants[*niche_variants.start()].name.as_str()); + + // Create the (singleton) list of descriptions of union members. + vec![ + MemberDescription { + name, + type_metadata: variant_type_metadata, + offset: Size::ZERO, + size: variant.size, + align: variant.align, + flags: DIFlags::FlagZero, + discriminant: None, + } + ] + } else { + variants.iter_enumerated().map(|(i, _)| { + let variant = self.layout.for_variant(cx, i); + let (variant_type_metadata, member_desc_factory) = + describe_enum_variant(cx, + variant, + &adt.variants[i], + OptimizedDiscriminant, + self_metadata, + self.span); + + let member_descriptions = member_desc_factory + .create_member_descriptions(cx); + + set_members_of_composite_type(cx, + variant_type_metadata, + member_descriptions); + + let niche_value = if i == dataful_variant { + None + } else { + let value = (i.as_u32() as u128) + .wrapping_sub(niche_variants.start().as_u32() as u128) + .wrapping_add(niche_start); + let value = value & ((1u128 << niche.value.size(cx).bits()) - 1); + Some(value as u64) + }; + + MemberDescription { + name: adt.variants[i].name.as_str().to_string(), + type_metadata: variant_type_metadata, + offset: Size::ZERO, + size: self.layout.size, + align: self.layout.align, + flags: DIFlags::FlagZero, + discriminant: niche_value, + } + }).collect() } - compute_field_path(cx, &mut name, - self.layout, - self.layout.fields.offset(0), - self.layout.field(cx, 0).size); - name.push_str(&adt.variants[*niche_variants.start()].name.as_str()); - - // Create the (singleton) list of descriptions of union members. - vec![ - MemberDescription { - name, - type_metadata: variant_type_metadata, - offset: Size::ZERO, - size: variant.size, - align: variant.align, - flags: DIFlags::FlagZero - } - ] } } } @@ -1305,14 +1400,19 @@ impl VariantMemberDescriptionFactory<'ll, 'tcx> { let (size, align) = cx.size_and_align_of(ty); MemberDescription { name: name.to_string(), - type_metadata: match self.discriminant_type_metadata { - Some(metadata) if i == 0 => metadata, - _ => type_metadata(cx, ty, self.span) + type_metadata: if use_enum_fallback(cx) { + match self.discriminant_type_metadata { + Some(metadata) if i == 0 => metadata, + _ => type_metadata(cx, ty, self.span) + } + } else { + type_metadata(cx, ty, self.span) }, offset: self.offsets[i], size, align, - flags: DIFlags::FlagZero + flags: DIFlags::FlagZero, + discriminant: None, } }).collect() } @@ -1325,10 +1425,10 @@ enum EnumDiscriminantInfo<'ll> { NoDiscriminant } -// Returns a tuple of (1) type_metadata_stub of the variant, (2) the llvm_type -// of the variant, and (3) a MemberDescriptionFactory for producing the -// descriptions of the fields of the variant. This is a rudimentary version of a -// full RecursiveTypeDescription. +// Returns a tuple of (1) type_metadata_stub of the variant, (2) a +// MemberDescriptionFactory for producing the descriptions of the +// fields of the variant. This is a rudimentary version of a full +// RecursiveTypeDescription. fn describe_enum_variant( cx: &CodegenCx<'ll, 'tcx>, layout: layout::TyLayout<'tcx>, @@ -1351,29 +1451,46 @@ fn describe_enum_variant( unique_type_id, Some(containing_scope)); - // If this is not a univariant enum, there is also the discriminant field. - let (discr_offset, discr_arg) = match discriminant_info { - RegularDiscriminant(_) => { - // We have the layout of an enum variant, we need the layout of the outer enum - let enum_layout = cx.layout_of(layout.ty); - (Some(enum_layout.fields.offset(0)), - Some(("RUST$ENUM$DISR".to_owned(), enum_layout.field(cx, 0).ty))) - } - _ => (None, None), - }; - let offsets = discr_offset.into_iter().chain((0..layout.fields.count()).map(|i| { - layout.fields.offset(i) - })).collect(); - // Build an array of (field name, field type) pairs to be captured in the factory closure. - let args = discr_arg.into_iter().chain((0..layout.fields.count()).map(|i| { - let name = if variant.ctor_kind == CtorKind::Fn { - format!("__{}", i) - } else { - variant.fields[i].ident.to_string() + let (offsets, args) = if use_enum_fallback(cx) { + // If this is not a univariant enum, there is also the discriminant field. + let (discr_offset, discr_arg) = match discriminant_info { + RegularDiscriminant(_) => { + // We have the layout of an enum variant, we need the layout of the outer enum + let enum_layout = cx.layout_of(layout.ty); + (Some(enum_layout.fields.offset(0)), + Some(("RUST$ENUM$DISR".to_owned(), enum_layout.field(cx, 0).ty))) + } + _ => (None, None), }; - (name, layout.field(cx, i).ty) - })).collect(); + ( + discr_offset.into_iter().chain((0..layout.fields.count()).map(|i| { + layout.fields.offset(i) + })).collect(), + discr_arg.into_iter().chain((0..layout.fields.count()).map(|i| { + let name = if variant.ctor_kind == CtorKind::Fn { + format!("__{}", i) + } else { + variant.fields[i].ident.to_string() + }; + (name, layout.field(cx, i).ty) + })).collect() + ) + } else { + ( + (0..layout.fields.count()).map(|i| { + layout.fields.offset(i) + }).collect(), + (0..layout.fields.count()).map(|i| { + let name = if variant.ctor_kind == CtorKind::Fn { + format!("__{}", i) + } else { + variant.fields[i].ident.to_string() + }; + (name, layout.field(cx, i).ty) + }).collect() + ) + }; let member_description_factory = VariantMDF(VariantMemberDescriptionFactory { @@ -1409,22 +1526,22 @@ fn prepare_enum_metadata( // <unknown> let file_metadata = unknown_file_metadata(cx); - let def = enum_type.ty_adt_def().unwrap(); - let enumerators_metadata: Vec<_> = def.discriminants(cx.tcx) - .zip(&def.variants) - .map(|(discr, v)| { - let name = SmallCStr::new(&v.name.as_str()); - unsafe { - Some(llvm::LLVMRustDIBuilderCreateEnumerator( - DIB(cx), - name.as_ptr(), - // FIXME: what if enumeration has i128 discriminant? - discr.val as u64)) - } - }) - .collect(); - let discriminant_type_metadata = |discr: layout::Primitive| { + let def = enum_type.ty_adt_def().unwrap(); + let enumerators_metadata: Vec<_> = def.discriminants(cx.tcx) + .zip(&def.variants) + .map(|((_, discr), v)| { + let name = SmallCStr::new(&v.name.as_str()); + unsafe { + Some(llvm::LLVMRustDIBuilderCreateEnumerator( + DIB(cx), + name.as_ptr(), + // FIXME: what if enumeration has i128 discriminant? + discr.val as u64)) + } + }) + .collect(); + let disr_type_key = (enum_def_id, discr); let cached_discriminant_type_metadata = debug_context(cx).created_enum_disr_types .borrow() @@ -1449,7 +1566,7 @@ fn prepare_enum_metadata( discriminant_size.bits(), discriminant_align.abi_bits() as u32, create_DIArray(DIB(cx), &enumerators_metadata), - discriminant_base_type_metadata) + discriminant_base_type_metadata, true) }; debug_context(cx).created_enum_disr_types @@ -1463,16 +1580,10 @@ fn prepare_enum_metadata( let layout = cx.layout_of(enum_type); - let discriminant_type_metadata = match layout.variants { - layout::Variants::Single { .. } | - layout::Variants::NicheFilling { .. } => None, - layout::Variants::Tagged { ref tag, .. } => { - Some(discriminant_type_metadata(tag.value)) - } - }; - - if let (&layout::Abi::Scalar(_), Some(discr)) = (&layout.abi, discriminant_type_metadata) { - return FinalMetadata(discr); + match (&layout.abi, &layout.variants) { + (&layout::Abi::Scalar(_), &layout::Variants::Tagged {ref tag, .. }) => + return FinalMetadata(discriminant_type_metadata(tag.value)), + _ => {} } let (enum_type_size, enum_type_align) = layout.size_and_align(); @@ -1481,30 +1592,145 @@ fn prepare_enum_metadata( let unique_type_id_str = SmallCStr::new( debug_context(cx).type_map.borrow().get_unique_type_id_as_string(unique_type_id) ); - let enum_metadata = unsafe { - llvm::LLVMRustDIBuilderCreateUnionType( - DIB(cx), - containing_scope, - enum_name.as_ptr(), - file_metadata, - UNKNOWN_LINE_NUMBER, - enum_type_size.bits(), - enum_type_align.abi_bits() as u32, - DIFlags::FlagZero, - None, - 0, // RuntimeLang - unique_type_id_str.as_ptr()) + + if use_enum_fallback(cx) { + let discriminant_type_metadata = match layout.variants { + layout::Variants::Single { .. } | + layout::Variants::NicheFilling { .. } => None, + layout::Variants::Tagged { ref tag, .. } => { + Some(discriminant_type_metadata(tag.value)) + } + }; + + let enum_metadata = unsafe { + llvm::LLVMRustDIBuilderCreateUnionType( + DIB(cx), + containing_scope, + enum_name.as_ptr(), + file_metadata, + UNKNOWN_LINE_NUMBER, + enum_type_size.bits(), + enum_type_align.abi_bits() as u32, + DIFlags::FlagZero, + None, + 0, // RuntimeLang + unique_type_id_str.as_ptr()) + }; + + return create_and_register_recursive_type_forward_declaration( + cx, + enum_type, + unique_type_id, + enum_metadata, + enum_metadata, + EnumMDF(EnumMemberDescriptionFactory { + enum_type, + layout, + discriminant_type_metadata, + containing_scope, + span, + }), + ); + } + + let discriminator_metadata = match &layout.variants { + // A single-variant enum has no discriminant. + &layout::Variants::Single { .. } => None, + + &layout::Variants::NicheFilling { ref niche, .. } => { + // Find the integer type of the correct size. + let size = niche.value.size(cx); + let align = niche.value.align(cx); + + let discr_type = match niche.value { + layout::Int(t, _) => t, + layout::Float(layout::FloatTy::F32) => Integer::I32, + layout::Float(layout::FloatTy::F64) => Integer::I64, + layout::Pointer => cx.data_layout().ptr_sized_integer(), + }.to_ty(cx.tcx, false); + + let discr_metadata = basic_type_metadata(cx, discr_type); + unsafe { + Some(llvm::LLVMRustDIBuilderCreateMemberType( + DIB(cx), + containing_scope, + ptr::null_mut(), + file_metadata, + UNKNOWN_LINE_NUMBER, + size.bits(), + align.abi_bits() as u32, + layout.fields.offset(0).bits(), + DIFlags::FlagArtificial, + discr_metadata)) + } + }, + + &layout::Variants::Tagged { ref tag, .. } => { + let discr_type = tag.value.to_ty(cx.tcx); + let (size, align) = cx.size_and_align_of(discr_type); + + let discr_metadata = basic_type_metadata(cx, discr_type); + unsafe { + Some(llvm::LLVMRustDIBuilderCreateMemberType( + DIB(cx), + containing_scope, + ptr::null_mut(), + file_metadata, + UNKNOWN_LINE_NUMBER, + size.bits(), + align.abi_bits() as u32, + layout.fields.offset(0).bits(), + DIFlags::FlagArtificial, + discr_metadata)) + } + }, + }; + + let empty_array = create_DIArray(DIB(cx), &[]); + let variant_part = unsafe { + llvm::LLVMRustDIBuilderCreateVariantPart( + DIB(cx), + containing_scope, + ptr::null_mut(), + file_metadata, + UNKNOWN_LINE_NUMBER, + enum_type_size.bits(), + enum_type_align.abi_bits() as u32, + DIFlags::FlagZero, + discriminator_metadata, + empty_array, + unique_type_id_str.as_ptr()) + }; + + // The variant part must be wrapped in a struct according to DWARF. + let type_array = create_DIArray(DIB(cx), &[Some(variant_part)]); + let struct_wrapper = unsafe { + llvm::LLVMRustDIBuilderCreateStructType( + DIB(cx), + Some(containing_scope), + enum_name.as_ptr(), + file_metadata, + UNKNOWN_LINE_NUMBER, + enum_type_size.bits(), + enum_type_align.abi_bits() as u32, + DIFlags::FlagZero, + None, + type_array, + 0, + None, + unique_type_id_str.as_ptr()) }; return create_and_register_recursive_type_forward_declaration( cx, enum_type, unique_type_id, - enum_metadata, + struct_wrapper, + variant_part, EnumMDF(EnumMemberDescriptionFactory { enum_type, layout, - discriminant_type_metadata, + discriminant_type_metadata: None, containing_scope, span, }), @@ -1573,7 +1799,7 @@ fn set_members_of_composite_type(cx: &CodegenCx<'ll, '_>, .map(|member_description| { let member_name = CString::new(member_description.name).unwrap(); unsafe { - Some(llvm::LLVMRustDIBuilderCreateMemberType( + Some(llvm::LLVMRustDIBuilderCreateVariantMemberType( DIB(cx), composite_type_metadata, member_name.as_ptr(), @@ -1582,6 +1808,10 @@ fn set_members_of_composite_type(cx: &CodegenCx<'ll, '_>, member_description.size.bits(), member_description.align.abi_bits() as u32, member_description.offset.bits(), + match member_description.discriminant { + None => None, + Some(value) => Some(C_u64(cx, value)), + }, member_description.flags, member_description.type_metadata)) } diff --git a/src/librustc_codegen_llvm/debuginfo/mod.rs b/src/librustc_codegen_llvm/debuginfo/mod.rs index acb79d6f568..042e72e921e 100644 --- a/src/librustc_codegen_llvm/debuginfo/mod.rs +++ b/src/librustc_codegen_llvm/debuginfo/mod.rs @@ -100,11 +100,11 @@ impl<'a, 'tcx> CrateDebugContext<'a, 'tcx> { llcontext, llmod, builder, - created_files: RefCell::new(FxHashMap()), - created_enum_disr_types: RefCell::new(FxHashMap()), - type_map: RefCell::new(TypeMap::new()), + created_files: Default::default(), + created_enum_disr_types: Default::default(), + type_map: Default::default(), namespace_map: RefCell::new(DefIdMap()), - composite_types_completed: RefCell::new(FxHashSet()), + composite_types_completed: Default::default(), } } } diff --git a/src/librustc_codegen_llvm/debuginfo/type_names.rs b/src/librustc_codegen_llvm/debuginfo/type_names.rs index f5abb527e43..eb5ae81b218 100644 --- a/src/librustc_codegen_llvm/debuginfo/type_names.rs +++ b/src/librustc_codegen_llvm/debuginfo/type_names.rs @@ -173,6 +173,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, ty::Infer(_) | ty::UnnormalizedProjection(..) | ty::Projection(..) | + ty::Bound(..) | ty::Opaque(..) | ty::GeneratorWitness(..) | ty::Param(_) => { diff --git a/src/librustc_codegen_llvm/declare.rs b/src/librustc_codegen_llvm/declare.rs index 26969e24f08..f4aede55ce1 100644 --- a/src/librustc_codegen_llvm/declare.rs +++ b/src/librustc_codegen_llvm/declare.rs @@ -22,7 +22,7 @@ use llvm; use llvm::AttributePlace::Function; -use rustc::ty::{self, Ty}; +use rustc::ty::{self, PolyFnSig}; use rustc::ty::layout::LayoutOf; use rustc::session::config::Sanitizer; use rustc_data_structures::small_c_str::SmallCStr; @@ -30,7 +30,6 @@ use rustc_target::spec::PanicStrategy; use abi::{Abi, FnType, FnTypeExt}; use attributes; use context::CodegenCx; -use common; use type_::Type; use value::Value; @@ -129,10 +128,9 @@ pub fn declare_cfn(cx: &CodegenCx<'ll, '_>, name: &str, fn_type: &'ll Type) -> & pub fn declare_fn( cx: &CodegenCx<'ll, 'tcx>, name: &str, - fn_type: Ty<'tcx>, + sig: PolyFnSig<'tcx>, ) -> &'ll Value { - debug!("declare_rust_fn(name={:?}, fn_type={:?})", name, fn_type); - let sig = common::ty_fn_sig(cx, fn_type); + debug!("declare_rust_fn(name={:?}, sig={:?})", name, sig); let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); debug!("declare_rust_fn (after region erasure) sig={:?}", sig); @@ -184,12 +182,12 @@ pub fn define_private_global(cx: &CodegenCx<'ll, '_>, ty: &'ll Type) -> &'ll Val pub fn define_fn( cx: &CodegenCx<'ll, 'tcx>, name: &str, - fn_type: Ty<'tcx>, + fn_sig: PolyFnSig<'tcx>, ) -> &'ll Value { if get_defined_value(cx, name).is_some() { cx.sess().fatal(&format!("symbol `{}` already defined", name)) } else { - declare_fn(cx, name, fn_type) + declare_fn(cx, name, fn_sig) } } @@ -201,9 +199,9 @@ pub fn define_fn( pub fn define_internal_fn( cx: &CodegenCx<'ll, 'tcx>, name: &str, - fn_type: Ty<'tcx>, + fn_sig: PolyFnSig<'tcx>, ) -> &'ll Value { - let llfn = define_fn(cx, name, fn_type); + let llfn = define_fn(cx, name, fn_sig); unsafe { llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::InternalLinkage) }; llfn } diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index 272196afa6f..a5f90149f4a 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -13,6 +13,7 @@ use attributes; use intrinsics::{self, Intrinsic}; use llvm::{self, TypeKind}; +use llvm_util; use abi::{Abi, FnType, LlvmType, PassMode}; use mir::place::PlaceRef; use mir::operand::{OperandRef, OperandValue}; @@ -23,7 +24,7 @@ use glue; use type_::Type; use type_of::LayoutLlvmExt; use rustc::ty::{self, Ty}; -use rustc::ty::layout::{HasDataLayout, LayoutOf}; +use rustc::ty::layout::LayoutOf; use rustc::hir; use syntax::ast; use syntax::symbol::Symbol; @@ -284,7 +285,8 @@ pub fn codegen_intrinsic_call( "ctlz" | "ctlz_nonzero" | "cttz" | "cttz_nonzero" | "ctpop" | "bswap" | "bitreverse" | "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" | "overflowing_add" | "overflowing_sub" | "overflowing_mul" | - "unchecked_div" | "unchecked_rem" | "unchecked_shl" | "unchecked_shr" | "exact_div" => { + "unchecked_div" | "unchecked_rem" | "unchecked_shl" | "unchecked_shr" | "exact_div" | + "rotate_left" | "rotate_right" => { let ty = arg_tys[0]; match int_type_width_signed(ty, cx) { Some((width, signed)) => @@ -363,6 +365,27 @@ pub fn codegen_intrinsic_call( } else { bx.lshr(args[0].immediate(), args[1].immediate()) }, + "rotate_left" | "rotate_right" => { + let is_left = name == "rotate_left"; + let val = args[0].immediate(); + let raw_shift = args[1].immediate(); + if llvm_util::get_major_version() >= 7 { + // rotate = funnel shift with first two args the same + let llvm_name = &format!("llvm.fsh{}.i{}", + if is_left { 'l' } else { 'r' }, width); + let llfn = cx.get_intrinsic(llvm_name); + bx.call(llfn, &[val, val, raw_shift], None) + } else { + // rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW)) + // rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW)) + let width = C_uint(Type::ix(cx, width), width); + let shift = bx.urem(raw_shift, width); + let inv_shift = bx.urem(bx.sub(width, raw_shift), width); + let shift1 = bx.shl(val, if is_left { shift } else { inv_shift }); + let shift2 = bx.lshr(val, if !is_left { shift } else { inv_shift }); + bx.or(shift1, shift2) + } + }, _ => bug!(), }, None => { @@ -477,8 +500,8 @@ pub fn codegen_intrinsic_call( "load" => { let ty = substs.type_at(0); if int_type_width_signed(ty, cx).is_some() { - let align = cx.align_of(ty); - bx.atomic_load(args[0].immediate(), order, align) + let size = cx.size_of(ty); + bx.atomic_load(args[0].immediate(), order, size) } else { return invalid_monomorphization(ty); } @@ -487,8 +510,8 @@ pub fn codegen_intrinsic_call( "store" => { let ty = substs.type_at(0); if int_type_width_signed(ty, cx).is_some() { - let align = cx.align_of(ty); - bx.atomic_store(args[1].immediate(), args[0].immediate(), order, align); + let size = cx.size_of(ty); + bx.atomic_store(args[1].immediate(), args[0].immediate(), order, size); return; } else { return invalid_monomorphization(ty); @@ -690,28 +713,14 @@ fn copy_intrinsic( let cx = bx.cx; let (size, align) = cx.size_and_align_of(ty); let size = C_usize(cx, size.bytes()); - let align = C_i32(cx, align.abi() as i32); - - let operation = if allow_overlap { - "memmove" - } else { - "memcpy" - }; - - let name = format!("llvm.{}.p0i8.p0i8.i{}", operation, - cx.data_layout().pointer_size.bits()); - + let align = align.abi(); let dst_ptr = bx.pointercast(dst, Type::i8p(cx)); let src_ptr = bx.pointercast(src, Type::i8p(cx)); - let llfn = cx.get_intrinsic(&name); - - bx.call(llfn, - &[dst_ptr, - src_ptr, - bx.mul(size, count), - align, - C_bool(cx, volatile)], - None) + if allow_overlap { + bx.memmove(dst_ptr, align, src_ptr, align, bx.mul(size, count), volatile) + } else { + bx.memcpy(dst_ptr, align, src_ptr, align, bx.mul(size, count), volatile) + } } fn memset_intrinsic( @@ -933,14 +942,14 @@ fn gen_fn<'ll, 'tcx>( output: Ty<'tcx>, codegen: &mut dyn FnMut(Builder<'_, 'll, 'tcx>), ) -> &'ll Value { - let rust_fn_ty = cx.tcx.mk_fn_ptr(ty::Binder::bind(cx.tcx.mk_fn_sig( + let rust_fn_sig = ty::Binder::bind(cx.tcx.mk_fn_sig( inputs.into_iter(), output, false, hir::Unsafety::Unsafe, Abi::Rust - ))); - let llfn = declare::define_internal_fn(cx, name, rust_fn_ty); + )); + let llfn = declare::define_internal_fn(cx, name, rust_fn_sig); attributes::from_fn_attrs(cx, llfn, None); let bx = Builder::new_block(cx, llfn, "entry-block"); codegen(bx); diff --git a/src/librustc_codegen_llvm/lib.rs b/src/librustc_codegen_llvm/lib.rs index 63a8ab077e5..5d9bae5412e 100644 --- a/src/librustc_codegen_llvm/lib.rs +++ b/src/librustc_codegen_llvm/lib.rs @@ -71,7 +71,6 @@ use back::bytecode::RLIB_BYTECODE_EXTENSION; pub use llvm_util::target_features; use std::any::Any; -use std::path::{PathBuf}; use std::sync::mpsc; use rustc_data_structures::sync::Lrc; @@ -87,20 +86,17 @@ use rustc::util::time_graph; use rustc::util::nodemap::{FxHashSet, FxHashMap}; use rustc::util::profiling::ProfileCategory; use rustc_mir::monomorphize; +use rustc_codegen_utils::{CompiledModule, ModuleKind}; use rustc_codegen_utils::codegen_backend::CodegenBackend; use rustc_data_structures::svh::Svh; mod diagnostics; mod back { - pub use rustc_codegen_utils::symbol_names; mod archive; pub mod bytecode; - mod command; - pub mod linker; pub mod link; pub mod lto; - pub mod symbol_export; pub mod write; mod rpath; pub mod wasm; @@ -194,15 +190,15 @@ impl CodegenBackend for LlvmCodegenBackend { } fn provide(&self, providers: &mut ty::query::Providers) { - back::symbol_names::provide(providers); - back::symbol_export::provide(providers); - base::provide(providers); + rustc_codegen_utils::symbol_export::provide(providers); + rustc_codegen_utils::symbol_names::provide(providers); + base::provide_both(providers); attributes::provide(providers); } fn provide_extern(&self, providers: &mut ty::query::Providers) { - back::symbol_export::provide_extern(providers); - base::provide_extern(providers); + rustc_codegen_utils::symbol_export::provide_extern(providers); + base::provide_both(providers); attributes::provide_extern(providers); } @@ -281,13 +277,6 @@ struct CachedModuleCodegen { source: WorkProduct, } -#[derive(Copy, Clone, Debug, PartialEq)] -enum ModuleKind { - Regular, - Metadata, - Allocator, -} - impl ModuleCodegen { fn into_compiled_module(self, emit_obj: bool, @@ -321,15 +310,6 @@ impl ModuleCodegen { } } -#[derive(Debug)] -struct CompiledModule { - name: String, - kind: ModuleKind, - object: Option<PathBuf>, - bytecode: Option<PathBuf>, - bytecode_compressed: Option<PathBuf>, -} - struct ModuleLlvm { llcx: &'static mut llvm::Context, llmod_raw: *const llvm::Module, @@ -377,7 +357,7 @@ struct CodegenResults { crate_hash: Svh, metadata: rustc::middle::cstore::EncodedMetadata, windows_subsystem: Option<String>, - linker_info: back::linker::LinkerInfo, + linker_info: rustc_codegen_utils::linker::LinkerInfo, crate_info: CrateInfo, } diff --git a/src/librustc_codegen_llvm/llvm/archive_ro.rs b/src/librustc_codegen_llvm/llvm/archive_ro.rs index e0a9f31e508..2a77f256e3a 100644 --- a/src/librustc_codegen_llvm/llvm/archive_ro.rs +++ b/src/librustc_codegen_llvm/llvm/archive_ro.rs @@ -40,7 +40,7 @@ impl ArchiveRO { return unsafe { let s = path2cstr(dst); let ar = super::LLVMRustOpenArchive(s.as_ptr()).ok_or_else(|| { - super::last_error().unwrap_or("failed to open archive".to_owned()) + super::last_error().unwrap_or_else(|| "failed to open archive".to_owned()) })?; Ok(ArchiveRO { raw: ar }) }; diff --git a/src/librustc_codegen_llvm/llvm/diagnostic.rs b/src/librustc_codegen_llvm/llvm/diagnostic.rs index c41a5f74ae3..b080c51c83a 100644 --- a/src/librustc_codegen_llvm/llvm/diagnostic.rs +++ b/src/librustc_codegen_llvm/llvm/diagnostic.rs @@ -77,7 +77,7 @@ impl OptimizationDiagnostic<'ll> { ).ok() ).ok(); - let mut filename = filename.unwrap_or(String::new()); + let mut filename = filename.unwrap_or_default(); if filename.is_empty() { filename.push_str("<unknown file>"); } diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index 0b98fa4eaf5..612581c1ac6 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs @@ -998,6 +998,22 @@ extern "C" { Bundle: Option<&OperandBundleDef<'a>>, Name: *const c_char) -> &'a Value; + pub fn LLVMRustBuildMemCpy(B: &Builder<'a>, + Dst: &'a Value, + DstAlign: c_uint, + Src: &'a Value, + SrcAlign: c_uint, + Size: &'a Value, + IsVolatile: bool) + -> &'a Value; + pub fn LLVMRustBuildMemMove(B: &Builder<'a>, + Dst: &'a Value, + DstAlign: c_uint, + Src: &'a Value, + SrcAlign: c_uint, + Size: &'a Value, + IsVolatile: bool) + -> &'a Value; pub fn LLVMBuildSelect(B: &Builder<'a>, If: &'a Value, Then: &'a Value, @@ -1041,42 +1057,42 @@ extern "C" { pub fn LLVMRustBuildVectorReduceFAdd(B: &Builder<'a>, Acc: &'a Value, Src: &'a Value) - -> Option<&'a Value>; + -> &'a Value; pub fn LLVMRustBuildVectorReduceFMul(B: &Builder<'a>, Acc: &'a Value, Src: &'a Value) - -> Option<&'a Value>; + -> &'a Value; pub fn LLVMRustBuildVectorReduceAdd(B: &Builder<'a>, Src: &'a Value) - -> Option<&'a Value>; + -> &'a Value; pub fn LLVMRustBuildVectorReduceMul(B: &Builder<'a>, Src: &'a Value) - -> Option<&'a Value>; + -> &'a Value; pub fn LLVMRustBuildVectorReduceAnd(B: &Builder<'a>, Src: &'a Value) - -> Option<&'a Value>; + -> &'a Value; pub fn LLVMRustBuildVectorReduceOr(B: &Builder<'a>, Src: &'a Value) - -> Option<&'a Value>; + -> &'a Value; pub fn LLVMRustBuildVectorReduceXor(B: &Builder<'a>, Src: &'a Value) - -> Option<&'a Value>; + -> &'a Value; pub fn LLVMRustBuildVectorReduceMin(B: &Builder<'a>, Src: &'a Value, IsSigned: bool) - -> Option<&'a Value>; + -> &'a Value; pub fn LLVMRustBuildVectorReduceMax(B: &Builder<'a>, Src: &'a Value, IsSigned: bool) - -> Option<&'a Value>; + -> &'a Value; pub fn LLVMRustBuildVectorReduceFMin(B: &Builder<'a>, Src: &'a Value, IsNaN: bool) - -> Option<&'a Value>; + -> &'a Value; pub fn LLVMRustBuildVectorReduceFMax(B: &Builder<'a>, Src: &'a Value, IsNaN: bool) - -> Option<&'a Value>; + -> &'a Value; pub fn LLVMRustBuildMinNum( B: &Builder<'a>, @@ -1157,7 +1173,7 @@ extern "C" { RunInliner: Bool); pub fn LLVMRustPassManagerBuilderPopulateThinLTOPassManager( PMB: &PassManagerBuilder, - PM: &PassManager) -> bool; + PM: &PassManager); // Stuff that's in rustllvm/ because it's not upstream yet. @@ -1307,6 +1323,19 @@ extern "C" { Ty: &'a DIType) -> &'a DIDerivedType; + pub fn LLVMRustDIBuilderCreateVariantMemberType(Builder: &DIBuilder<'a>, + Scope: &'a DIScope, + Name: *const c_char, + File: &'a DIFile, + LineNumber: c_uint, + SizeInBits: u64, + AlignInBits: u32, + OffsetInBits: u64, + Discriminant: Option<&'a Value>, + Flags: DIFlags, + Ty: &'a DIType) + -> &'a DIType; + pub fn LLVMRustDIBuilderCreateLexicalBlock(Builder: &DIBuilder<'a>, Scope: &'a DIScope, File: &'a DIFile, @@ -1384,7 +1413,8 @@ extern "C" { SizeInBits: u64, AlignInBits: u32, Elements: &'a DIArray, - ClassType: &'a DIType) + ClassType: &'a DIType, + IsFixed: bool) -> &'a DIType; pub fn LLVMRustDIBuilderCreateUnionType(Builder: &DIBuilder<'a>, @@ -1400,6 +1430,19 @@ extern "C" { UniqueId: *const c_char) -> &'a DIType; + pub fn LLVMRustDIBuilderCreateVariantPart(Builder: &DIBuilder<'a>, + Scope: &'a DIScope, + Name: *const c_char, + File: &'a DIFile, + LineNo: c_uint, + SizeInBits: u64, + AlignInBits: u32, + Flags: DIFlags, + Discriminator: Option<&'a DIDerivedType>, + Elements: &'a DIArray, + UniqueId: *const c_char) + -> &'a DIDerivedType; + pub fn LLVMSetUnnamedAddr(GlobalVar: &Value, UnnamedAddr: Bool); pub fn LLVMRustDIBuilderCreateTemplateTypeParameter(Builder: &DIBuilder<'a>, @@ -1599,8 +1642,6 @@ extern "C" { pub fn LLVMRustModuleBufferFree(p: &'static mut ModuleBuffer); pub fn LLVMRustModuleCost(M: &Module) -> u64; - pub fn LLVMRustThinLTOAvailable() -> bool; - pub fn LLVMRustPGOAvailable() -> bool; pub fn LLVMRustThinLTOBufferCreate(M: &Module) -> &'static mut ThinLTOBuffer; pub fn LLVMRustThinLTOBufferFree(M: &'static mut ThinLTOBuffer); pub fn LLVMRustThinLTOBufferPtr(M: &ThinLTOBuffer) -> *const c_char; diff --git a/src/librustc_codegen_llvm/llvm/mod.rs b/src/librustc_codegen_llvm/llvm/mod.rs index 4343c8c184e..fbd5192a63f 100644 --- a/src/librustc_codegen_llvm/llvm/mod.rs +++ b/src/librustc_codegen_llvm/llvm/mod.rs @@ -190,7 +190,7 @@ impl ObjectFile { pub fn new(llmb: &'static mut MemoryBuffer) -> Option<ObjectFile> { unsafe { let llof = LLVMCreateObjectFile(llmb)?; - Some(ObjectFile { llof: llof }) + Some(ObjectFile { llof }) } } } diff --git a/src/librustc_codegen_llvm/llvm_util.rs b/src/librustc_codegen_llvm/llvm_util.rs index 0a80fdddbf9..267d7e0d54b 100644 --- a/src/librustc_codegen_llvm/llvm_util.rs +++ b/src/librustc_codegen_llvm/llvm_util.rs @@ -184,7 +184,7 @@ const WASM_WHITELIST: &[(&str, Option<&str>)] = &[ ]; /// When rustdoc is running, provide a list of all known features so that all their respective -/// primtives may be documented. +/// primitives may be documented. /// /// IMPORTANT: If you're adding another whitelist to the above lists, make sure to add it to this /// iterator! @@ -243,7 +243,8 @@ pub fn target_feature_whitelist(sess: &Session) "hexagon" => HEXAGON_WHITELIST, "mips" | "mips64" => MIPS_WHITELIST, "powerpc" | "powerpc64" => POWERPC_WHITELIST, - "wasm32" => WASM_WHITELIST, + // wasm32 on emscripten does not support these target features + "wasm32" if !sess.target.target.options.is_like_emscripten => WASM_WHITELIST, _ => &[], } } @@ -256,6 +257,10 @@ pub fn print_version() { } } +pub fn get_major_version() -> u32 { + unsafe { llvm::LLVMRustVersionMajor() } +} + pub fn print_passes() { // Can be called without initializing LLVM unsafe { llvm::LLVMRustPrintPasses(); } diff --git a/src/librustc_codegen_llvm/meth.rs b/src/librustc_codegen_llvm/meth.rs index 29c2e71960c..0dc5a4ddde8 100644 --- a/src/librustc_codegen_llvm/meth.rs +++ b/src/librustc_codegen_llvm/meth.rs @@ -39,7 +39,7 @@ impl<'a, 'tcx> VirtualIndex { // Load the data pointer from the object. debug!("get_fn({:?}, {:?})", llvtable, self); - let llvtable = bx.pointercast(llvtable, fn_ty.llvm_type(bx.cx).ptr_to().ptr_to()); + let llvtable = bx.pointercast(llvtable, fn_ty.ptr_to_llvm_type(bx.cx).ptr_to()); let ptr_align = bx.tcx().data_layout.pointer_align; let ptr = bx.load(bx.inbounds_gep(llvtable, &[C_usize(bx.cx, self.0)]), ptr_align); bx.nonnull_metadata(ptr); @@ -89,7 +89,7 @@ pub fn get_vtable( let methods = tcx.vtable_methods(trait_ref.with_self_ty(tcx, ty)); let methods = methods.iter().cloned().map(|opt_mth| { opt_mth.map_or(nullptr, |(def_id, substs)| { - callee::resolve_and_get_fn(cx, def_id, substs) + callee::resolve_and_get_fn_for_vtable(cx, def_id, substs) }) }); diff --git a/src/librustc_codegen_llvm/mir/analyze.rs b/src/librustc_codegen_llvm/mir/analyze.rs index a0d6cc46295..2af772bd7ce 100644 --- a/src/librustc_codegen_llvm/mir/analyze.rs +++ b/src/librustc_codegen_llvm/mir/analyze.rs @@ -15,7 +15,7 @@ use rustc_data_structures::bit_set::BitSet; use rustc_data_structures::graph::dominators::Dominators; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc::mir::{self, Location, TerminatorKind}; -use rustc::mir::visit::{Visitor, PlaceContext}; +use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext}; use rustc::mir::traversal; use rustc::ty; use rustc::ty::layout::LayoutOf; @@ -116,7 +116,11 @@ impl Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'll, 'tcx> { self.not_ssa(index); } } else { - self.visit_place(place, PlaceContext::Store, location); + self.visit_place( + place, + PlaceContext::MutatingUse(MutatingUseContext::Store), + location + ); } self.visit_rvalue(rvalue, location); @@ -142,7 +146,11 @@ impl Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'll, 'tcx> { // is not guaranteed to be statically dominated by the // definition of x, so x must always be in an alloca. if let mir::Operand::Move(ref place) = args[0] { - self.visit_place(place, PlaceContext::Drop, location); + self.visit_place( + place, + PlaceContext::MutatingUse(MutatingUseContext::Drop), + location + ); } } } @@ -160,7 +168,8 @@ impl Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'll, 'tcx> { if let mir::Place::Projection(ref proj) = *place { // Allow uses of projections that are ZSTs or from scalar fields. let is_consume = match context { - PlaceContext::Copy | PlaceContext::Move => true, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => true, _ => false }; if is_consume { @@ -168,7 +177,9 @@ impl Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'll, 'tcx> { let base_ty = self.fx.monomorphize(&base_ty); // ZSTs don't require any actual memory access. - let elem_ty = base_ty.projection_ty(cx.tcx, &proj.elem).to_ty(cx.tcx); + let elem_ty = base_ty + .projection_ty(cx.tcx, &proj.elem) + .to_ty(cx.tcx); let elem_ty = self.fx.monomorphize(&elem_ty); if cx.layout_of(elem_ty).is_zst() { return; @@ -188,7 +199,11 @@ impl Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'll, 'tcx> { // A deref projection only reads the pointer, never needs the place. if let mir::ProjectionElem::Deref = proj.elem { - return self.visit_place(&proj.base, PlaceContext::Copy, location); + return self.visit_place( + &proj.base, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), + location + ); } } @@ -200,16 +215,15 @@ impl Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'll, 'tcx> { context: PlaceContext<'tcx>, location: Location) { match context { - PlaceContext::Call => { + PlaceContext::MutatingUse(MutatingUseContext::Call) => { self.assign(local, location); } - PlaceContext::StorageLive | - PlaceContext::StorageDead | - PlaceContext::Validate => {} + PlaceContext::NonUse(_) | + PlaceContext::MutatingUse(MutatingUseContext::Retag) => {} - PlaceContext::Copy | - PlaceContext::Move => { + PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => { // Reads from uninitialized variables (e.g. in dead code, after // optimizations) require locals to be in (uninitialized) memory. // NB: there can be uninitialized reads of a local visited after @@ -225,15 +239,19 @@ impl Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'll, 'tcx> { } } - PlaceContext::Inspect | - PlaceContext::Store | - PlaceContext::AsmOutput | - PlaceContext::Borrow { .. } | - PlaceContext::Projection(..) => { + PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) | + PlaceContext::MutatingUse(MutatingUseContext::Store) | + PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) | + PlaceContext::MutatingUse(MutatingUseContext::Borrow(..)) | + PlaceContext::MutatingUse(MutatingUseContext::Projection) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow(..)) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow(..)) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow(..)) | + PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => { self.not_ssa(local); } - PlaceContext::Drop => { + PlaceContext::MutatingUse(MutatingUseContext::Drop) => { let ty = mir::Place::Local(local).ty(self.fx.mir, self.fx.cx.tcx); let ty = self.fx.monomorphize(&ty.to_ty(self.fx.cx.tcx)); @@ -328,7 +346,7 @@ pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec<mir::BasicBlock funclet, succ, kind); match kind { CleanupKind::NotCleanup => { - result[succ] = CleanupKind::Internal { funclet: funclet }; + result[succ] = CleanupKind::Internal { funclet }; } CleanupKind::Funclet => { if funclet != succ { diff --git a/src/librustc_codegen_llvm/mir/block.rs b/src/librustc_codegen_llvm/mir/block.rs index 68e30227185..3f9921a5cf9 100644 --- a/src/librustc_codegen_llvm/mir/block.rs +++ b/src/librustc_codegen_llvm/mir/block.rs @@ -298,8 +298,7 @@ impl FunctionCx<'a, 'll, 'tcx> { }; let (drop_fn, fn_ty) = match ty.sty { ty::Dynamic(..) => { - let fn_ty = drop_fn.ty(bx.cx.tcx); - let sig = common::ty_fn_sig(bx.cx, fn_ty); + let sig = drop_fn.fn_sig(bx.cx.tcx); let sig = bx.tcx().normalize_erasing_late_bound_regions( ty::ParamEnv::reveal_all(), &sig, @@ -643,14 +642,54 @@ impl FunctionCx<'a, 'll, 'tcx> { (&args[..], None) }; - for (i, arg) in first_args.iter().enumerate() { + 'make_args: for (i, arg) in first_args.iter().enumerate() { let mut op = self.codegen_operand(&bx, arg); + if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) { - if let Pair(data_ptr, meta) = op.val { + if let Pair(..) = op.val { + // In the case of Rc<Self>, we need to explicitly pass a + // *mut RcBox<Self> with a Scalar (not ScalarPair) ABI. This is a hack + // that is understood elsewhere in the compiler as a method on + // `dyn Trait`. + // To get a `*mut RcBox<Self>`, we just keep unwrapping newtypes until + // we get a value of a built-in pointer type + 'descend_newtypes: while !op.layout.ty.is_unsafe_ptr() + && !op.layout.ty.is_region_ptr() + { + 'iter_fields: for i in 0..op.layout.fields.count() { + let field = op.extract_field(&bx, i); + if !field.layout.is_zst() { + // we found the one non-zero-sized field that is allowed + // now find *its* non-zero-sized field, or stop if it's a + // pointer + op = field; + continue 'descend_newtypes + } + } + + span_bug!(span, "receiver has no non-zero-sized fields {:?}", op); + } + + // now that we have `*dyn Trait` or `&dyn Trait`, split it up into its + // data pointer and vtable. Look up the method in the vtable, and pass + // the data pointer as the first argument + match op.val { + Pair(data_ptr, meta) => { + llfn = Some(meth::VirtualIndex::from_index(idx) + .get_fn(&bx, meta, &fn_ty)); + llargs.push(data_ptr); + continue 'make_args + } + other => bug!("expected a Pair, got {:?}", other) + } + } else if let Ref(data_ptr, Some(meta), _) = op.val { + // by-value dynamic dispatch llfn = Some(meth::VirtualIndex::from_index(idx) .get_fn(&bx, meta, &fn_ty)); llargs.push(data_ptr); continue; + } else { + span_bug!(span, "can't codegen a virtual call on {:?}", op); } } @@ -745,7 +784,8 @@ impl FunctionCx<'a, 'll, 'tcx> { // have scary latent bugs around. let scratch = PlaceRef::alloca(bx, arg.layout, "arg"); - base::memcpy_ty(bx, scratch.llval, llval, op.layout, align, MemFlags::empty()); + base::memcpy_ty(bx, scratch.llval, scratch.align, llval, align, + op.layout, MemFlags::empty()); (scratch.llval, scratch.align, true) } else { (llval, align, true) diff --git a/src/librustc_codegen_llvm/mir/constant.rs b/src/librustc_codegen_llvm/mir/constant.rs index 9f0f7443890..586a4907740 100644 --- a/src/librustc_codegen_llvm/mir/constant.rs +++ b/src/librustc_codegen_llvm/mir/constant.rs @@ -9,12 +9,11 @@ // except according to those terms. use llvm; -use rustc::mir::interpret::{ConstEvalErr, read_target_uint}; +use rustc::mir::interpret::{ErrorHandled, read_target_uint}; use rustc_mir::const_eval::const_field; use rustc::hir::def_id::DefId; use rustc::mir; use rustc_data_structures::indexed_vec::Idx; -use rustc_data_structures::sync::Lrc; use rustc::mir::interpret::{GlobalId, Pointer, Scalar, Allocation, ConstValue, AllocType}; use rustc::ty::{self, Ty}; use rustc::ty::layout::{self, HasDataLayout, LayoutOf, Size}; @@ -88,8 +87,8 @@ pub fn scalar_to_llvm( pub fn const_alloc_to_llvm(cx: &CodegenCx<'ll, '_>, alloc: &Allocation) -> &'ll Value { let mut llvals = Vec::with_capacity(alloc.relocations.len() + 1); - let layout = cx.data_layout(); - let pointer_size = layout.pointer_size.bytes() as usize; + let dl = cx.data_layout(); + let pointer_size = dl.pointer_size.bytes() as usize; let mut next_offset = 0; for &(offset, ((), alloc_id)) in alloc.relocations.iter() { @@ -100,7 +99,7 @@ pub fn const_alloc_to_llvm(cx: &CodegenCx<'ll, '_>, alloc: &Allocation) -> &'ll llvals.push(C_bytes(cx, &alloc.bytes[next_offset..offset])); } let ptr_offset = read_target_uint( - layout.endian, + dl.endian, &alloc.bytes[offset..(offset + pointer_size)], ).expect("const_alloc_to_llvm: could not read relocation pointer") as u64; llvals.push(scalar_to_llvm( @@ -124,7 +123,7 @@ pub fn const_alloc_to_llvm(cx: &CodegenCx<'ll, '_>, alloc: &Allocation) -> &'ll pub fn codegen_static_initializer( cx: &CodegenCx<'ll, 'tcx>, def_id: DefId, -) -> Result<(&'ll Value, &'tcx Allocation), Lrc<ConstEvalErr<'tcx>>> { +) -> Result<(&'ll Value, &'tcx Allocation), ErrorHandled> { let instance = ty::Instance::mono(cx.tcx, def_id); let cid = GlobalId { instance, @@ -145,7 +144,7 @@ impl FunctionCx<'a, 'll, 'tcx> { &mut self, bx: &Builder<'a, 'll, 'tcx>, constant: &'tcx ty::Const<'tcx>, - ) -> Result<&'tcx ty::Const<'tcx>, Lrc<ConstEvalErr<'tcx>>> { + ) -> Result<&'tcx ty::Const<'tcx>, ErrorHandled> { match constant.val { ConstValue::Unevaluated(def_id, ref substs) => { let tcx = bx.tcx(); @@ -165,7 +164,7 @@ impl FunctionCx<'a, 'll, 'tcx> { &mut self, bx: &Builder<'a, 'll, 'tcx>, constant: &mir::Constant<'tcx>, - ) -> Result<&'tcx ty::Const<'tcx>, Lrc<ConstEvalErr<'tcx>>> { + ) -> Result<&'tcx ty::Const<'tcx>, ErrorHandled> { let c = self.monomorphize(&constant.literal); self.fully_evaluate(bx, c) } @@ -176,7 +175,7 @@ impl FunctionCx<'a, 'll, 'tcx> { bx: &Builder<'a, 'll, 'tcx>, span: Span, ty: Ty<'tcx>, - constant: Result<&'tcx ty::Const<'tcx>, Lrc<ConstEvalErr<'tcx>>>, + constant: Result<&'tcx ty::Const<'tcx>, ErrorHandled>, ) -> (&'ll Value, Ty<'tcx>) { constant .and_then(|c| { @@ -185,7 +184,7 @@ impl FunctionCx<'a, 'll, 'tcx> { ty::Array(_, n) => n.unwrap_usize(bx.tcx()), ref other => bug!("invalid simd shuffle type: {}", other), }; - let values: Result<Vec<_>, Lrc<_>> = (0..fields).map(|field| { + let values: Result<Vec<_>, ErrorHandled> = (0..fields).map(|field| { let field = const_field( bx.tcx(), ty::ParamEnv::reveal_all(), @@ -211,9 +210,9 @@ impl FunctionCx<'a, 'll, 'tcx> { let llval = C_struct(bx.cx, &values?, false); Ok((llval, c.ty)) }) - .unwrap_or_else(|e| { - e.report_as_error( - bx.tcx().at(span), + .unwrap_or_else(|_| { + bx.tcx().sess.span_err( + span, "could not evaluate shuffle_indices at compile time", ); // We've errored, so we don't have to produce working code. diff --git a/src/librustc_codegen_llvm/mir/mod.rs b/src/librustc_codegen_llvm/mir/mod.rs index a6e2ccf92e4..e5b25ea068b 100644 --- a/src/librustc_codegen_llvm/mir/mod.rs +++ b/src/librustc_codegen_llvm/mir/mod.rs @@ -12,6 +12,7 @@ use common::{C_i32, C_null}; use libc::c_uint; use llvm::{self, BasicBlock}; use llvm::debuginfo::DIScope; +use llvm_util; use rustc::ty::{self, Ty, TypeFoldable, UpvarSubsts}; use rustc::ty::layout::{LayoutOf, TyLayout}; use rustc::mir::{self, Mir}; @@ -612,7 +613,7 @@ fn arg_local_refs( // doesn't actually strip the offset when splitting the closure // environment into its components so it ends up out of bounds. // (cuviper) It seems to be fine without the alloca on LLVM 6 and later. - let env_alloca = !env_ref && unsafe { llvm::LLVMRustVersionMajor() < 6 }; + let env_alloca = !env_ref && llvm_util::get_major_version() < 6; let env_ptr = if env_alloca { let scratch = PlaceRef::alloca(bx, bx.cx.layout_of(tcx.mk_mut_ptr(arg.layout.ty)), diff --git a/src/librustc_codegen_llvm/mir/operand.rs b/src/librustc_codegen_llvm/mir/operand.rs index ab43531240f..c76cbfcd971 100644 --- a/src/librustc_codegen_llvm/mir/operand.rs +++ b/src/librustc_codegen_llvm/mir/operand.rs @@ -8,11 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::mir::interpret::{ConstValue, ConstEvalErr}; +use rustc::mir::interpret::{ConstValue, ErrorHandled}; use rustc::mir; use rustc::ty; use rustc::ty::layout::{self, Align, LayoutOf, TyLayout}; -use rustc_data_structures::sync::Lrc; use base; use common::{CodegenCx, C_undef, C_usize}; @@ -79,7 +78,7 @@ impl OperandRef<'ll, 'tcx> { pub fn from_const(bx: &Builder<'a, 'll, 'tcx>, val: &'tcx ty::Const<'tcx>) - -> Result<OperandRef<'ll, 'tcx>, Lrc<ConstEvalErr<'tcx>>> { + -> Result<OperandRef<'ll, 'tcx>, ErrorHandled> { let layout = bx.cx.layout_of(val.ty); if layout.is_zst() { @@ -283,8 +282,8 @@ impl OperandValue<'ll> { } match self { OperandValue::Ref(r, None, source_align) => { - base::memcpy_ty(bx, dest.llval, r, dest.layout, - source_align.min(dest.align), flags) + base::memcpy_ty(bx, dest.llval, dest.align, r, source_align, + dest.layout, flags) } OperandValue::Ref(_, Some(_), _) => { bug!("cannot directly store unsized values"); @@ -325,7 +324,7 @@ impl OperandValue<'ll> { // Allocate an appropriate region on the stack, and copy the value into it let (llsize, _) = glue::size_and_align_of_dst(&bx, unsized_ty, Some(llextra)); let lldst = bx.array_alloca(Type::i8(bx.cx), llsize, "unsized_tmp", max_align); - base::call_memcpy(&bx, lldst, llptr, llsize, min_align, flags); + base::call_memcpy(&bx, lldst, max_align, llptr, min_align, llsize, flags); // Store the allocated region and the extra to the indirect place. let indirect_operand = OperandValue::Pair(lldst, llextra); @@ -424,10 +423,13 @@ impl FunctionCx<'a, 'll, 'tcx> { self.eval_mir_constant(bx, constant) .and_then(|c| OperandRef::from_const(bx, c)) .unwrap_or_else(|err| { - err.report_as_error( - bx.tcx().at(constant.span), - "could not evaluate constant operand", - ); + match err { + // errored or at least linted + ErrorHandled::Reported => {}, + ErrorHandled::TooGeneric => { + bug!("codgen encountered polymorphic constant") + }, + } // Allow RalfJ to sleep soundly knowing that even refactorings that remove // the above error (or silence it under some conditions) will not cause UB let fnname = bx.cx.get_intrinsic(&("llvm.trap")); diff --git a/src/librustc_codegen_llvm/mir/place.rs b/src/librustc_codegen_llvm/mir/place.rs index e7b6f5908a4..75ec5ead243 100644 --- a/src/librustc_codegen_llvm/mir/place.rs +++ b/src/librustc_codegen_llvm/mir/place.rs @@ -10,7 +10,7 @@ use llvm::{self, LLVMConstInBoundsGEP}; use rustc::ty::{self, Ty}; -use rustc::ty::layout::{self, Align, TyLayout, LayoutOf, Size}; +use rustc::ty::layout::{self, Align, TyLayout, LayoutOf, Size, VariantIdx}; use rustc::mir; use rustc::mir::tcx::PlaceTy; use base; @@ -281,7 +281,7 @@ impl PlaceRef<'ll, 'tcx> { match self.layout.variants { layout::Variants::Single { index } => { let discr_val = self.layout.ty.ty_adt_def().map_or( - index as u128, + index.as_u32() as u128, |def| def.discriminant_for_variant(bx.cx.tcx, index).val); return C_uint_big(cast_to, discr_val); } @@ -320,16 +320,16 @@ impl PlaceRef<'ll, 'tcx> { C_uint_big(niche_llty, niche_start) }; bx.select(bx.icmp(llvm::IntEQ, lldiscr, niche_llval), - C_uint(cast_to, *niche_variants.start() as u64), - C_uint(cast_to, dataful_variant as u64)) + C_uint(cast_to, niche_variants.start().as_u32() as u64), + C_uint(cast_to, dataful_variant.as_u32() as u64)) } else { // Rebase from niche values to discriminant values. - let delta = niche_start.wrapping_sub(*niche_variants.start() as u128); + let delta = niche_start.wrapping_sub(niche_variants.start().as_u32() as u128); let lldiscr = bx.sub(lldiscr, C_uint_big(niche_llty, delta)); - let lldiscr_max = C_uint(niche_llty, *niche_variants.end() as u64); + let lldiscr_max = C_uint(niche_llty, niche_variants.end().as_u32() as u64); bx.select(bx.icmp(llvm::IntULE, lldiscr, lldiscr_max), bx.intcast(lldiscr, cast_to, false), - C_uint(cast_to, dataful_variant as u64)) + C_uint(cast_to, dataful_variant.as_u32() as u64)) } } } @@ -337,7 +337,7 @@ impl PlaceRef<'ll, 'tcx> { /// Set the discriminant for a new value of the given case of the given /// representation. - pub fn codegen_set_discr(&self, bx: &Builder<'a, 'll, 'tcx>, variant_index: usize) { + pub fn codegen_set_discr(&self, bx: &Builder<'a, 'll, 'tcx>, variant_index: VariantIdx) { if self.layout.for_variant(bx.cx, variant_index).abi.is_uninhabited() { return; } @@ -376,7 +376,8 @@ impl PlaceRef<'ll, 'tcx> { let niche = self.project_field(bx, 0); let niche_llty = niche.layout.immediate_llvm_type(bx.cx); - let niche_value = ((variant_index - *niche_variants.start()) as u128) + let niche_value = variant_index.as_u32() - niche_variants.start().as_u32(); + let niche_value = (niche_value as u128) .wrapping_add(niche_start); // FIXME(eddyb) Check the actual primitive type here. let niche_llval = if niche_value == 0 { @@ -401,7 +402,7 @@ impl PlaceRef<'ll, 'tcx> { } } - pub fn project_downcast(&self, bx: &Builder<'a, 'll, 'tcx>, variant_index: usize) + pub fn project_downcast(&self, bx: &Builder<'a, 'll, 'tcx>, variant_index: VariantIdx) -> PlaceRef<'ll, 'tcx> { let mut downcast = *self; downcast.layout = self.layout.for_variant(bx.cx, variant_index); @@ -517,7 +518,8 @@ impl FunctionCx<'a, 'll, 'tcx> { let mut subslice = cg_base.project_index(bx, C_usize(bx.cx, from as u64)); let projected_ty = PlaceTy::Ty { ty: cg_base.layout.ty } - .projection_ty(tcx, &projection.elem).to_ty(bx.tcx()); + .projection_ty(tcx, &projection.elem) + .to_ty(bx.tcx()); subslice.layout = bx.cx.layout_of(self.monomorphize(&projected_ty)); if subslice.layout.is_unsized() { diff --git a/src/librustc_codegen_llvm/mir/statement.rs b/src/librustc_codegen_llvm/mir/statement.rs index 93be0074f6e..8bda2c98594 100644 --- a/src/librustc_codegen_llvm/mir/statement.rs +++ b/src/librustc_codegen_llvm/mir/statement.rs @@ -84,21 +84,18 @@ impl FunctionCx<'a, 'll, 'tcx> { }).collect(); let input_vals = inputs.iter() - .try_fold(Vec::with_capacity(inputs.len()), |mut acc, input| { + .fold(Vec::with_capacity(inputs.len()), |mut acc, (span, input)| { let op = self.codegen_operand(&bx, input); if let OperandValue::Immediate(_) = op.val { acc.push(op.immediate()); - Ok(acc) } else { - Err(op) + span_err!(bx.sess(), span.to_owned(), E0669, + "invalid value for constraint in inline assembly"); } + acc }); - if input_vals.is_err() { - span_err!(bx.sess(), statement.source_info.span, E0669, - "invalid value for constraint in inline assembly"); - } else { - let input_vals = input_vals.unwrap(); + if input_vals.len() == inputs.len() { let res = asm::codegen_inline_asm(&bx, asm, outputs, input_vals); if !res { span_err!(bx.sess(), statement.source_info.span, E0668, @@ -108,8 +105,9 @@ impl FunctionCx<'a, 'll, 'tcx> { bx } mir::StatementKind::FakeRead(..) | - mir::StatementKind::EndRegion(_) | - mir::StatementKind::Validate(..) | + mir::StatementKind::EndRegion(..) | + mir::StatementKind::Retag { .. } | + mir::StatementKind::EscapeToRaw { .. } | mir::StatementKind::AscribeUserType(..) | mir::StatementKind::Nop => bx, } diff --git a/src/librustc_codegen_llvm/mono_item.rs b/src/librustc_codegen_llvm/mono_item.rs index dab9b147cc0..91c1ccbe002 100644 --- a/src/librustc_codegen_llvm/mono_item.rs +++ b/src/librustc_codegen_llvm/mono_item.rs @@ -153,9 +153,9 @@ fn predefine_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, assert!(!instance.substs.needs_infer() && !instance.substs.has_param_types()); - let mono_ty = instance.ty(cx.tcx); + let mono_sig = instance.fn_sig(cx.tcx); let attrs = cx.tcx.codegen_fn_attrs(instance.def_id()); - let lldecl = declare::declare_fn(cx, symbol_name, mono_ty); + let lldecl = declare::declare_fn(cx, symbol_name, mono_sig); unsafe { llvm::LLVMRustSetLinkage(lldecl, base::linkage_to_llvm(linkage)) }; base::set_link_section(lldecl, &attrs); if linkage == Linkage::LinkOnceODR || @@ -178,7 +178,7 @@ fn predefine_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, } } - debug!("predefine_fn: mono_ty = {:?} instance = {:?}", mono_ty, instance); + debug!("predefine_fn: mono_sig = {:?} instance = {:?}", mono_sig, instance); if instance.def.is_inline(cx.tcx) { attributes::inline(cx, lldecl, attributes::InlineAttr::Hint); } diff --git a/src/librustc_codegen_llvm/type_.rs b/src/librustc_codegen_llvm/type_.rs index 51a233d7916..6fb78fe4aa5 100644 --- a/src/librustc_codegen_llvm/type_.rs +++ b/src/librustc_codegen_llvm/type_.rs @@ -234,6 +234,8 @@ impl Type { } pub fn ptr_to(&self) -> &Type { + assert_ne!(self.kind(), TypeKind::Function, + "don't call ptr_to on function types, use ptr_to_llvm_type on FnType instead"); unsafe { llvm::LLVMPointerType(self, 0) } diff --git a/src/librustc_codegen_llvm/type_of.rs b/src/librustc_codegen_llvm/type_of.rs index 03ded64e642..fea02edf7be 100644 --- a/src/librustc_codegen_llvm/type_of.rs +++ b/src/librustc_codegen_llvm/type_of.rs @@ -265,7 +265,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> { ty::ParamEnv::reveal_all(), &sig, ); - FnType::new(cx, sig, &[]).llvm_type(cx).ptr_to() + FnType::new(cx, sig, &[]).ptr_to_llvm_type(cx) } _ => self.scalar_llvm_type_at(cx, scalar, Size::ZERO) }; @@ -285,7 +285,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> { debug!("llvm_type({:#?})", self); - assert!(!self.ty.has_escaping_regions(), "{:?} has escaping regions", self.ty); + assert!(!self.ty.has_escaping_bound_vars(), "{:?} has escaping bound vars", self.ty); // Make sure lifetimes are erased, to avoid generating distinct LLVM // types for Rust types that only differ in the choice of lifetimes. | 
