diff --git a/.gitmodules b/.gitmodules index 39288a7ae4907..503d0bff276a7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -17,3 +17,7 @@ [submodule "src/liblibc"] path = src/liblibc url = https://github.com/rust-lang/libc.git +[submodule "src/lld"] + path = src/lld + url = git://github.com/japaric/lld.git + branch = rust-lld-2016-07-09 diff --git a/configure b/configure index a8bd3acdff1ac..1d4b75edb4327 100755 --- a/configure +++ b/configure @@ -620,6 +620,7 @@ opt rustbuild 0 "use the rust and cargo based build system" opt codegen-tests 1 "run the src/test/codegen tests" opt option-checking 1 "complain about unrecognized options in this configure script" opt ninja 0 "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)" +opt lld 1 "Build and embed lld, the LLVM linker, into rustc" # Optimization and debugging options. These may be overridden by the release channel, etc. opt_nosave optimize 1 "build optimized rust code" diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index 2b9d717cbd48d..08b88955363b9 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -308,6 +308,11 @@ pub fn krate(build: &Build, continue } + // FIXME(maybe) rustc_lld doesn't work right now but it doesn't have any tests anyway + if crate_name.contains("rustc_lld") { + continue + } + cargo.arg("-p").arg(crate_name); } diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 0f69bcfbb649d..17675852a3073 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -37,19 +37,20 @@ use toml::{Parser, Decoder, Value}; /// `src/bootstrap/config.toml.example`. #[derive(Default)] pub struct Config { - pub ccache: bool, - pub ninja: bool, pub verbose: bool, pub submodules: bool, pub compiler_docs: bool, pub docs: bool, pub target_config: HashMap, - // llvm codegen options + // llvm options (see struct Llvm) + pub ccache: bool, + pub ninja: bool, pub llvm_assertions: bool, pub llvm_optimize: bool, pub llvm_version_check: bool, pub llvm_static_stdcpp: bool, + pub lld: bool, // rust codegen options pub rust_optimize: bool, @@ -128,6 +129,7 @@ struct Llvm { optimize: Option, version_check: Option, static_libstdcpp: Option, + lld: Option, } /// TOML representation of how the Rust build is configured. @@ -163,6 +165,7 @@ struct TomlTarget { impl Config { pub fn parse(build: &str, file: Option) -> Config { let mut config = Config::default(); + config.lld = true; config.llvm_optimize = true; config.use_jemalloc = true; config.backtrace = true; @@ -229,6 +232,7 @@ impl Config { set(&mut config.llvm_optimize, llvm.optimize); set(&mut config.llvm_version_check, llvm.version_check); set(&mut config.llvm_static_stdcpp, llvm.static_libstdcpp); + set(&mut config.lld, llvm.lld); } if let Some(ref rust) = toml.rust { set(&mut config.rust_debug_assertions, rust.debug_assertions); @@ -330,6 +334,7 @@ impl Config { ("LOCAL_REBUILD", self.local_rebuild), ("NINJA", self.ninja), ("CODEGEN_TESTS", self.codegen_tests), + ("LLD", self.lld), } match key { diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index ebca0c8ecea76..377bfb1ab77ee 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -550,6 +550,11 @@ impl Build { continue } + if submodule.path.components().any(|c| c == Component::Normal("lld".as_ref())) && + !self.config.lld { + continue + } + match submodule.state { State::MaybeDirty => { // drop staged changes @@ -730,6 +735,9 @@ impl Build { if self.config.use_jemalloc { features.push_str(" jemalloc"); } + if self.config.lld { + features.push_str(" lld"); + } return features } diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index df6408e5fe1c8..5f7d6153102de 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -75,6 +75,11 @@ pub fn llvm(build: &Build, target: &str) { .define("LLVM_ENABLE_LIBEDIT", "OFF") .define("LLVM_PARALLEL_COMPILE_JOBS", build.jobs().to_string()); + if build.config.lld { + cfg.define("LLVM_EXTERNAL_LLD_SOURCE_DIR", build.src.join("src/lld")); + cfg.define("LLVM_EXTERNAL_PROJECTS", "lld"); + } + if target.starts_with("i686") { cfg.define("LLVM_BUILD_32_BITS", "ON"); } diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 2009e18f6ee20..f38c6d3a8e62c 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -915,6 +915,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "the directory the MIR is dumped into"), perf_stats: bool = (false, parse_bool, [UNTRACKED], "print some performance-related statistics"), + use_lld: bool = (false, parse_bool, [UNTRACKED], "use lld as linker"), } pub fn default_lib_output() -> CrateType { diff --git a/src/librustc_back/target/linux_base.rs b/src/librustc_back/target/linux_base.rs index d1ab71e41404e..b05d9577f33c7 100644 --- a/src/librustc_back/target/linux_base.rs +++ b/src/librustc_back/target/linux_base.rs @@ -30,6 +30,11 @@ pub fn opts() -> TargetOptions { // Always enable NX protection when it is available "-Wl,-z,noexecstack".to_string(), ], + pre_lld_args: vec![ + "--as-needed".to_string(), + "-z".to_string(), + "noexecstack".to_string(), + ], position_independent_executables: true, exe_allocation_crate: super::maybe_jemalloc(), has_elf_tls: true, diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 756586602b45a..835cec6079692 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -245,6 +245,12 @@ pub struct TargetOptions { /// user-defined libraries. pub post_link_args: Vec, + /// lld flags are slightly different from the default linker's (e.g. cc) and we can't always map + /// one to the other so we just maintain a different set of args just for lld + pub pre_lld_args: Vec, + pub late_lld_args: Vec, + pub post_lld_args: Vec, + /// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Defaults /// to "generic". pub cpu: String, @@ -355,6 +361,9 @@ impl Default for TargetOptions { ar: option_env!("CFG_DEFAULT_AR").unwrap_or("ar").to_string(), pre_link_args: Vec::new(), post_link_args: Vec::new(), + pre_lld_args: Vec::new(), + late_lld_args: Vec::new(), + post_lld_args: Vec::new(), cpu: "generic".to_string(), features: "".to_string(), dynamic_linking: false, @@ -492,11 +501,14 @@ impl Target { key!(linker); key!(ar); key!(pre_link_args, list); + key!(pre_lld_args, list); key!(pre_link_objects_exe, list); key!(pre_link_objects_dll, list); key!(late_link_args, list); + key!(late_lld_args, list); key!(post_link_objects, list); key!(post_link_args, list); + key!(post_lld_args, list); key!(cpu); key!(features); key!(dynamic_linking, bool); @@ -634,11 +646,14 @@ impl ToJson for Target { target_option_val!(linker); target_option_val!(ar); target_option_val!(pre_link_args); + target_option_val!(pre_lld_args); target_option_val!(pre_link_objects_exe); target_option_val!(pre_link_objects_dll); target_option_val!(late_link_args); + target_option_val!(late_lld_args); target_option_val!(post_link_objects); target_option_val!(post_link_args); + target_option_val!(post_lld_args); target_option_val!(cpu); target_option_val!(features); target_option_val!(dynamic_linking); diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index 772d83eb2cfad..057c521c2a632 100644 --- a/src/librustc_driver/Cargo.toml +++ b/src/librustc_driver/Cargo.toml @@ -35,3 +35,6 @@ syntax = { path = "../libsyntax" } syntax_ext = { path = "../libsyntax_ext" } syntax_pos = { path = "../libsyntax_pos" } proc_macro = { path = "../libproc_macro" } + +[features] +lld = ["rustc_llvm/lld", "rustc_trans/lld"] diff --git a/src/librustc_lld/Cargo.toml b/src/librustc_lld/Cargo.toml new file mode 100644 index 0000000000000..88e23a0dcfe22 --- /dev/null +++ b/src/librustc_lld/Cargo.toml @@ -0,0 +1,17 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_lld" +version = "0.0.0" +build = "build.rs" + +[lib] +name = "rustc_lld" +path = "lib.rs" +crate-type = ["dylib"] + +[build-dependencies] +build_helper = { path = "../build_helper" } +gcc = "0.3.27" + +[dependencies] +rustc_llvm = { path = "../librustc_llvm" } diff --git a/src/librustc_lld/build.rs b/src/librustc_lld/build.rs new file mode 100644 index 0000000000000..54b01a03f7756 --- /dev/null +++ b/src/librustc_lld/build.rs @@ -0,0 +1,115 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +extern crate build_helper; +extern crate gcc; + +use std::env; +use std::path::PathBuf; +use std::process::Command; + +use build_helper::output; + +// NOTE This pretty much looks the same as librustc_llvm build.rs. Refer to that file for an +// explanation of what this function does. + +fn main() { + let target = env::var("TARGET").unwrap(); + let host = env::var("HOST").unwrap(); + let is_crossed = target != host; + + let llvm_config = env::var_os("LLVM_CONFIG") + .map(PathBuf::from) + .unwrap_or_else(|| { + if let Some(dir) = env::var_os("CARGO_TARGET_DIR") + .map(PathBuf::from) { + let to_test = dir.parent() + .unwrap() + .parent() + .unwrap() + .join(&target) + .join("llvm/bin/llvm-config"); + if Command::new(&to_test).output().is_ok() { + return to_test; + } + } + PathBuf::from("llvm-config") + }); + + println!("cargo:rerun-if-changed={}", llvm_config.display()); + + let mut cmd = Command::new(&llvm_config); + cmd.arg("--cxxflags"); + let cxxflags = output(&mut cmd); + + let mut cfg = gcc::Config::new(); + for flag in cxxflags.split_whitespace() { + // Ignore flags like `-m64` when we're doing a cross build + if is_crossed && flag.starts_with("-m") { + continue; + } + cfg.flag(flag); + } + + cfg.file("../rustlld/RustWrapper.cpp") + .cpp(true) + .cpp_link_stdlib(None) + .compile("librustlld.a"); + + let mut cmd = Command::new(&llvm_config); + cmd.arg("--ldflags"); + for lib in output(&mut cmd).split_whitespace() { + if lib.starts_with("-LIBPATH:") { + println!("cargo:rustc-link-search=native={}", &lib[9..]); + } else if is_crossed { + if lib.starts_with("-L") { + println!("cargo:rustc-link-search=native={}", + lib[2..].replace(&host, &target)); + } + } else if lib.starts_with("-l") { + println!("cargo:rustc-link-lib={}", &lib[2..]); + } else if lib.starts_with("-L") { + println!("cargo:rustc-link-search=native={}", &lib[2..]); + } + } + + if !target.contains("msvc") { + if let Some(s) = env::var_os("LLVM_STATIC_STDCPP") { + assert!(!cxxflags.contains("stdlib=libc++")); + let path = PathBuf::from(s); + println!("cargo:rustc-link-search=native={}", + path.parent().unwrap().display()); + println!("cargo:rustc-link-lib=static=stdc++"); + } else if cxxflags.contains("stdlib=libc++") { + println!("cargo:rustc-link-lib=c++"); + } else { + println!("cargo:rustc-link-lib=stdc++"); + } + } + + // NOTE After this point comes LLD specific stuff + // + // We need to link to these libraries to be able to use lld functions via ffi + // These libraries depend on llvm libraries but we don't link this crate to those libraries + // because that can result in duplicate linking to static libraries, instead rustc_llvm will + // link to those libraries for us. + // + // To elaborate on this last point: which libraries we must link to come from the output of + // llvm-config. llvm-config maps "components" to -l flags. rustc_llvm is already using this + // method to figure out which -l flags we need to use llvm via ffi. If we do the same thing here + // we risk ending up static llvm libraries being linked up more than once in the final rustc + // binary -- and this last part can cause runtime failures of rustc like + // + // : CommandLine Error: Option 'color' registered more than once! + // LLVM ERROR: inconsistency in registered CommandLine options + // + println!("cargo:rustc-link-lib=static=lldConfig"); + println!("cargo:rustc-link-lib=static=lldELF"); +} diff --git a/src/librustc_lld/ffi.rs b/src/librustc_lld/ffi.rs new file mode 100644 index 0000000000000..7fa18000b74be --- /dev/null +++ b/src/librustc_lld/ffi.rs @@ -0,0 +1,16 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::os::raw::{c_char, c_uint}; + +#[linked_from = "rustlld"] // not quite true but good enough +extern { + pub fn LldRustElfLink(Args: *const *const c_char, NumArgs: c_uint)-> bool; +} diff --git a/src/librustc_lld/lib.rs b/src/librustc_lld/lib.rs new file mode 100644 index 0000000000000..bf1ad583b4b63 --- /dev/null +++ b/src/librustc_lld/lib.rs @@ -0,0 +1,17 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(linked_from)] +#![feature(rustc_private)] + +// NOTE Used to "inherit" the static llvm libraries that rustc_llvm links to +extern crate rustc_llvm; + +pub mod ffi; diff --git a/src/librustc_llvm/Cargo.toml b/src/librustc_llvm/Cargo.toml index f97daa22ff662..5aaa32bb5d180 100644 --- a/src/librustc_llvm/Cargo.toml +++ b/src/librustc_llvm/Cargo.toml @@ -10,6 +10,7 @@ path = "lib.rs" crate-type = ["dylib"] [features] +lld = [] static-libstdcpp = [] [dependencies] diff --git a/src/librustc_llvm/build.rs b/src/librustc_llvm/build.rs index ac83d860b6e90..5fa5d3a55c090 100644 --- a/src/librustc_llvm/build.rs +++ b/src/librustc_llvm/build.rs @@ -70,20 +70,24 @@ fn main() { // FIXME: surely we don't need all these components, right? Stuff like mcjit // or interpreter the compiler itself never uses. - let required_components = &["ipo", - "bitreader", - "bitwriter", - "linker", - "asmparser", - "mcjit", - "interpreter", - "instrumentation"]; + let mut required_components = vec!["ipo", + "bitreader", + "bitwriter", + "linker", + "asmparser", + "mcjit", + "interpreter", + "instrumentation"]; + + if env::var_os("CARGO_FEATURE_LLD").is_some() { + required_components.extend(&["lto", "option", "passes"]); + } let components = output(Command::new(&llvm_config).arg("--components")); let mut components = components.split_whitespace().collect::>(); components.retain(|c| optional_components.contains(c) || required_components.contains(c)); - for component in required_components { + for component in &required_components { if !components.contains(component) { panic!("require llvm component {} but wasn't found", component); } diff --git a/src/librustc_trans/Cargo.toml b/src/librustc_trans/Cargo.toml index 38f9e7ab0c51c..e4c5b3b51cb92 100644 --- a/src/librustc_trans/Cargo.toml +++ b/src/librustc_trans/Cargo.toml @@ -21,8 +21,12 @@ rustc_const_math = { path = "../librustc_const_math" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } rustc_incremental = { path = "../librustc_incremental" } +rustc_lld = { path = "../librustc_lld", optional = true } rustc_llvm = { path = "../librustc_llvm" } rustc_platform_intrinsics = { path = "../librustc_platform_intrinsics" } serialize = { path = "../libserialize" } syntax = { path = "../libsyntax" } -syntax_pos = { path = "../libsyntax_pos" } \ No newline at end of file +syntax_pos = { path = "../libsyntax_pos" } + +[features] +lld = ["rustc_lld"] diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 288249a7d9934..05f07bed1e757 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -10,6 +10,8 @@ use super::archive::{ArchiveBuilder, ArchiveConfig}; use super::linker::Linker; +#[cfg(feature = "lld")] +use super::linker::LldLinker; use super::rpath::RPathConfig; use super::rpath; use super::msvc; @@ -29,6 +31,8 @@ use rustc::dep_graph::DepNode; use rustc::hir::svh::Svh; use rustc_back::tempdir::TempDir; use rustc_incremental::IncrementalHashesMap; +#[cfg(feature = "lld")] +use lld::ffi; use std::ascii; use std::char; @@ -350,10 +354,21 @@ fn link_binary_output(sess: &Session, config::CrateTypeStaticlib => { link_staticlib(sess, &objects, &out_filename, tmpdir.path()); } + #[cfg(not(feature = "lld"))] _ => { link_natively(sess, crate_type, &objects, &out_filename, trans, outputs, tmpdir.path()); } + #[cfg(feature = "lld")] + _ => { + if sess.opts.debugging_opts.use_lld { + link_natively_using_lld(sess, crate_type, &objects, &out_filename, + outputs, tmpdir.path()); + } else { + link_natively(sess, crate_type, &objects, &out_filename, trans, + outputs, tmpdir.path()); + } + } } out_filename @@ -603,6 +618,51 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path, } } +#[cfg(feature = "lld")] +fn link_natively_using_lld(sess: &Session, + crate_type: config::CrateType, + objects: &[PathBuf], + out_filename: &Path, + outputs: &OutputFilenames, + tmpdir: &Path) { + let mut lld = LldLinker::new(); + + let root = sess.target_filesearch(PathKind::Native).get_lib_path(); + lld.cmd_like_args(&sess.target.target.options.pre_lld_args); + + let pre_link_objects = if crate_type == config::CrateTypeExecutable { + &sess.target.target.options.pre_link_objects_exe + } else { + &sess.target.target.options.pre_link_objects_dll + }; + for obj in pre_link_objects { + lld.cmd_like_arg(root.join(obj)); + } + + link_args(&mut lld, sess, crate_type, tmpdir, + objects, out_filename, outputs); + lld.cmd_like_args(&sess.target.target.options.late_link_args); + for obj in &sess.target.target.options.post_link_objects { + lld.cmd_like_arg(root.join(obj)); + } + lld.cmd_like_args(&sess.target.target.options.post_lld_args); + + if sess.opts.debugging_opts.print_link_args { + println!("{:?}", lld.args()); + } + + // May have not found libraries in the right formats. + sess.abort_if_errors(); + + // Invoke the linker + time(sess.time_passes(), "running linker", || { + let args = lld.args().iter().map(|s| s.as_ptr()).collect::>(); + unsafe { + assert!(ffi::LldRustElfLink(args.as_ptr(), args.len() as u32)); + } + }); +} + // Create a dynamic library or executable // // This will invoke the system linker/cc to create the resulting file. This @@ -660,8 +720,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(|b| char::from_u32(b as u32).unwrap())); x }) } @@ -669,8 +729,8 @@ fn link_natively(sess: &Session, let mut output = prog.stderr.clone(); output.extend_from_slice(&prog.stdout); sess.struct_err(&format!("linking with `{}` failed: {}", - pname, - prog.status)) + pname, + prog.status)) .note(&format!("{:?}", &cmd)) .note(&escape_string(&output[..])) .emit(); diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index 58cad5c117f93..e2cb456238068 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -10,6 +10,8 @@ use std::collections::HashMap; use std::ffi::OsString; +#[cfg(feature = "lld")] +use std::ffi::{CString, OsStr}; use std::fs::{self, File}; use std::io::prelude::*; use std::io::{self, BufWriter}; @@ -94,6 +96,161 @@ pub trait Linker { fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType); } +#[cfg(feature = "lld")] +pub struct LldLinker { + args: Vec, +} + +#[cfg(feature = "lld")] +fn cstring(s: S) -> CString + where S: Into> +{ + CString::new(s).unwrap() +} + +#[cfg(feature = "lld")] +impl LldLinker { + pub fn new() -> Self { + // NOTE The first argument will be skipped, but we must set it to something. Using lld here + // matches the behavior of the `lld` tool. + LldLinker { args: vec![cstring("lld")] } + } + + pub fn cmd_like_arg(&mut self, arg: S) + where S: AsRef + { + self.args.push(cstring(arg.as_ref().to_string_lossy().into_owned())) + } + + pub fn cmd_like_args(&mut self, args: &[S]) + where S: AsRef + { + for arg in args { + self.cmd_like_arg(arg) + } + } + + pub fn args(&self) -> &[CString] { + &self.args + } +} + +#[cfg(feature = "lld")] +impl Linker for LldLinker { + fn link_dylib(&mut self, lib: &str) { + self.args.push(cstring("-l")); + self.args.push(cstring(lib)); + } + + fn link_staticlib(&mut self, lib: &str) { + self.args.push(cstring("-l")); + self.args.push(cstring(lib)); + } + + fn link_rlib(&mut self, lib: &Path) { + self.cmd_like_arg(lib); + } + + fn include_path(&mut self, path: &Path) { + self.args.push(cstring("-L")); + self.cmd_like_arg(path); + } + + fn framework_path(&mut self, _: &Path) { + // TODO + } + + fn output_filename(&mut self, path: &Path) { + self.args.push(cstring("-o")); + self.cmd_like_arg(path); + } + + fn add_object(&mut self, path: &Path) { + self.cmd_like_arg(path); + } + + fn position_independent_executable(&mut self) { + // TODO --pie support disabled. Raise a warning + } + + fn args(&mut self, args: &[String]) { + self.args.extend(args.iter().cloned().map(cstring)) + } + + fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { + self.args.push(cstring("-l")); + self.args.push(cstring(lib)); + } + + fn link_framework(&mut self, _: &str) { + // TODO + } + + fn link_whole_staticlib(&mut self, lib: &str, _: &[PathBuf]) { + // TODO OSX + self.whole_archives(); + self.args.push(cstring("-l")); + self.args.push(cstring(lib)); + self.no_whole_archives(); + } + + fn link_whole_rlib(&mut self, lib: &Path) { + // TODO OSX + self.whole_archives(); + self.cmd_like_arg(lib); + self.no_whole_archives(); + } + + fn gc_sections(&mut self, _: bool) { + self.args.push(cstring("--gc-sections")); + } + + fn optimize(&mut self) { + // TODO + } + + fn debuginfo(&mut self) { + // TODO fact check this + // Don't do anything special here for GNU-style linkers. + } + + fn no_default_libraries(&mut self) { + // NOTE lld doesn't pass any library by default + } + + fn build_dylib(&mut self, _: &Path) { + // TODO + } + + fn whole_archives(&mut self) { + // TODO take_hints? + // if !self.takes_hints() { return } + self.args.push(cstring("--whole-archive")); + } + + fn no_whole_archives(&mut self) { + // TODO take_hints? + // if !self.takes_hints() { return } + self.args.push(cstring("--no-whole-archive")); + } + + fn hint_static(&mut self) { + // TODO take_hints? + // if !self.takes_hints() { return } + self.args.push(cstring("--Bstatic")); + } + + fn hint_dynamic(&mut self) { + // TODO take_hints? + // if !self.takes_hints() { return } + self.args.push(cstring("--Bdynamic")); + } + + fn export_symbols(&mut self, _: &Path, _: CrateType) { + // TODO + } +} + pub struct GnuLinker<'a> { cmd: &'a mut Command, sess: &'a Session, diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 3e60369acbff3..1fa8a6eca2489 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -50,6 +50,8 @@ extern crate rustc_back; extern crate rustc_data_structures; extern crate rustc_incremental; pub extern crate rustc_llvm as llvm; +#[cfg(feature = "lld")] +pub extern crate rustc_lld as lld; extern crate rustc_platform_intrinsics as intrinsics; extern crate serialize; extern crate rustc_const_math; diff --git a/src/lld b/src/lld new file mode 160000 index 0000000000000..9fc6ba504f93f --- /dev/null +++ b/src/lld @@ -0,0 +1 @@ +Subproject commit 9fc6ba504f93f99c8d67e3d50971c91d9f33f74d diff --git a/src/rustc/Cargo.lock b/src/rustc/Cargo.lock index 3377fc43d8a60..556d2e371b786 100644 --- a/src/rustc/Cargo.lock +++ b/src/rustc/Cargo.lock @@ -205,6 +205,15 @@ dependencies = [ "syntax_pos 0.0.0", ] +[[package]] +name = "rustc_lld" +version = "0.0.0" +dependencies = [ + "build_helper 0.1.0", + "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_llvm 0.0.0", +] + [[package]] name = "rustc_llvm" version = "0.0.0" @@ -336,6 +345,7 @@ dependencies = [ "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", "rustc_incremental 0.0.0", + "rustc_lld 0.0.0", "rustc_llvm 0.0.0", "rustc_platform_intrinsics 0.0.0", "serialize 0.0.0", diff --git a/src/rustc/Cargo.toml b/src/rustc/Cargo.toml index 24499cb8f08c2..88857a3520262 100644 --- a/src/rustc/Cargo.toml +++ b/src/rustc/Cargo.toml @@ -31,3 +31,4 @@ rustdoc = { path = "../librustdoc" } [features] jemalloc = ["rustc_back/jemalloc"] +lld = ["rustc_driver/lld"] diff --git a/src/rustlld/RustWrapper.cpp b/src/rustlld/RustWrapper.cpp new file mode 100644 index 0000000000000..4b49e1ed845c2 --- /dev/null +++ b/src/rustlld/RustWrapper.cpp @@ -0,0 +1,26 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//===----------------------------------------------------------------------=== +// +// This file defines alternate interfaces to core functions that are more +// readily callable by Rust's FFI. +// +//===----------------------------------------------------------------------=== + +#include "llvm/ADT/ArrayRef.h" + +#include "lld/Driver/Driver.h" + +using namespace llvm; + +extern "C" bool LldRustElfLink(const char* Args[], unsigned NumArgs) { + return lld::elf::link(makeArrayRef(Args, NumArgs)); +} diff --git a/src/test/run-make/lld/Makefile b/src/test/run-make/lld/Makefile new file mode 100644 index 0000000000000..414a27ecea706 --- /dev/null +++ b/src/test/run-make/lld/Makefile @@ -0,0 +1,10 @@ +# Test that we can link, using the embedded lld, a "bare-metal" executable for a foreign +# architecture (ARM Cortex M, in this case) without relying on a external linker (like +# arm-none-eabi-gcc) or external C stuff (libraries or startup objects). + +# TODO this test should be skipped when lld support has not been built into rustc + +-include ../tools.mk + +all: + $(RUSTC) --target cortex-m3 -Z print-link-args -Z use-lld bare.rs diff --git a/src/test/run-make/lld/bare.rs b/src/test/run-make/lld/bare.rs new file mode 100644 index 0000000000000..777684b43a81e --- /dev/null +++ b/src/test/run-make/lld/bare.rs @@ -0,0 +1,34 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(asm)] +#![feature(lang_items)] +#![feature(no_core)] +#![no_core] +#![no_main] + +#[no_mangle] +pub fn _start() -> ! { + unsafe { + asm!("bkpt"); + } + + loop {} +} + +#[allow(private_no_mangle_fns)] +#[no_mangle] +fn __aeabi_unwind_cpp_pr0() {} + +#[lang = "copy"] +trait Copy {} + +#[lang = "sized"] +trait Sized {} diff --git a/src/test/run-make/lld/cortex-m3.json b/src/test/run-make/lld/cortex-m3.json new file mode 100644 index 0000000000000..355c5fe53dfce --- /dev/null +++ b/src/test/run-make/lld/cortex-m3.json @@ -0,0 +1,11 @@ +{ + "arch": "arm", + "cpu": "cortex-m3", + "data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64", + "executables": true, + "llvm-target": "thumbv7m-none-eabi", + "no-compiler-rt": true, + "os": "none", + "target-endian": "little", + "target-pointer-width": "32" +} diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 2839bbded1a5f..a4a6927dd20d1 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -57,10 +57,12 @@ fn main() { fn filter_dirs(path: &Path) -> bool { let skip = [ "src/jemalloc", + "src/lld", "src/llvm", "src/libbacktrace", "src/compiler-rt", "src/rt/hoedown", + "src/rustlld", "src/rustllvm", "src/rust-installer", "src/liblibc",