diff --git a/src/ci/docker/wasm32-unknown/Dockerfile b/src/ci/docker/wasm32-unknown/Dockerfile index 0972eb85191a9..6c0ec1ad9d4e1 100644 --- a/src/ci/docker/wasm32-unknown/Dockerfile +++ b/src/ci/docker/wasm32-unknown/Dockerfile @@ -26,6 +26,7 @@ ENV RUST_CONFIGURE_ARGS \ --set rust.lld ENV SCRIPT python2.7 /checkout/x.py test --target $TARGETS \ + src/test/run-make \ src/test/ui \ src/test/run-pass \ src/test/compile-fail \ diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 09d7ce599ab03..8bcec79d99f17 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -593,6 +593,7 @@ define_dep_nodes!( <'tcx> [] ImplementationsOfTrait { krate: CrateNum, trait_id: DefId }, [] AllTraitImplementations(CrateNum), + [] DllimportForeignItems(CrateNum), [] IsDllimportForeignItem(DefId), [] IsStaticallyIncludedForeignItem(DefId), [] NativeLibraryKind(DefId), @@ -655,6 +656,8 @@ define_dep_nodes!( <'tcx> [input] Features, [] ProgramClausesFor(DefId), + [] WasmImportModuleMap(CrateNum), + [] ForeignModules(CrateNum), ); trait DepNodeParams<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> : fmt::Debug { diff --git a/src/librustc/hir/check_attr.rs b/src/librustc/hir/check_attr.rs index f141cac161483..9b2647ad4db2b 100644 --- a/src/librustc/hir/check_attr.rs +++ b/src/librustc/hir/check_attr.rs @@ -26,6 +26,7 @@ enum Target { Union, Enum, Const, + ForeignMod, Other, } @@ -37,6 +38,7 @@ impl Target { hir::ItemUnion(..) => Target::Union, hir::ItemEnum(..) => Target::Enum, hir::ItemConst(..) => Target::Const, + hir::ItemForeignMod(..) => Target::ForeignMod, _ => Target::Other, } } @@ -57,25 +59,42 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> { .emit(); } + let mut has_wasm_import_module = false; for attr in &item.attrs { - if let Some(name) = attr.name() { - if name == "inline" { - self.check_inline(attr, item, target) + if attr.check_name("inline") { + self.check_inline(attr, item, target) + } else if attr.check_name("wasm_import_module") { + has_wasm_import_module = true; + if attr.value_str().is_none() { + self.tcx.sess.span_err(attr.span, "\ + must be of the form #[wasm_import_module = \"...\"]"); + } + if target != Target::ForeignMod { + self.tcx.sess.span_err(attr.span, "\ + must only be attached to foreign modules"); + } + } else if attr.check_name("wasm_custom_section") { + if target != Target::Const { + self.tcx.sess.span_err(attr.span, "only allowed on consts"); } - if name == "wasm_custom_section" { - if target != Target::Const { - self.tcx.sess.span_err(attr.span, "only allowed on consts"); - } - - if attr.value_str().is_none() { - self.tcx.sess.span_err(attr.span, "must be of the form \ - #[wasm_custom_section = \"foo\"]"); - } + if attr.value_str().is_none() { + self.tcx.sess.span_err(attr.span, "must be of the form \ + #[wasm_custom_section = \"foo\"]"); } } } + if target == Target::ForeignMod && + !has_wasm_import_module && + self.tcx.sess.target.target.arch == "wasm32" && + false // FIXME: eventually enable this warning when stable + { + self.tcx.sess.span_warn(item.span, "\ + must have a #[wasm_import_module = \"...\"] attribute, this \ + will become a hard error before too long"); + } + self.check_repr(item, target); } diff --git a/src/librustc/ich/impls_cstore.rs b/src/librustc/ich/impls_cstore.rs index 18a02ff5c5882..0071850e1052b 100644 --- a/src/librustc/ich/impls_cstore.rs +++ b/src/librustc/ich/impls_cstore.rs @@ -33,7 +33,12 @@ impl_stable_hash_for!(struct middle::cstore::NativeLibrary { kind, name, cfg, - foreign_items + foreign_module +}); + +impl_stable_hash_for!(struct middle::cstore::ForeignModule { + foreign_items, + def_id }); impl_stable_hash_for!(enum middle::cstore::LinkagePreference { diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 3c451d7ae46a1..add9b621596b6 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -132,7 +132,13 @@ pub struct NativeLibrary { pub kind: NativeLibraryKind, pub name: Symbol, pub cfg: Option, + pub foreign_module: Option, +} + +#[derive(Clone, Hash, RustcEncodable, RustcDecodable)] +pub struct ForeignModule { pub foreign_items: Vec, + pub def_id: DefId, } pub enum LoadedMacro { diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index 0b41c3ab2fa20..7565e90df986f 100644 --- a/src/librustc/ty/maps/config.rs +++ b/src/librustc/ty/maps/config.rs @@ -430,6 +430,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::native_libraries<'tcx> { } } +impl<'tcx> QueryDescription<'tcx> for queries::foreign_modules<'tcx> { + fn describe(_tcx: TyCtxt, _: CrateNum) -> String { + format!("looking up the foreign modules of a linked crate") + } +} + impl<'tcx> QueryDescription<'tcx> for queries::plugin_registrar_fn<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("looking up the plugin registrar for a crate") @@ -705,6 +711,18 @@ impl<'tcx> QueryDescription<'tcx> for queries::program_clauses_for<'tcx> { } } +impl<'tcx> QueryDescription<'tcx> for queries::wasm_import_module_map<'tcx> { + fn describe(_tcx: TyCtxt, _: CrateNum) -> String { + format!("wasm import module map") + } +} + +impl<'tcx> QueryDescription<'tcx> for queries::dllimport_foreign_items<'tcx> { + fn describe(_tcx: TyCtxt, _: CrateNum) -> String { + format!("wasm import module map") + } +} + macro_rules! impl_disk_cacheable_query( ($query_name:ident, |$key:tt| $cond:expr) => { impl<'tcx> QueryDescription<'tcx> for queries::$query_name<'tcx> { diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 2b4c1992762ea..c16ad0d8ca140 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -18,7 +18,7 @@ use infer::canonical::{Canonical, QueryResult}; use lint; use middle::borrowck::BorrowCheckResult; use middle::cstore::{ExternCrate, LinkagePreference, NativeLibrary, - ExternBodyNestedBodies}; + ExternBodyNestedBodies, ForeignModule}; use middle::cstore::{NativeLibraryKind, DepKind, CrateSource, ExternConstBody}; use middle::privacy::AccessLevels; use middle::reachable::ReachableSet; @@ -320,6 +320,9 @@ define_maps! { <'tcx> [] fn native_libraries: NativeLibraries(CrateNum) -> Lrc>, + + [] fn foreign_modules: ForeignModules(CrateNum) -> Lrc>, + [] fn plugin_registrar_fn: PluginRegistrarFn(CrateNum) -> Option, [] fn derive_registrar_fn: DeriveRegistrarFn(CrateNum) -> Option, [] fn crate_disambiguator: CrateDisambiguator(CrateNum) -> CrateDisambiguator, @@ -331,6 +334,8 @@ define_maps! { <'tcx> [] fn all_trait_implementations: AllTraitImplementations(CrateNum) -> Lrc>, + [] fn dllimport_foreign_items: DllimportForeignItems(CrateNum) + -> Lrc>, [] fn is_dllimport_foreign_item: IsDllimportForeignItem(DefId) -> bool, [] fn is_statically_included_foreign_item: IsStaticallyIncludedForeignItem(DefId) -> bool, [] fn native_library_kind: NativeLibraryKind(DefId) @@ -426,6 +431,8 @@ define_maps! { <'tcx> [] fn program_clauses_for: ProgramClausesFor(DefId) -> Lrc>>, [] fn wasm_custom_sections: WasmCustomSections(CrateNum) -> Lrc>, + [] fn wasm_import_module_map: WasmImportModuleMap(CrateNum) + -> Lrc>, } ////////////////////////////////////////////////////////////////////// diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs index 910c00b832ef8..46106d8ec0e91 100644 --- a/src/librustc/ty/maps/plumbing.rs +++ b/src/librustc/ty/maps/plumbing.rs @@ -886,6 +886,9 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, force!(all_trait_implementations, krate!()); } + DepKind::DllimportForeignItems => { + force!(dllimport_foreign_items, krate!()); + } DepKind::IsDllimportForeignItem => { force!(is_dllimport_foreign_item, def_id!()); } @@ -941,6 +944,8 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::ProgramClausesFor => { force!(program_clauses_for, def_id!()); } DepKind::WasmCustomSections => { force!(wasm_custom_sections, krate!()); } + DepKind::WasmImportModuleMap => { force!(wasm_import_module_map, krate!()); } + DepKind::ForeignModules => { force!(foreign_modules, krate!()); } } true diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index a0546b369a8eb..baaf57c890898 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -12,7 +12,6 @@ use cstore::{self, CStore, CrateSource, MetadataBlob}; use locator::{self, CratePaths}; -use native_libs::relevant_lib; use schema::CrateRoot; use rustc_data_structures::sync::{Lrc, RwLock, Lock}; @@ -230,7 +229,7 @@ impl<'a> CrateLoader<'a> { .map(|trait_impls| (trait_impls.trait_id, trait_impls.impls)) .collect(); - let mut cmeta = cstore::CrateMetadata { + let cmeta = cstore::CrateMetadata { name, extern_crate: Lock::new(None), def_path_table: Lrc::new(def_path_table), @@ -250,25 +249,8 @@ impl<'a> CrateLoader<'a> { rlib, rmeta, }, - // Initialize this with an empty set. The field is populated below - // after we were able to deserialize its contents. - dllimport_foreign_items: FxHashSet(), }; - let dllimports: FxHashSet<_> = cmeta - .root - .native_libraries - .decode((&cmeta, self.sess)) - .filter(|lib| relevant_lib(self.sess, lib) && - lib.kind == cstore::NativeLibraryKind::NativeUnknown) - .flat_map(|lib| { - assert!(lib.foreign_items.iter().all(|def_id| def_id.krate == cnum)); - lib.foreign_items.into_iter().map(|def_id| def_id.index) - }) - .collect(); - - cmeta.dllimport_foreign_items = dllimports; - let cmeta = Lrc::new(cmeta); self.cstore.set_crate_data(cnum, cmeta.clone()); (cnum, cmeta) diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index bd5ad93946e3c..53986f0741005 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -20,7 +20,7 @@ use rustc::middle::cstore::{DepKind, ExternCrate, MetadataLoader}; use rustc::session::{Session, CrateDisambiguator}; use rustc_back::PanicStrategy; use rustc_data_structures::indexed_vec::IndexVec; -use rustc::util::nodemap::{FxHashMap, FxHashSet, NodeMap}; +use rustc::util::nodemap::{FxHashMap, NodeMap}; use rustc_data_structures::sync::{Lrc, RwLock, Lock}; use syntax::{ast, attr}; @@ -30,7 +30,7 @@ use syntax_pos; pub use rustc::middle::cstore::{NativeLibrary, NativeLibraryKind, LinkagePreference}; pub use rustc::middle::cstore::NativeLibraryKind::*; -pub use rustc::middle::cstore::{CrateSource, LibSource}; +pub use rustc::middle::cstore::{CrateSource, LibSource, ForeignModule}; pub use cstore_impl::{provide, provide_extern}; @@ -84,8 +84,6 @@ pub struct CrateMetadata { pub source: CrateSource, pub proc_macros: Option)>>, - // Foreign items imported from a dylib (Windows only) - pub dllimport_foreign_items: FxHashSet, } pub struct CStore { diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index 3c2f984ef8b3f..e911a03bbe2b5 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -12,6 +12,7 @@ use cstore; use encoder; use link_args; use native_libs; +use foreign_modules; use schema; use rustc::ty::maps::QueryConfig; @@ -197,6 +198,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, Lrc::new(reachable_non_generics) } native_libraries => { Lrc::new(cdata.get_native_libraries(tcx.sess)) } + foreign_modules => { Lrc::new(cdata.get_foreign_modules(tcx.sess)) } plugin_registrar_fn => { cdata.root.plugin_registrar_fn.map(|index| { DefId { krate: def_id.krate, index } @@ -224,9 +226,6 @@ provide! { <'tcx> tcx, def_id, other, cdata, Lrc::new(result) } - is_dllimport_foreign_item => { - cdata.is_dllimport_foreign_item(def_id.index) - } visibility => { cdata.get_visibility(def_id.index) } dep_kind => { let r = *cdata.dep_kind.lock(); @@ -306,13 +305,28 @@ pub fn provide<'tcx>(providers: &mut Providers<'tcx>) { tcx.native_libraries(id.krate) .iter() .filter(|lib| native_libs::relevant_lib(&tcx.sess, lib)) - .find(|l| l.foreign_items.contains(&id)) + .find(|lib| { + let fm_id = match lib.foreign_module { + Some(id) => id, + None => return false, + }; + tcx.foreign_modules(id.krate) + .iter() + .find(|m| m.def_id == fm_id) + .expect("failed to find foreign module") + .foreign_items + .contains(&id) + }) .map(|l| l.kind) }, native_libraries: |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); Lrc::new(native_libs::collect(tcx)) }, + foreign_modules: |tcx, cnum| { + assert_eq!(cnum, LOCAL_CRATE); + Lrc::new(foreign_modules::collect(tcx)) + }, link_args: |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); Lrc::new(link_args::collect(tcx)) diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 0e5df3142af64..e72f9ddd82abe 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -10,7 +10,7 @@ // Decoding metadata from a single crate's metadata -use cstore::{self, CrateMetadata, MetadataBlob, NativeLibrary}; +use cstore::{self, CrateMetadata, MetadataBlob, NativeLibrary, ForeignModule}; use schema::*; use rustc_data_structures::sync::{Lrc, ReadGuard}; @@ -1031,6 +1031,10 @@ impl<'a, 'tcx> CrateMetadata { self.root.native_libraries.decode((self, sess)).collect() } + pub fn get_foreign_modules(&self, sess: &Session) -> Vec { + self.root.foreign_modules.decode((self, sess)).collect() + } + pub fn get_dylib_dependency_formats(&self) -> Vec<(CrateNum, LinkagePreference)> { self.root .dylib_dependency_formats @@ -1103,10 +1107,6 @@ impl<'a, 'tcx> CrateMetadata { } } - pub fn is_dllimport_foreign_item(&self, id: DefIndex) -> bool { - self.dllimport_foreign_items.contains(&id) - } - pub fn fn_sig(&self, id: DefIndex, tcx: TyCtxt<'a, 'tcx, 'tcx>) diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 56981b8f4a1e9..39de1ec852ec4 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -14,7 +14,7 @@ use isolated_encoder::IsolatedEncoder; use schema::*; use rustc::middle::cstore::{LinkMeta, LinkagePreference, NativeLibrary, - EncodedMetadata}; + EncodedMetadata, ForeignModule}; use rustc::hir::def::CtorKind; use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefIndex, DefId, LocalDefId, LOCAL_CRATE}; use rustc::hir::map::definitions::DefPathTable; @@ -412,6 +412,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { ()); let native_lib_bytes = self.position() - i; + let foreign_modules = self.tracked( + IsolatedEncoder::encode_foreign_modules, + ()); + // Encode codemap i = self.position(); let codemap = self.encode_codemap(); @@ -480,6 +484,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { lang_items, lang_items_missing, native_libraries, + foreign_modules, codemap, def_path_table, impls, @@ -1337,6 +1342,11 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { self.lazy_seq(used_libraries.iter().cloned()) } + fn encode_foreign_modules(&mut self, _: ()) -> LazySeq { + let foreign_modules = self.tcx.foreign_modules(LOCAL_CRATE); + self.lazy_seq(foreign_modules.iter().cloned()) + } + fn encode_crate_deps(&mut self, _: ()) -> LazySeq { let crates = self.tcx.crates(); diff --git a/src/librustc_metadata/foreign_modules.rs b/src/librustc_metadata/foreign_modules.rs new file mode 100644 index 0000000000000..c44d891b7f39a --- /dev/null +++ b/src/librustc_metadata/foreign_modules.rs @@ -0,0 +1,48 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::hir::itemlikevisit::ItemLikeVisitor; +use rustc::hir; +use rustc::middle::cstore::ForeignModule; +use rustc::ty::TyCtxt; + +pub fn collect<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Vec { + let mut collector = Collector { + tcx, + modules: Vec::new(), + }; + tcx.hir.krate().visit_all_item_likes(&mut collector); + return collector.modules +} + +struct Collector<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + modules: Vec, +} + +impl<'a, 'tcx> ItemLikeVisitor<'tcx> for Collector<'a, 'tcx> { + fn visit_item(&mut self, it: &'tcx hir::Item) { + let fm = match it.node { + hir::ItemForeignMod(ref fm) => fm, + _ => return, + }; + + let foreign_items = fm.items.iter() + .map(|it| self.tcx.hir.local_def_id(it.id)) + .collect(); + self.modules.push(ForeignModule { + foreign_items, + def_id: self.tcx.hir.local_def_id(it.id), + }); + } + + fn visit_trait_item(&mut self, _it: &'tcx hir::TraitItem) {} + fn visit_impl_item(&mut self, _it: &'tcx hir::ImplItem) {} +} diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs index f77c22bd89544..8509966744948 100644 --- a/src/librustc_metadata/lib.rs +++ b/src/librustc_metadata/lib.rs @@ -56,6 +56,7 @@ mod isolated_encoder; mod schema; mod native_libs; mod link_args; +mod foreign_modules; pub mod creader; pub mod cstore; diff --git a/src/librustc_metadata/native_libs.rs b/src/librustc_metadata/native_libs.rs index 2504f8dc251f9..4bb6d8fb87cf5 100644 --- a/src/librustc_metadata/native_libs.rs +++ b/src/librustc_metadata/native_libs.rs @@ -105,14 +105,11 @@ impl<'a, 'tcx> ItemLikeVisitor<'tcx> for Collector<'a, 'tcx> { } else { None }; - let foreign_items = fm.items.iter() - .map(|it| self.tcx.hir.local_def_id(it.id)) - .collect(); let lib = NativeLibrary { name: n, kind, cfg, - foreign_items, + foreign_module: Some(self.tcx.hir.local_def_id(it.id)), }; self.register_native_lib(Some(m.span), lib); } @@ -218,7 +215,7 @@ impl<'a, 'tcx> Collector<'a, 'tcx> { name: Symbol::intern(new_name.unwrap_or(name)), kind: if let Some(k) = kind { k } else { cstore::NativeUnknown }, cfg: None, - foreign_items: Vec::new(), + foreign_module: None, }; self.register_native_lib(None, lib); } diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index 001772623e75b..9832794529701 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -15,8 +15,8 @@ use rustc::hir; use rustc::hir::def::{self, CtorKind}; use rustc::hir::def_id::{DefIndex, DefId, CrateNum}; use rustc::ich::StableHashingContext; -use rustc::middle::cstore::{DepKind, LinkagePreference, NativeLibrary}; use rustc::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; +use rustc::middle::cstore::{DepKind, LinkagePreference, NativeLibrary, ForeignModule}; use rustc::middle::lang_items; use rustc::mir; use rustc::session::CrateDisambiguator; @@ -200,6 +200,7 @@ pub struct CrateRoot { pub lang_items: LazySeq<(DefIndex, usize)>, pub lang_items_missing: LazySeq, pub native_libraries: LazySeq, + pub foreign_modules: LazySeq, pub codemap: LazySeq, pub def_path_table: Lazy, pub impls: LazySeq, diff --git a/src/librustc_trans/attributes.rs b/src/librustc_trans/attributes.rs index 16253aa92acc7..df78ccdd2298a 100644 --- a/src/librustc_trans/attributes.rs +++ b/src/librustc_trans/attributes.rs @@ -18,6 +18,7 @@ use rustc::session::config::Sanitizer; use rustc::ty::TyCtxt; use rustc::ty::maps::Providers; use rustc_data_structures::sync::Lrc; +use rustc_data_structures::fx::FxHashMap; use llvm::{self, Attribute, ValueRef}; use llvm::AttributePlace::Function; @@ -141,6 +142,20 @@ pub fn from_fn_attrs(cx: &CodegenCx, llfn: ValueRef, id: DefId) { llfn, llvm::AttributePlace::Function, cstr("target-features\0"), &val); } + + // Note that currently the `wasm-import-module` doesn't do anything, but + // eventually LLVM 7 should read this and ferry the appropriate import + // module to the output file. + if cx.tcx.sess.target.target.arch == "wasm32" { + if let Some(module) = wasm_import_module(cx.tcx, id) { + llvm::AddFunctionAttrStringValue( + llfn, + llvm::AttributePlace::Function, + cstr("wasm-import-module\0"), + &module, + ); + } + } } fn cstr(s: &'static str) -> &CStr { @@ -170,6 +185,8 @@ pub fn provide(providers: &mut Providers) { tcx.hir.krate().visit_all_item_likes(&mut finder); Lrc::new(finder.list) }; + + provide_extern(providers); } struct WasmSectionFinder<'a, 'tcx: 'a> { @@ -192,3 +209,32 @@ impl<'a, 'tcx: 'a> ItemLikeVisitor<'tcx> for WasmSectionFinder<'a, 'tcx> { fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) {} } + +pub fn provide_extern(providers: &mut Providers) { + providers.wasm_import_module_map = |tcx, cnum| { + let mut ret = FxHashMap(); + for lib in tcx.foreign_modules(cnum).iter() { + let attrs = tcx.get_attrs(lib.def_id); + let mut module = None; + for attr in attrs.iter().filter(|a| a.check_name("wasm_import_module")) { + module = attr.value_str(); + } + let module = match module { + Some(s) => s, + None => continue, + }; + for id in lib.foreign_items.iter() { + assert_eq!(id.krate, cnum); + ret.insert(*id, module.to_string()); + } + } + + Lrc::new(ret) + } +} + +fn wasm_import_module(tcx: TyCtxt, id: DefId) -> Option { + tcx.wasm_import_module_map(id.krate) + .get(&id) + .map(|s| CString::new(&s[..]).unwrap()) +} diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 8e8ba823b6f8d..542cdc5baad32 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -813,6 +813,7 @@ fn link_natively(sess: &Session, } if sess.opts.target_triple == "wasm32-unknown-unknown" { + wasm::rewrite_imports(&out_filename, &trans.crate_info.wasm_imports); wasm::add_custom_sections(&out_filename, &trans.crate_info.wasm_custom_sections); } diff --git a/src/librustc_trans/back/wasm.rs b/src/librustc_trans/back/wasm.rs index 99f1e4b7e78fe..d6d386c9fbe77 100644 --- a/src/librustc_trans/back/wasm.rs +++ b/src/librustc_trans/back/wasm.rs @@ -8,37 +8,254 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::collections::BTreeMap; use std::fs; use std::path::Path; -use std::collections::BTreeMap; +use std::str; +use rustc_data_structures::fx::FxHashMap; use serialize::leb128; +// https://webassembly.github.io/spec/core/binary/modules.html#binary-importsec +const WASM_IMPORT_SECTION_ID: u8 = 2; + +const WASM_EXTERNAL_KIND_FUNCTION: u8 = 0; +const WASM_EXTERNAL_KIND_TABLE: u8 = 1; +const WASM_EXTERNAL_KIND_MEMORY: u8 = 2; +const WASM_EXTERNAL_KIND_GLOBAL: u8 = 3; + +/// Append all the custom sections listed in `sections` to the wasm binary +/// specified at `path`. +/// +/// LLVM 6 which we're using right now doesn't have the ability to create custom +/// sections in wasm files nor does LLD have the ability to merge these sections +/// into one larger section when linking. It's expected that this will +/// eventually get implemented, however! +/// +/// Until that time though this is a custom implementation in rustc to append +/// all sections to a wasm file to the finished product that LLD produces. +/// +/// Support for this is landing in LLVM in https://reviews.llvm.org/D43097, +/// although after that support will need to be in LLD as well. pub fn add_custom_sections(path: &Path, sections: &BTreeMap>) { - let mut wasm = fs::read(path).expect("failed to read wasm output"); + if sections.len() == 0 { + return + } + + let wasm = fs::read(path).expect("failed to read wasm output"); // see https://webassembly.github.io/spec/core/binary/modules.html#custom-section + let mut wasm = WasmEncoder { data: wasm }; for (section, bytes) in sections { // write the `id` identifier, 0 for a custom section - let len = wasm.len(); - leb128::write_u32_leb128(&mut wasm, len, 0); + wasm.byte(0); // figure out how long our name descriptor will be - let mut name = Vec::new(); - leb128::write_u32_leb128(&mut name, 0, section.len() as u32); - name.extend_from_slice(section.as_bytes()); + let mut name = WasmEncoder::new(); + name.str(section); + + // write the length of the payload followed by all its contents + wasm.u32((bytes.len() + name.data.len()) as u32); + wasm.data.extend_from_slice(&name.data); + wasm.data.extend_from_slice(bytes); + } + + fs::write(path, &wasm.data).expect("failed to write wasm output"); +} + +/// Rewrite the module imports are listed from in a wasm module given the field +/// name to module name mapping in `import_map`. +/// +/// LLVM 6 which we're using right now doesn't have the ability to configure the +/// module a wasm symbol is import from. Rather all imported symbols come from +/// the bland `"env"` module unconditionally. Furthermore we'd *also* need +/// support in LLD for preserving these import modules, which it unfortunately +/// currently does not. +/// +/// This function is intended as a hack for now where we manually rewrite the +/// wasm output by LLVM to have the correct import modules listed. The +/// `#[wasm_import_module]` attribute in Rust translates to the module that each +/// symbol is imported from, so here we manually go through the wasm file, +/// decode it, rewrite imports, and then rewrite the wasm module. +/// +/// Support for this was added to LLVM in +/// https://github.com/llvm-mirror/llvm/commit/0f32e1365, although support still +/// needs to be added (AFAIK at the time of this writing) to LLD +pub fn rewrite_imports(path: &Path, import_map: &FxHashMap) { + if import_map.len() == 0 { + return + } + + let wasm = fs::read(path).expect("failed to read wasm output"); + let mut ret = WasmEncoder::new(); + ret.data.extend(&wasm[..8]); + + // skip the 8 byte wasm/version header + for (id, raw) in WasmSections(WasmDecoder::new(&wasm[8..])) { + ret.byte(id); + if id == WASM_IMPORT_SECTION_ID { + info!("rewriting import section"); + let data = rewrite_import_section( + &mut WasmDecoder::new(raw), + import_map, + ); + ret.bytes(&data); + } else { + info!("carry forward section {}, {} bytes long", id, raw.len()); + ret.bytes(raw); + } + } + + fs::write(path, &ret.data).expect("failed to write wasm output"); + + fn rewrite_import_section( + wasm: &mut WasmDecoder, + import_map: &FxHashMap, + ) + -> Vec + { + let mut dst = WasmEncoder::new(); + let n = wasm.u32(); + dst.u32(n); + info!("rewriting {} imports", n); + for _ in 0..n { + rewrite_import_entry(wasm, &mut dst, import_map); + } + return dst.data + } + + fn rewrite_import_entry(wasm: &mut WasmDecoder, + dst: &mut WasmEncoder, + import_map: &FxHashMap) { + // More info about the binary format here is available at: + // https://webassembly.github.io/spec/core/binary/modules.html#import-section + // + // Note that you can also find the whole point of existence of this + // function here, where we map the `module` name to a different one if + // we've got one listed. + let module = wasm.str(); + let field = wasm.str(); + let new_module = if module == "env" { + import_map.get(field).map(|s| &**s).unwrap_or(module) + } else { + module + }; + info!("import rewrite ({} => {}) / {}", module, new_module, field); + dst.str(new_module); + dst.str(field); + let kind = wasm.byte(); + dst.byte(kind); + match kind { + WASM_EXTERNAL_KIND_FUNCTION => dst.u32(wasm.u32()), + WASM_EXTERNAL_KIND_TABLE => { + dst.byte(wasm.byte()); // element_type + dst.limits(wasm.limits()); + } + WASM_EXTERNAL_KIND_MEMORY => dst.limits(wasm.limits()), + WASM_EXTERNAL_KIND_GLOBAL => { + dst.byte(wasm.byte()); // content_type + dst.bool(wasm.bool()); // mutable + } + b => panic!("unknown kind: {}", b), + } + } +} - // write the length of the payload - let len = wasm.len(); - let total_len = bytes.len() + name.len(); - leb128::write_u32_leb128(&mut wasm, len, total_len as u32); +struct WasmSections<'a>(WasmDecoder<'a>); - // write out the name section - wasm.extend(name); +impl<'a> Iterator for WasmSections<'a> { + type Item = (u8, &'a [u8]); - // and now the payload itself - wasm.extend_from_slice(bytes); + fn next(&mut self) -> Option<(u8, &'a [u8])> { + if self.0.data.len() == 0 { + return None + } + + // see https://webassembly.github.io/spec/core/binary/modules.html#sections + let id = self.0.byte(); + let section_len = self.0.u32(); + info!("new section {} / {} bytes", id, section_len); + let section = self.0.skip(section_len as usize); + Some((id, section)) } +} + +struct WasmDecoder<'a> { + data: &'a [u8], +} - fs::write(path, &wasm).expect("failed to write wasm output"); +impl<'a> WasmDecoder<'a> { + fn new(data: &'a [u8]) -> WasmDecoder<'a> { + WasmDecoder { data } + } + + fn byte(&mut self) -> u8 { + self.skip(1)[0] + } + + fn u32(&mut self) -> u32 { + let (n, l1) = leb128::read_u32_leb128(self.data); + self.data = &self.data[l1..]; + return n + } + + fn skip(&mut self, amt: usize) -> &'a [u8] { + let (data, rest) = self.data.split_at(amt); + self.data = rest; + data + } + + fn str(&mut self) -> &'a str { + let len = self.u32(); + str::from_utf8(self.skip(len as usize)).unwrap() + } + + fn bool(&mut self) -> bool { + self.byte() == 1 + } + + fn limits(&mut self) -> (u32, Option) { + let has_max = self.bool(); + (self.u32(), if has_max { Some(self.u32()) } else { None }) + } +} + +struct WasmEncoder { + data: Vec, +} + +impl WasmEncoder { + fn new() -> WasmEncoder { + WasmEncoder { data: Vec::new() } + } + + fn u32(&mut self, val: u32) { + let at = self.data.len(); + leb128::write_u32_leb128(&mut self.data, at, val); + } + + fn byte(&mut self, val: u8) { + self.data.push(val); + } + + fn bytes(&mut self, val: &[u8]) { + self.u32(val.len() as u32); + self.data.extend_from_slice(val); + } + + fn str(&mut self, val: &str) { + self.bytes(val.as_bytes()) + } + + fn bool(&mut self, b: bool) { + self.byte(b as u8); + } + + fn limits(&mut self, limits: (u32, Option)) { + self.bool(limits.1.is_some()); + self.u32(limits.0); + if let Some(c) = limits.1 { + self.u32(c); + } + } } diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 11f952bc5bc77..56eece9f31e7e 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -72,6 +72,7 @@ use type_::Type; use type_of::LayoutLlvmExt; use rustc::util::nodemap::{FxHashMap, FxHashSet, DefIdSet}; use CrateInfo; +use rustc_data_structures::sync::Lrc; use std::any::Any; use std::collections::BTreeMap; @@ -1072,14 +1073,15 @@ impl CrateInfo { used_crates_static: cstore::used_crates(tcx, LinkagePreference::RequireStatic), used_crate_source: FxHashMap(), wasm_custom_sections: BTreeMap::new(), + wasm_imports: FxHashMap(), }; - let load_wasm_sections = tcx.sess.crate_types.borrow() + let load_wasm_items = tcx.sess.crate_types.borrow() .iter() .any(|c| *c != config::CrateTypeRlib) && tcx.sess.opts.target_triple == "wasm32-unknown-unknown"; - if load_wasm_sections { + if load_wasm_items { info!("attempting to load all wasm sections"); for &id in tcx.wasm_custom_sections(LOCAL_CRATE).iter() { let (name, contents) = fetch_wasm_section(tcx, id); @@ -1087,6 +1089,7 @@ impl CrateInfo { .or_insert(Vec::new()) .extend(contents); } + info.load_wasm_imports(tcx, LOCAL_CRATE); } for &cnum in tcx.crates().iter() { @@ -1108,19 +1111,27 @@ impl CrateInfo { if tcx.is_no_builtins(cnum) { info.is_no_builtins.insert(cnum); } - if load_wasm_sections { + if load_wasm_items { for &id in tcx.wasm_custom_sections(cnum).iter() { let (name, contents) = fetch_wasm_section(tcx, id); info.wasm_custom_sections.entry(name) .or_insert(Vec::new()) .extend(contents); } + info.load_wasm_imports(tcx, cnum); } } - return info } + + fn load_wasm_imports(&mut self, tcx: TyCtxt, cnum: CrateNum) { + for (&id, module) in tcx.wasm_import_module_map(cnum).iter() { + let instance = Instance::mono(tcx, id); + let import_name = tcx.symbol_name(instance); + self.wasm_imports.insert(import_name.to_string(), module.clone()); + } + } } fn is_translated_item(tcx: TyCtxt, id: DefId) -> bool { @@ -1248,6 +1259,39 @@ pub fn provide(providers: &mut Providers) { .expect(&format!("failed to find cgu with name {:?}", name)) }; providers.compile_codegen_unit = compile_codegen_unit; + + provide_extern(providers); +} + +pub fn provide_extern(providers: &mut Providers) { + providers.dllimport_foreign_items = |tcx, krate| { + let module_map = tcx.foreign_modules(krate); + let module_map = module_map.iter() + .map(|lib| (lib.def_id, lib)) + .collect::>(); + + let dllimports = tcx.native_libraries(krate) + .iter() + .filter(|lib| { + if lib.kind != cstore::NativeLibraryKind::NativeUnknown { + return false + } + let cfg = match lib.cfg { + Some(ref cfg) => cfg, + None => return true, + }; + attr::cfg_matches(cfg, &tcx.sess.parse_sess, None) + }) + .filter_map(|lib| lib.foreign_module) + .map(|id| &module_map[&id]) + .flat_map(|module| module.foreign_items.iter().cloned()) + .collect(); + Lrc::new(dllimports) + }; + + providers.is_dllimport_foreign_item = |tcx, def_id| { + tcx.dllimport_foreign_items(def_id.krate).contains(&def_id) + }; } pub fn linkage_to_llvm(linkage: Linkage) -> llvm::Linkage { diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index bb2aeca374882..9f654c8ab29c0 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -216,6 +216,8 @@ impl TransCrate for LlvmTransCrate { fn provide_extern(&self, providers: &mut ty::maps::Providers) { back::symbol_export::provide_extern(providers); + base::provide_extern(providers); + attributes::provide_extern(providers); } fn trans_crate<'a, 'tcx>( @@ -403,6 +405,7 @@ struct CrateInfo { used_crates_static: Vec<(CrateNum, LibSource)>, used_crates_dynamic: Vec<(CrateNum, LibSource)>, wasm_custom_sections: BTreeMap>, + wasm_imports: FxHashMap, } __build_diagnostic_array! { librustc_trans, DIAGNOSTICS } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index dbcfee208ca2d..270cee26948e3 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -454,6 +454,9 @@ declare_features! ( // The #[wasm_custom_section] attribute (active, wasm_custom_section, "1.26.0", None, None), + + // The #![wasm_import_module] attribute + (active, wasm_import_module, "1.26.0", None, None), ); declare_features! ( @@ -920,6 +923,10 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG "the `#[no_debug]` attribute was an experimental feature that has been \ deprecated due to lack of demand", cfg_fn!(no_debug))), + ("wasm_import_module", Normal, Gated(Stability::Unstable, + "wasm_import_module", + "experimental attribute", + cfg_fn!(wasm_import_module))), ("omit_gdb_pretty_printer_section", Whitelisted, Gated(Stability::Unstable, "omit_gdb_pretty_printer_section", "the `#[omit_gdb_pretty_printer_section]` \ diff --git a/src/test/run-make/wasm-custom-section/foo.js b/src/test/run-make/wasm-custom-section/foo.js index df69354f3a400..e7a4cfc344efb 100644 --- a/src/test/run-make/wasm-custom-section/foo.js +++ b/src/test/run-make/wasm-custom-section/foo.js @@ -43,4 +43,4 @@ assert.strictEqual(section[1], 6); assert.strictEqual(section[2], 1); assert.strictEqual(section[3], 2); -process.exit(1); +process.exit(0); diff --git a/src/test/run-make/wasm-import-module/Makefile b/src/test/run-make/wasm-import-module/Makefile new file mode 100644 index 0000000000000..399951e516314 --- /dev/null +++ b/src/test/run-make/wasm-import-module/Makefile @@ -0,0 +1,10 @@ +-include ../../run-make-fulldeps/tools.mk + +ifeq ($(TARGET),wasm32-unknown-unknown) +all: + $(RUSTC) foo.rs --target wasm32-unknown-unknown + $(RUSTC) bar.rs -C lto -O --target wasm32-unknown-unknown + $(NODE) foo.js $(TMPDIR)/bar.wasm +else +all: +endif diff --git a/src/test/run-make/wasm-import-module/bar.rs b/src/test/run-make/wasm-import-module/bar.rs new file mode 100644 index 0000000000000..9e659223c651c --- /dev/null +++ b/src/test/run-make/wasm-import-module/bar.rs @@ -0,0 +1,29 @@ +// Copyright 2018 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. + +#![crate_type = "cdylib"] +#![feature(wasm_import_module)] +#![deny(warnings)] + +extern crate foo; + +#[wasm_import_module = "./me"] +extern { + #[link_name = "me_in_dep"] + fn dep(); +} + +#[no_mangle] +pub extern fn foo() { + unsafe { + foo::dep(); + dep(); + } +} diff --git a/src/test/run-make/wasm-import-module/foo.js b/src/test/run-make/wasm-import-module/foo.js new file mode 100644 index 0000000000000..369962e55b1eb --- /dev/null +++ b/src/test/run-make/wasm-import-module/foo.js @@ -0,0 +1,28 @@ +// Copyright 2018 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. + +const fs = require('fs'); +const process = require('process'); +const assert = require('assert'); +const buffer = fs.readFileSync(process.argv[2]); + +let m = new WebAssembly.Module(buffer); +let imports = WebAssembly.Module.imports(m); +console.log('imports', imports); +assert.strictEqual(imports.length, 2); + +assert.strictEqual(imports[0].kind, 'function'); +assert.strictEqual(imports[1].kind, 'function'); + +let modules = [imports[0].module, imports[1].module]; +modules.sort(); + +assert.strictEqual(modules[0], './dep'); +assert.strictEqual(modules[1], './me'); diff --git a/src/test/run-make/wasm-import-module/foo.rs b/src/test/run-make/wasm-import-module/foo.rs new file mode 100644 index 0000000000000..bcd2ca70befaa --- /dev/null +++ b/src/test/run-make/wasm-import-module/foo.rs @@ -0,0 +1,18 @@ +// Copyright 2018 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. + +#![crate_type = "rlib"] +#![feature(wasm_import_module)] +#![deny(warnings)] + +#[wasm_import_module = "./dep"] +extern { + pub fn dep(); +} diff --git a/src/test/ui/feature-gate-wasm_custom_section.stderr b/src/test/ui/feature-gate-wasm_custom_section.stderr index 5977ec9edad5f..1b4415539f449 100644 --- a/src/test/ui/feature-gate-wasm_custom_section.stderr +++ b/src/test/ui/feature-gate-wasm_custom_section.stderr @@ -8,4 +8,4 @@ LL | #[wasm_custom_section = "foo"] //~ ERROR: attribute is currently unstable error: aborting due to previous error -If you want more information on this error, try using "rustc --explain E0658" +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gate-wasm_import_module.rs b/src/test/ui/feature-gate-wasm_import_module.rs new file mode 100644 index 0000000000000..c5898a9c12697 --- /dev/null +++ b/src/test/ui/feature-gate-wasm_import_module.rs @@ -0,0 +1,15 @@ +// Copyright 2018 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. + +#[wasm_import_module = "test"] //~ ERROR: experimental +extern { +} + +fn main() {} diff --git a/src/test/ui/feature-gate-wasm_import_module.stderr b/src/test/ui/feature-gate-wasm_import_module.stderr new file mode 100644 index 0000000000000..bae5fa9d5956c --- /dev/null +++ b/src/test/ui/feature-gate-wasm_import_module.stderr @@ -0,0 +1,11 @@ +error[E0658]: experimental attribute + --> $DIR/feature-gate-wasm_import_module.rs:11:1 + | +LL | #[wasm_import_module = "test"] //~ ERROR: experimental + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add #![feature(wasm_import_module)] to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/wasm-import-module.rs b/src/test/ui/wasm-import-module.rs new file mode 100644 index 0000000000000..0b743d9e486b6 --- /dev/null +++ b/src/test/ui/wasm-import-module.rs @@ -0,0 +1,20 @@ +// Copyright 2018 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(wasm_import_module)] + +#[wasm_import_module] //~ ERROR: must be of the form +extern {} + +#[wasm_import_module = "foo"] //~ ERROR: must only be attached to +fn foo() {} + +fn main() {} + diff --git a/src/test/ui/wasm-import-module.stderr b/src/test/ui/wasm-import-module.stderr new file mode 100644 index 0000000000000..bf301ce5269a7 --- /dev/null +++ b/src/test/ui/wasm-import-module.stderr @@ -0,0 +1,14 @@ +error: must be of the form #[wasm_import_module = "..."] + --> $DIR/wasm-import-module.rs:13:1 + | +LL | #[wasm_import_module] //~ ERROR: must be of the form + | ^^^^^^^^^^^^^^^^^^^^^ + +error: must only be attached to foreign modules + --> $DIR/wasm-import-module.rs:16:1 + | +LL | #[wasm_import_module = "foo"] //~ ERROR: must only be attached to + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors +