diff --git a/.gitignore b/.gitignore index b54bab177d0ae..309fbd95345a4 100644 --- a/.gitignore +++ b/.gitignore @@ -103,3 +103,6 @@ version.texi .cargo !src/vendor/** /src/target/ + +no_llvm_build + diff --git a/src/Cargo.lock b/src/Cargo.lock index 807375e00afdb..e9a20a7c79ca7 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -1778,7 +1778,12 @@ dependencies = [ name = "rustc_trans_utils" version = "0.0.0" dependencies = [ + "ar 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", + "rustc_back 0.0.0", "syntax 0.0.0", "syntax_pos 0.0.0", ] diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 8a6c998c932c2..ffd959d86f580 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -531,7 +531,10 @@ impl<'a> Builder<'a> { // For other crates, however, we know that we've already got a standard // library up and running, so we can use the normal compiler to compile // build scripts in that situation. - if mode == Mode::Libstd { + // + // If LLVM support is disabled we need to use the snapshot compiler to compile + // build scripts, as the new compiler doesnt support executables. + if mode == Mode::Libstd || !self.build.config.llvm_enabled { cargo.env("RUSTC_SNAPSHOT", &self.initial_rustc) .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir()); } else { diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index ce4ab2c8a1de9..56c17036cc5e3 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![cfg_attr(not(feature="llvm"), allow(dead_code))] - use rustc::dep_graph::DepGraph; use rustc::hir::{self, map as hir_map}; use rustc::hir::lowering::lower_crate; @@ -34,8 +32,8 @@ use rustc_incremental; use rustc_resolve::{MakeGlobMap, Resolver}; use rustc_metadata::creader::CrateLoader; use rustc_metadata::cstore::{self, CStore}; -use rustc_trans::back::write; use rustc_trans as trans; +use rustc_trans_utils::trans_crate::TransCrate; use rustc_typeck as typeck; use rustc_privacy; use rustc_plugin::registry::Registry; @@ -43,6 +41,7 @@ use rustc_plugin as plugin; use rustc_passes::{ast_validation, no_asm, loops, consts, static_recursion, hir_stats}; use rustc_const_eval::{self, check_match}; use super::Compilation; +use ::DefaultTransCrate; use serialize::json; @@ -76,7 +75,8 @@ pub fn compile_input(sess: &Session, output: &Option, addl_plugins: Option>, control: &CompileController) -> CompileResult { - use rustc_trans::back::write::OngoingCrateTranslation; + use rustc::session::config::CrateType; + macro_rules! controller_entry_point { ($point: ident, $tsess: expr, $make_state: expr, $phase_result: expr) => {{ let state = &mut $make_state; @@ -94,17 +94,16 @@ pub fn compile_input(sess: &Session, } if cfg!(not(feature="llvm")) { - use rustc::session::config::CrateType; - if !sess.opts.debugging_opts.no_trans && sess.opts.output_types.should_trans() { - sess.err("LLVM is not supported by this rustc. Please use -Z no-trans to compile") - } - - if sess.opts.crate_types.iter().all(|&t|{ - t != CrateType::CrateTypeRlib && t != CrateType::CrateTypeExecutable - }) && !sess.opts.crate_types.is_empty() { - sess.err( - "LLVM is not supported by this rustc, so non rlib libraries are not supported" - ); + for cty in sess.opts.crate_types.iter() { + match *cty { + CrateType::CrateTypeRlib | CrateType::CrateTypeDylib | + CrateType::CrateTypeExecutable => {}, + _ => { + sess.parse_sess.span_diagnostic.warn( + &format!("LLVM unsupported, so output type {} is not supported", cty) + ); + }, + } } sess.abort_if_errors(); @@ -117,7 +116,7 @@ pub fn compile_input(sess: &Session, // We need nested scopes here, because the intermediate results can keep // large chunks of memory alive and we want to free them as soon as // possible to keep the peak memory usage low - let (outputs, trans, dep_graph): (OutputFilenames, OngoingCrateTranslation, DepGraph) = { + let (outputs, trans, dep_graph) = { let krate = match phase_1_parse_input(control, sess, input) { Ok(krate) => krate, Err(mut parse_error) => { @@ -246,7 +245,7 @@ pub fn compile_input(sess: &Session, tcx.print_debug_stats(); } - let trans = phase_4_translate_to_llvm(tcx, rx); + let trans = phase_4_translate_to_llvm::(tcx, rx); if log_enabled!(::log::LogLevel::Info) { println!("Post-trans"); @@ -264,44 +263,42 @@ pub fn compile_input(sess: &Session, })?? }; - if cfg!(not(feature="llvm")) { - let (_, _) = (outputs, trans); - sess.fatal("LLVM is not supported by this rustc"); + if sess.opts.debugging_opts.print_type_sizes { + sess.code_stats.borrow().print_type_sizes(); } - #[cfg(feature="llvm")] - { - if sess.opts.debugging_opts.print_type_sizes { - sess.code_stats.borrow().print_type_sizes(); - } - - let (phase5_result, trans) = phase_5_run_llvm_passes(sess, &dep_graph, trans); + let (phase5_result, trans) = + phase_5_run_llvm_passes::(sess, &dep_graph, trans); - controller_entry_point!(after_llvm, - sess, - CompileState::state_after_llvm(input, sess, outdir, output, &trans), - phase5_result); - phase5_result?; + controller_entry_point!(after_llvm, + sess, + CompileState::state_after_llvm(input, sess, outdir, output, &trans), + phase5_result); + phase5_result?; - phase_6_link_output(sess, &trans, &outputs); - - // Now that we won't touch anything in the incremental compilation directory - // any more, we can finalize it (which involves renaming it) - rustc_incremental::finalize_session_directory(sess, trans.link.crate_hash); + // Run the linker on any artifacts that resulted from the LLVM run. + // This should produce either a finished executable or library. + time(sess.time_passes(), "linking", || { + DefaultTransCrate::link_binary(sess, &trans, &outputs) + }); - if sess.opts.debugging_opts.perf_stats { - sess.print_perf_stats(); - } + // Now that we won't touch anything in the incremental compilation directory + // any more, we can finalize it (which involves renaming it) + #[cfg(feature="llvm")] + rustc_incremental::finalize_session_directory(sess, trans.link.crate_hash); - controller_entry_point!( - compilation_done, - sess, - CompileState::state_when_compilation_done(input, sess, outdir, output), - Ok(()) - ); + if sess.opts.debugging_opts.perf_stats { + sess.print_perf_stats(); + } + controller_entry_point!( + compilation_done, + sess, + CompileState::state_when_compilation_done(input, sess, outdir, output), Ok(()) - } + ); + + Ok(()) } fn keep_hygiene_data(sess: &Session) -> bool { @@ -962,7 +959,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, mir::provide(&mut local_providers); reachable::provide(&mut local_providers); rustc_privacy::provide(&mut local_providers); - trans::provide_local(&mut local_providers); + DefaultTransCrate::provide_local(&mut local_providers); typeck::provide(&mut local_providers); ty::provide(&mut local_providers); traits::provide(&mut local_providers); @@ -974,7 +971,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, let mut extern_providers = ty::maps::Providers::default(); cstore::provide(&mut extern_providers); - trans::provide_extern(&mut extern_providers); + DefaultTransCrate::provide_extern(&mut extern_providers); ty::provide_extern(&mut extern_providers); traits::provide_extern(&mut extern_providers); // FIXME(eddyb) get rid of this once we replace const_eval with miri. @@ -1120,9 +1117,9 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, /// Run the translation phase to LLVM, after which the AST and analysis can /// be discarded. -pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub fn phase_4_translate_to_llvm<'a, 'tcx, Trans: TransCrate>(tcx: TyCtxt<'a, 'tcx, 'tcx>, rx: mpsc::Receiver>) - -> write::OngoingCrateTranslation { + -> ::OngoingCrateTranslation { let time_passes = tcx.sess.time_passes(); time(time_passes, @@ -1131,9 +1128,8 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let translation = time(time_passes, "translation", move || { - trans::trans_crate(tcx, rx) + Trans::trans_crate(tcx, rx) }); - if tcx.sess.profile_queries() { profile::dump("profile_queries".to_string()) } @@ -1143,15 +1139,14 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, /// Run LLVM itself, producing a bitcode file, assembly file or object file /// as a side effect. -#[cfg(feature="llvm")] -pub fn phase_5_run_llvm_passes(sess: &Session, +pub fn phase_5_run_llvm_passes(sess: &Session, dep_graph: &DepGraph, - trans: write::OngoingCrateTranslation) - -> (CompileResult, trans::CrateTranslation) { - let trans = trans.join(sess, dep_graph); + trans: ::OngoingCrateTranslation) + -> (CompileResult, ::TranslatedCrate) { + let trans = Trans::join_trans(trans, sess, dep_graph); if sess.opts.debugging_opts.incremental_info { - write::dump_incremental_data(&trans); + Trans::dump_incremental_data(&trans); } time(sess.time_passes(), @@ -1161,20 +1156,6 @@ pub fn phase_5_run_llvm_passes(sess: &Session, (sess.compile_status(), trans) } -/// Run the linker on any artifacts that resulted from the LLVM run. -/// This should produce either a finished executable or library. -#[cfg(feature="llvm")] -pub fn phase_6_link_output(sess: &Session, - trans: &trans::CrateTranslation, - outputs: &OutputFilenames) { - time(sess.time_passes(), "linking", || { - ::rustc_trans::back::link::link_binary(sess, - trans, - outputs, - &trans.crate_name.as_str()) - }); -} - fn escape_dep_filename(filename: &str) -> String { // Apparently clang and gcc *only* escape spaces: // http://llvm.org/klaus/clang/commit/9d50634cfc268ecc9a7250226dd5ca0e945240d4 diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 044f4a5eaf512..6bdad0b212cf5 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -75,6 +75,7 @@ use rustc::middle::cstore::CrateStore; use rustc_metadata::locator; use rustc_metadata::cstore::CStore; use rustc::util::common::{time, ErrorReported}; +use rustc_trans_utils::trans_crate::TransCrate; use serialize::json::ToJson; @@ -151,101 +152,31 @@ pub fn run(run_compiler: F) -> isize } #[cfg(not(feature="llvm"))] -pub use no_llvm_metadata_loader::NoLLvmMetadataLoader as MetadataLoader; +pub use rustc_trans_utils::trans_crate::MetadataOnlyTransCrate as DefaultTransCrate; #[cfg(feature="llvm")] -pub use rustc_trans::LlvmMetadataLoader as MetadataLoader; - -#[cfg(not(feature="llvm"))] -mod no_llvm_metadata_loader { - extern crate ar; - extern crate owning_ref; - - use rustc::middle::cstore::MetadataLoader as MetadataLoaderTrait; - use rustc_back::target::Target; - use std::io; - use std::fs::File; - use std::path::Path; - - use self::ar::Archive; - use self::owning_ref::{OwningRef, ErasedBoxRef}; - - pub struct NoLLvmMetadataLoader; - - impl MetadataLoaderTrait for NoLLvmMetadataLoader { - fn get_rlib_metadata( - &self, - _: &Target, - filename: &Path - ) -> Result, String> { - let file = File::open(filename).map_err(|e| { - format!("metadata file open err: {:?}", e) - })?; - let mut archive = Archive::new(file); - - while let Some(entry_result) = archive.next_entry() { - let mut entry = entry_result.map_err(|e| { - format!("metadata section read err: {:?}", e) - })?; - if entry.header().identifier() == "rust.metadata.bin" { - let mut buf = Vec::new(); - io::copy(&mut entry, &mut buf).unwrap(); - let buf: OwningRef, [u8]> = OwningRef::new(buf).into(); - return Ok(buf.map_owner_box().erase_owner()); - } - } - - Err("Couldnt find metadata section".to_string()) - } - - fn get_dylib_metadata(&self, - _target: &Target, - _filename: &Path) - -> Result, String> { - panic!("Dylib metadata loading not supported without LLVM") - } - } -} +pub use rustc_trans::LlvmTransCrate as DefaultTransCrate; #[cfg(not(feature="llvm"))] mod rustc_trans { use syntax_pos::symbol::Symbol; use rustc::session::Session; - use rustc::session::config::{PrintRequest, OutputFilenames}; - use rustc::ty::{TyCtxt, CrateAnalysis}; - use rustc::ty::maps::Providers; - use rustc_incremental::IncrementalHashesMap; - - use self::back::write::OngoingCrateTranslation; + use rustc::session::config::PrintRequest; + pub use rustc_trans_utils::trans_crate::MetadataOnlyTransCrate as LlvmTransCrate; + pub use rustc_trans_utils::trans_crate::TranslatedCrate as CrateTranslation; pub fn init(_sess: &Session) {} pub fn enable_llvm_debug() {} - pub fn provide(_providers: &mut Providers) {} pub fn print_version() {} pub fn print_passes() {} pub fn print(_req: PrintRequest, _sess: &Session) {} pub fn target_features(_sess: &Session) -> Vec { vec![] } - pub fn trans_crate<'a, 'tcx>( - _tcx: TyCtxt<'a, 'tcx, 'tcx>, - _analysis: CrateAnalysis, - _incr_hashes_map: IncrementalHashesMap, - _output_filenames: &OutputFilenames - ) -> OngoingCrateTranslation { - OngoingCrateTranslation(()) - } - - pub struct CrateTranslation(()); - pub mod back { pub mod write { - pub struct OngoingCrateTranslation(pub (in ::rustc_trans) ()); - pub const RELOC_MODEL_ARGS: [(&'static str, ()); 0] = []; pub const CODE_GEN_MODEL_ARGS: [(&'static str, ()); 0] = []; } } - - __build_diagnostic_array! { librustc_trans, DIAGNOSTICS } } // Parse args and run the compiler. This is the primary entry point for rustc. @@ -293,7 +224,7 @@ pub fn run_compiler<'a>(args: &[String], }, }; - let cstore = Rc::new(CStore::new(box ::MetadataLoader)); + let cstore = Rc::new(CStore::new(DefaultTransCrate::metadata_loader())); let loader = file_loader.unwrap_or(box RealFileLoader); let codemap = Rc::new(CodeMap::with_file_loader(loader, sopts.file_path_mapping())); @@ -1331,6 +1262,7 @@ pub fn diagnostics_registry() -> errors::registry::Registry { all_errors.extend_from_slice(&rustc_borrowck::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_resolve::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_privacy::DIAGNOSTICS); + #[cfg(feature="llvm")] all_errors.extend_from_slice(&rustc_trans::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_const_eval::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_metadata::DIAGNOSTICS); diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index fad24e6a0f2b6..6de36820f0c19 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -30,6 +30,7 @@ use rustc::hir::map as hir_map; use rustc::mir::transform::Passes; use rustc::session::{self, config}; use rustc::session::config::{OutputFilenames, OutputTypes}; +use rustc_trans_utils::trans_crate::TransCrate; use std::rc::Rc; use syntax::ast; use syntax::abi::Abi; @@ -105,7 +106,7 @@ fn test_env(source_string: &str, options.unstable_features = UnstableFeatures::Allow; let diagnostic_handler = errors::Handler::with_emitter(true, false, emitter); - let cstore = Rc::new(CStore::new(box ::MetadataLoader)); + let cstore = Rc::new(CStore::new(::DefaultTransCrate::metadata_loader())); let sess = session::build_session_(options, None, diagnostic_handler, diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 9d13d4ce15b79..1630e7759919c 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -extern crate rustc_trans_utils; - use super::archive::{ArchiveBuilder, ArchiveConfig}; use super::linker::Linker; use super::command::Command; @@ -20,14 +18,12 @@ use rustc::session::config::{self, NoDebugInfo, OutputFilenames, OutputType, Pri use rustc::session::filesearch; use rustc::session::search_paths::PathKind; use rustc::session::Session; -use rustc::ich::Fingerprint; -use rustc::middle::cstore::{LinkMeta, NativeLibrary, LibSource, NativeLibraryKind}; +use rustc::middle::cstore::{NativeLibrary, LibSource, NativeLibraryKind}; use rustc::middle::dependency_format::Linkage; use {CrateTranslation, CrateInfo}; use rustc::util::common::time; use rustc::util::fs::fix_windows_verbatim_for_gcc; use rustc::hir::def_id::CrateNum; -use rustc::hir::svh::Svh; use rustc_back::tempdir::TempDir; use rustc_back::{PanicStrategy, RelroLevel}; use context::get_reloc_model; @@ -88,16 +84,9 @@ pub const RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET: usize = pub const RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET: usize = RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET + 8; -pub use self::rustc_trans_utils::link::{find_crate_name, filename_for_input, - default_output_for_target, invalid_output_for_target}; - -pub fn build_link_meta(crate_hash: Fingerprint) -> LinkMeta { - let r = LinkMeta { - crate_hash: Svh::new(crate_hash.to_smaller_hash()), - }; - info!("{:?}", r); - return r; -} +pub use rustc_trans_utils::link::{find_crate_name, filename_for_input, default_output_for_target, + invalid_output_for_target, build_link_meta, 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 @@ -225,13 +214,6 @@ pub fn link_binary(sess: &Session, out_filenames } -fn is_writeable(p: &Path) -> bool { - match p.metadata() { - Err(..) => true, - Ok(m) => !m.permissions().readonly() - } -} - fn filename_for_metadata(sess: &Session, crate_name: &str, outputs: &OutputFilenames) -> PathBuf { let out_filename = outputs.single_output_file.clone() .unwrap_or(outputs @@ -295,32 +277,6 @@ pub fn ignored_for_lto(info: &CrateInfo, cnum: CrateNum) -> bool { info.is_no_builtins.contains(&cnum) || info.compiler_builtins == Some(cnum) } -fn out_filename(sess: &Session, - crate_type: config::CrateType, - outputs: &OutputFilenames, - crate_name: &str) - -> PathBuf { - let default_filename = filename_for_input(sess, crate_type, crate_name, outputs); - let out_filename = outputs.outputs.get(&OutputType::Exe) - .and_then(|s| s.to_owned()) - .or_else(|| outputs.single_output_file.clone()) - .unwrap_or(default_filename); - - check_file_is_writeable(&out_filename, sess); - - out_filename -} - -// Make sure files are writeable. Mac, FreeBSD, and Windows system linkers -// check this already -- however, the Linux linker will happily overwrite a -// read-only file. We should be consistent. -fn check_file_is_writeable(file: &Path, sess: &Session) { - if !is_writeable(file) { - sess.fatal(&format!("output file {} is not writeable -- check its \ - permissions", file.display())); - } -} - fn link_binary_output(sess: &Session, trans: &CrateTranslation, crate_type: config::CrateType, diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index bfa18d84d2705..b0d24670daeaa 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -43,7 +43,6 @@ use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::maps::Providers; use rustc::dep_graph::{DepNode, DepKind}; use rustc::middle::cstore::{self, LinkMeta, LinkagePreference}; -use rustc::hir::map as hir_map; use rustc::util::common::{time, print_time_passes_entry}; use rustc::session::config::{self, NoDebugInfo}; use rustc::session::Session; @@ -95,6 +94,8 @@ use syntax::ast; use mir::lvalue::Alignment; +pub use rustc_trans_utils::{find_exported_symbols, check_for_rustc_errors_attr}; + pub struct StatRecorder<'a, 'tcx: 'a> { ccx: &'a CrateContext<'a, 'tcx>, name: Option, @@ -660,20 +661,6 @@ pub fn set_link_section(ccx: &CrateContext, } } -// check for the #[rustc_error] annotation, which forces an -// error in trans. This is used to write compile-fail tests -// that actually test that compilation succeeds without -// reporting an error. -fn check_for_rustc_errors_attr(tcx: TyCtxt) { - if let Some((id, span)) = *tcx.sess.entry_fn.borrow() { - let main_def_id = tcx.hir.local_def_id(id); - - if tcx.has_attr(main_def_id, "rustc_error") { - tcx.sess.span_fatal(span, "compilation successful"); - } - } -} - /// Create the `main` function which will initialize the rust runtime and call /// users main function. fn maybe_create_entry_wrapper(ccx: &CrateContext) { @@ -885,59 +872,10 @@ fn iter_globals(llmod: llvm::ModuleRef) -> ValueIter { } } -/// The context provided lists a set of reachable ids as calculated by -/// middle::reachable, but this contains far more ids and symbols than we're -/// actually exposing from the object file. This function will filter the set in -/// the context to the set of ids which correspond to symbols that are exposed -/// from the object file being generated. -/// -/// This list is later used by linkers to determine the set of symbols needed to -/// be exposed from a dynamic library and it's also encoded into the metadata. -pub fn find_exported_symbols(tcx: TyCtxt) -> NodeSet { - tcx.reachable_set(LOCAL_CRATE).0.iter().cloned().filter(|&id| { - // Next, 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(id) { - hir_map::NodeForeignItem(..) => { - let def_id = tcx.hir.local_def_id(id); - tcx.is_statically_included_foreign_item(def_id) - } - - // Only consider nodes that actually have exported symbols. - hir_map::NodeItem(&hir::Item { - node: hir::ItemStatic(..), .. }) | - hir_map::NodeItem(&hir::Item { - node: hir::ItemFn(..), .. }) | - hir_map::NodeImplItem(&hir::ImplItem { - node: hir::ImplItemKind::Method(..), .. }) => { - let def_id = tcx.hir.local_def_id(id); - let generics = tcx.generics_of(def_id); - let attributes = tcx.get_attrs(def_id); - (generics.parent_types == 0 && generics.types.is_empty()) && - // Functions marked with #[inline] are only ever translated - // with "internal" linkage and are never exported. - !attr::requests_inline(&attributes) - } - - _ => false - } - }).collect() -} - pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, rx: mpsc::Receiver>) -> OngoingCrateTranslation { + check_for_rustc_errors_attr(tcx); diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 57e9f1d091b21..8a2c478cea061 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -50,6 +50,7 @@ extern crate rustc_incremental; extern crate rustc_llvm as llvm; extern crate rustc_platform_intrinsics as intrinsics; extern crate rustc_const_math; +extern crate rustc_trans_utils; extern crate rustc_demangle; extern crate jobserver; extern crate num_cpus; @@ -137,6 +138,63 @@ mod type_; mod type_of; mod value; +use std::sync::mpsc; +use std::any::Any; +use rustc::ty::{self, TyCtxt}; +use rustc::session::Session; +use rustc::session::config::OutputFilenames; +use rustc::middle::cstore::MetadataLoader; +use rustc::dep_graph::DepGraph; + +pub struct LlvmTransCrate(()); + +impl LlvmTransCrate { + pub fn new() -> Self { + LlvmTransCrate(()) + } +} + +impl rustc_trans_utils::trans_crate::TransCrate for LlvmTransCrate { + type MetadataLoader = metadata::LlvmMetadataLoader; + type OngoingCrateTranslation = back::write::OngoingCrateTranslation; + type TranslatedCrate = CrateTranslation; + + fn metadata_loader() -> Box { + box metadata::LlvmMetadataLoader + } + + fn provide_local(providers: &mut ty::maps::Providers) { + provide_local(providers); + } + + fn provide_extern(providers: &mut ty::maps::Providers) { + provide_extern(providers); + } + + fn trans_crate<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + rx: mpsc::Receiver> + ) -> Self::OngoingCrateTranslation { + base::trans_crate(tcx, rx) + } + + fn join_trans( + trans: Self::OngoingCrateTranslation, + sess: &Session, + dep_graph: &DepGraph + ) -> Self::TranslatedCrate { + trans.join(sess, dep_graph) + } + + fn link_binary(sess: &Session, trans: &Self::TranslatedCrate, outputs: &OutputFilenames) { + back::link::link_binary(sess, trans, outputs, &trans.crate_name.as_str()); + } + + fn dump_incremental_data(trans: &Self::TranslatedCrate) { + back::write::dump_incremental_data(trans); + } +} + pub struct ModuleTranslation { /// The name of the module. When the crate may be saved between /// compilations, incremental compilation requires that name be diff --git a/src/librustc_trans_utils/Cargo.toml b/src/librustc_trans_utils/Cargo.toml index f026d4fcbc280..bedbea0068874 100644 --- a/src/librustc_trans_utils/Cargo.toml +++ b/src/librustc_trans_utils/Cargo.toml @@ -10,6 +10,12 @@ crate-type = ["dylib"] test = false [dependencies] -rustc = { path = "../librustc" } +ar = "0.3.0" +flate2 = "0.2" +owning_ref = "0.3.3" +log = "0.3" + syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } +rustc = { path = "../librustc" } +rustc_back = { path = "../librustc_back" } diff --git a/src/librustc_trans_utils/lib.rs b/src/librustc_trans_utils/lib.rs index 5e8abe59ad9a4..6873befd2bfca 100644 --- a/src/librustc_trans_utils/lib.rs +++ b/src/librustc_trans_utils/lib.rs @@ -29,8 +29,89 @@ #![cfg_attr(stage0, feature(const_fn))] +extern crate ar; +extern crate flate2; +extern crate owning_ref; +#[macro_use] +extern crate log; + +#[macro_use] extern crate rustc; +extern crate rustc_back; extern crate syntax; extern crate syntax_pos; +use rustc::ty::TyCtxt; +use rustc::hir; +use rustc::hir::def_id::LOCAL_CRATE; +use rustc::hir::map as hir_map; +use rustc::util::nodemap::NodeSet; + +use syntax::attr; + pub mod link; +pub mod trans_crate; + +/// check for the #[rustc_error] annotation, which forces an +/// error in trans. This is used to write compile-fail tests +/// that actually test that compilation succeeds without +/// reporting an error. +pub fn check_for_rustc_errors_attr(tcx: TyCtxt) { + if let Some((id, span)) = *tcx.sess.entry_fn.borrow() { + let main_def_id = tcx.hir.local_def_id(id); + + if tcx.has_attr(main_def_id, "rustc_error") { + tcx.sess.span_fatal(span, "compilation successful"); + } + } +} + +/// The context provided lists a set of reachable ids as calculated by +/// middle::reachable, but this contains far more ids and symbols than we're +/// actually exposing from the object file. This function will filter the set in +/// the context to the set of ids which correspond to symbols that are exposed +/// from the object file being generated. +/// +/// This list is later used by linkers to determine the set of symbols needed to +/// be exposed from a dynamic library and it's also encoded into the metadata. +pub fn find_exported_symbols(tcx: TyCtxt) -> NodeSet { + tcx.reachable_set(LOCAL_CRATE).0.iter().cloned().filter(|&id| { + // Next, 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(id) { + hir_map::NodeForeignItem(..) => { + let def_id = tcx.hir.local_def_id(id); + tcx.is_statically_included_foreign_item(def_id) + } + + // Only consider nodes that actually have exported symbols. + hir_map::NodeItem(&hir::Item { + node: hir::ItemStatic(..), .. }) | + hir_map::NodeItem(&hir::Item { + node: hir::ItemFn(..), .. }) | + hir_map::NodeImplItem(&hir::ImplItem { + node: hir::ImplItemKind::Method(..), .. }) => { + let def_id = tcx.hir.local_def_id(id); + let generics = tcx.generics_of(def_id); + let attributes = tcx.get_attrs(def_id); + (generics.parent_types == 0 && generics.types.is_empty()) && + // Functions marked with #[inline] are only ever translated + // with "internal" linkage and are never exported. + !attr::requests_inline(&attributes) + } + + _ => false + } + }).collect() +} diff --git a/src/librustc_trans_utils/link.rs b/src/librustc_trans_utils/link.rs index aa8637fabe85f..47484488fb8e8 100644 --- a/src/librustc_trans_utils/link.rs +++ b/src/librustc_trans_utils/link.rs @@ -8,13 +8,56 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use rustc::ich::Fingerprint; use rustc::session::config::{self, OutputFilenames, Input, OutputType}; use rustc::session::Session; -use rustc::middle::cstore; -use std::path::PathBuf; +use rustc::middle::cstore::{self, LinkMeta}; +use rustc::hir::svh::Svh; +use std::path::{Path, PathBuf}; use syntax::ast; use syntax_pos::Span; +pub fn out_filename(sess: &Session, + crate_type: config::CrateType, + outputs: &OutputFilenames, + crate_name: &str) + -> PathBuf { + let default_filename = filename_for_input(sess, crate_type, crate_name, outputs); + let out_filename = outputs.outputs.get(&OutputType::Exe) + .and_then(|s| s.to_owned()) + .or_else(|| outputs.single_output_file.clone()) + .unwrap_or(default_filename); + + check_file_is_writeable(&out_filename, sess); + + out_filename +} + +// Make sure files are writeable. Mac, FreeBSD, and Windows system linkers +// check this already -- however, the Linux linker will happily overwrite a +// read-only file. We should be consistent. +pub fn check_file_is_writeable(file: &Path, sess: &Session) { + if !is_writeable(file) { + sess.fatal(&format!("output file {} is not writeable -- check its \ + permissions", file.display())); + } +} + +fn is_writeable(p: &Path) -> bool { + match p.metadata() { + Err(..) => true, + Ok(m) => !m.permissions().readonly() + } +} + +pub fn build_link_meta(crate_hash: Fingerprint) -> LinkMeta { + let r = LinkMeta { + crate_hash: Svh::new(crate_hash.to_smaller_hash()), + }; + info!("{:?}", r); + return r; +} + pub fn find_crate_name(sess: Option<&Session>, attrs: &[ast::Attribute], input: &Input) -> String { diff --git a/src/librustc_trans_utils/trans_crate.rs b/src/librustc_trans_utils/trans_crate.rs new file mode 100644 index 0000000000000..f51a463fcc23e --- /dev/null +++ b/src/librustc_trans_utils/trans_crate.rs @@ -0,0 +1,249 @@ +// Copyright 2014-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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The Rust compiler. +//! +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/")] +#![deny(warnings)] + +#![feature(box_syntax)] + +use std::any::Any; +use std::io::prelude::*; +use std::io::{self, Cursor}; +use std::fs::File; +use std::path::Path; +use std::sync::mpsc; + +use owning_ref::{ErasedBoxRef, OwningRef}; +use ar::{Archive, Builder, Header}; +use flate2::Compression; +use flate2::write::DeflateEncoder; + +use syntax::symbol::Symbol; +use rustc::hir::def_id::LOCAL_CRATE; +use rustc::session::Session; +use rustc::session::config::{CrateType, OutputFilenames}; +use rustc::ty::TyCtxt; +use rustc::ty::maps::Providers; +use rustc::middle::cstore::EncodedMetadata; +use rustc::middle::cstore::MetadataLoader as MetadataLoaderTrait; +use rustc::dep_graph::{DepGraph, DepNode, DepKind}; +use rustc_back::target::Target; +use link::{build_link_meta, out_filename}; + +pub trait TransCrate { + type MetadataLoader: MetadataLoaderTrait; + type OngoingCrateTranslation; + type TranslatedCrate; + + fn metadata_loader() -> Box; + fn provide_local(_providers: &mut Providers); + fn provide_extern(_providers: &mut Providers); + fn trans_crate<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + rx: mpsc::Receiver> + ) -> Self::OngoingCrateTranslation; + fn join_trans( + trans: Self::OngoingCrateTranslation, + sess: &Session, + dep_graph: &DepGraph + ) -> Self::TranslatedCrate; + fn link_binary(sess: &Session, trans: &Self::TranslatedCrate, outputs: &OutputFilenames); + fn dump_incremental_data(trans: &Self::TranslatedCrate); +} + +pub struct DummyTransCrate; + +impl TransCrate for DummyTransCrate { + type MetadataLoader = DummyMetadataLoader; + type OngoingCrateTranslation = (); + type TranslatedCrate = (); + + fn metadata_loader() -> Box { + box DummyMetadataLoader(()) + } + + fn provide_local(_providers: &mut Providers) { + bug!("DummyTransCrate::provide_local"); + } + + fn provide_extern(_providers: &mut Providers) { + bug!("DummyTransCrate::provide_extern"); + } + + fn trans_crate<'a, 'tcx>( + _tcx: TyCtxt<'a, 'tcx, 'tcx>, + _rx: mpsc::Receiver> + ) -> Self::OngoingCrateTranslation { + bug!("DummyTransCrate::trans_crate"); + } + + fn join_trans( + _trans: Self::OngoingCrateTranslation, + _sess: &Session, + _dep_graph: &DepGraph + ) -> Self::TranslatedCrate { + bug!("DummyTransCrate::join_trans"); + } + + fn link_binary(_sess: &Session, _trans: &Self::TranslatedCrate, _outputs: &OutputFilenames) { + bug!("DummyTransCrate::link_binary"); + } + + fn dump_incremental_data(_trans: &Self::TranslatedCrate) { + bug!("DummyTransCrate::dump_incremental_data"); + } +} + +pub struct DummyMetadataLoader(()); + +impl MetadataLoaderTrait for DummyMetadataLoader { + fn get_rlib_metadata( + &self, + _target: &Target, + _filename: &Path + ) -> Result, String> { + bug!("DummyMetadataLoader::get_rlib_metadata"); + } + + fn get_dylib_metadata( + &self, + _target: &Target, + _filename: &Path + ) -> Result, String> { + bug!("DummyMetadataLoader::get_dylib_metadata"); + } +} + +pub struct NoLlvmMetadataLoader; + +impl MetadataLoaderTrait for NoLlvmMetadataLoader { + fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result, String> { + let file = File::open(filename) + .map_err(|e| format!("metadata file open err: {:?}", e))?; + let mut archive = Archive::new(file); + + while let Some(entry_result) = archive.next_entry() { + let mut entry = entry_result + .map_err(|e| format!("metadata section read err: {:?}", e))?; + if entry.header().identifier() == "rust.metadata.bin" { + let mut buf = Vec::new(); + io::copy(&mut entry, &mut buf).unwrap(); + let buf: OwningRef, [u8]> = OwningRef::new(buf).into(); + return Ok(buf.map_owner_box().erase_owner()); + } + } + + Err("Couldnt find metadata section".to_string()) + } + + fn get_dylib_metadata( + &self, + _target: &Target, + _filename: &Path, + ) -> Result, String> { + // FIXME: Support reading dylibs from llvm enabled rustc + self.get_rlib_metadata(_target, _filename) + } +} + +pub struct MetadataOnlyTransCrate; +pub struct OngoingCrateTranslation { + metadata: EncodedMetadata, + metadata_version: Vec, + crate_name: Symbol, +} +pub struct TranslatedCrate(OngoingCrateTranslation); + +impl MetadataOnlyTransCrate { + #[allow(dead_code)] + pub fn new() -> Self { + MetadataOnlyTransCrate + } +} + +impl TransCrate for MetadataOnlyTransCrate { + type MetadataLoader = NoLlvmMetadataLoader; + type OngoingCrateTranslation = OngoingCrateTranslation; + type TranslatedCrate = TranslatedCrate; + + fn metadata_loader() -> Box { + box NoLlvmMetadataLoader + } + + fn provide_local(_providers: &mut Providers) {} + fn provide_extern(_providers: &mut Providers) {} + + fn trans_crate<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + _rx: mpsc::Receiver> + ) -> Self::OngoingCrateTranslation { + ::check_for_rustc_errors_attr(tcx); + let _ = tcx.link_args(LOCAL_CRATE); + let _ = tcx.native_libraries(LOCAL_CRATE); + tcx.sess.abort_if_errors(); + + let crate_hash = tcx.dep_graph + .fingerprint_of(&DepNode::new_no_params(DepKind::Krate)); + let link_meta = build_link_meta(crate_hash); + let exported_symbols = ::find_exported_symbols(tcx); + let (metadata, _hashes) = tcx.encode_metadata(&link_meta, &exported_symbols); + + OngoingCrateTranslation { + metadata: metadata, + metadata_version: tcx.metadata_encoding_version().to_vec(), + crate_name: tcx.crate_name(LOCAL_CRATE), + } + } + + fn join_trans( + trans: Self::OngoingCrateTranslation, + _sess: &Session, + _dep_graph: &DepGraph, + ) -> Self::TranslatedCrate { + TranslatedCrate(trans) + } + + fn link_binary(sess: &Session, trans: &Self::TranslatedCrate, outputs: &OutputFilenames) { + for &crate_type in sess.opts.crate_types.iter() { + if crate_type != CrateType::CrateTypeRlib && crate_type != CrateType::CrateTypeDylib { + continue; + } + let output_name = + out_filename(sess, crate_type, &outputs, &trans.0.crate_name.as_str()); + let mut compressed = trans.0.metadata_version.clone(); + let metadata = if crate_type == CrateType::CrateTypeDylib { + DeflateEncoder::new(&mut compressed, Compression::Fast) + .write_all(&trans.0.metadata.raw_data) + .unwrap(); + &compressed + } else { + &trans.0.metadata.raw_data + }; + let mut builder = Builder::new(File::create(&output_name).unwrap()); + let header = Header::new("rust.metadata.bin".to_string(), metadata.len() as u64); + builder.append(&header, Cursor::new(metadata)).unwrap(); + } + + if !sess.opts.crate_types.contains(&CrateType::CrateTypeRlib) + && !sess.opts.crate_types.contains(&CrateType::CrateTypeDylib) { + sess.fatal("Executables are not supported by the metadata-only backend."); + } + } + + fn dump_incremental_data(_trans: &Self::TranslatedCrate) {} +}