提交 d889957d 编写于 作者: A Alex Crichton

rustc: Add a `#[wasm_import_module]` attribute

This commit adds a new attribute to the Rust compiler specific to the wasm
target (and no other targets). The `#[wasm_import_module]` attribute is used to
specify the module that a name is imported from, and is used like so:

    #[wasm_import_module = "./foo.js"]
    extern {
        fn some_js_function();
    }

Here the import of the symbol `some_js_function` is tagged with the `./foo.js`
module in the wasm output file. Wasm-the-format includes two fields on all
imports, a module and a field. The field is the symbol name (`some_js_function`
above) and the module has historically unconditionally been `"env"`. I'm not
sure if this `"env"` convention has asm.js or LLVM roots, but regardless we'd
like the ability to configure it!

The proposed ES module integration with wasm (aka a wasm module is "just another
ES module") requires that the import module of wasm imports is interpreted as an
ES module import, meaning that you'll need to encode paths, NPM packages, etc.
As a result, we'll need this to be something other than `"env"`!

Unfortunately neither our version of LLVM nor LLD supports custom import modules
(aka anything not `"env"`). My hope is that by the time LLVM 7 is released both
will have support, but in the meantime this commit adds some primitive
encoding/decoding of wasm files to the compiler. This way rustc postprocesses
the wasm module that LLVM emits to ensure it's got all the imports we'd like to
have in it.

Eventually I'd ideally like to unconditionally require this attribute to be
placed on all `extern { ... }` blocks. For now though it seemed prudent to add
it as an unstable attribute, so for now it's not required (as that'd force usage
of a feature gate). Hopefully it doesn't take too long to "stabilize" this!

cc rust-lang-nursery/rust-wasm#29
上级 7df6f416
......@@ -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 \
......
......@@ -593,6 +593,7 @@ pub fn fingerprint_needed_for_crate_hash(self) -> bool {
[] ImplementationsOfTrait { krate: CrateNum, trait_id: DefId },
[] AllTraitImplementations(CrateNum),
[] DllimportForeignItems(CrateNum),
[] IsDllimportForeignItem(DefId),
[] IsStaticallyIncludedForeignItem(DefId),
[] NativeLibraryKind(DefId),
......@@ -655,6 +656,8 @@ pub fn fingerprint_needed_for_crate_hash(self) -> bool {
[input] Features,
[] ProgramClausesFor(DefId),
[] WasmImportModuleMap(CrateNum),
[] ForeignModules(CrateNum),
);
trait DepNodeParams<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> : fmt::Debug {
......
......@@ -26,6 +26,7 @@ enum Target {
Union,
Enum,
Const,
ForeignMod,
Other,
}
......@@ -37,6 +38,7 @@ fn from_item(item: &hir::Item) -> Target {
hir::ItemUnion(..) => Target::Union,
hir::ItemEnum(..) => Target::Enum,
hir::ItemConst(..) => Target::Const,
hir::ItemForeignMod(..) => Target::ForeignMod,
_ => Target::Other,
}
}
......@@ -57,25 +59,42 @@ fn check_attributes(&self, item: &hir::Item, target: Target) {
.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);
}
......
......@@ -33,7 +33,12 @@
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 {
......
......@@ -132,7 +132,13 @@ pub struct NativeLibrary {
pub kind: NativeLibraryKind,
pub name: Symbol,
pub cfg: Option<ast::MetaItem>,
pub foreign_module: Option<DefId>,
}
#[derive(Clone, Hash, RustcEncodable, RustcDecodable)]
pub struct ForeignModule {
pub foreign_items: Vec<DefId>,
pub def_id: DefId,
}
pub enum LoadedMacro {
......
......@@ -430,6 +430,12 @@ fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
}
}
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 @@ fn describe(_tcx: TyCtxt, _: DefId) -> String {
}
}
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> {
......
......@@ -18,7 +18,7 @@
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 @@
[] fn native_libraries: NativeLibraries(CrateNum) -> Lrc<Vec<NativeLibrary>>,
[] fn foreign_modules: ForeignModules(CrateNum) -> Lrc<Vec<ForeignModule>>,
[] fn plugin_registrar_fn: PluginRegistrarFn(CrateNum) -> Option<DefId>,
[] fn derive_registrar_fn: DeriveRegistrarFn(CrateNum) -> Option<DefId>,
[] fn crate_disambiguator: CrateDisambiguator(CrateNum) -> CrateDisambiguator,
......@@ -331,6 +334,8 @@
[] fn all_trait_implementations: AllTraitImplementations(CrateNum)
-> Lrc<Vec<DefId>>,
[] fn dllimport_foreign_items: DllimportForeignItems(CrateNum)
-> Lrc<FxHashSet<DefId>>,
[] 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 @@
[] fn program_clauses_for: ProgramClausesFor(DefId) -> Lrc<Vec<Clause<'tcx>>>,
[] fn wasm_custom_sections: WasmCustomSections(CrateNum) -> Lrc<Vec<DefId>>,
[] fn wasm_import_module_map: WasmImportModuleMap(CrateNum)
-> Lrc<FxHashMap<DefId, String>>,
}
//////////////////////////////////////////////////////////////////////
......
......@@ -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
......
......@@ -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 @@ fn register_crate(&mut self,
.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 @@ fn register_crate(&mut self,
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)
......
......@@ -20,7 +20,7 @@
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 @@
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<Vec<(ast::Name, Lrc<SyntaxExtension>)>>,
// Foreign items imported from a dylib (Windows only)
pub dllimport_foreign_items: FxHashSet<DefIndex>,
}
pub struct CStore {
......
......@@ -12,6 +12,7 @@
use encoder;
use link_args;
use native_libs;
use foreign_modules;
use schema;
use rustc::ty::maps::QueryConfig;
......@@ -197,6 +198,7 @@ fn into_args(self) -> (DefId, DefId) { (self.0.as_def_id(), self.1) }
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 @@ fn into_args(self) -> (DefId, DefId) { (self.0.as_def_id(), self.1) }
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 @@ fn is_const_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
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))
......
......@@ -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 @@ pub fn get_native_libraries(&self, sess: &Session) -> Vec<NativeLibrary> {
self.root.native_libraries.decode((self, sess)).collect()
}
pub fn get_foreign_modules(&self, sess: &Session) -> Vec<ForeignModule> {
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 @@ pub fn is_foreign_item(&self, id: DefIndex) -> bool {
}
}
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>)
......
......@@ -14,7 +14,7 @@
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 @@ fn encode_crate_root(&mut self) -> Lazy<CrateRoot> {
());
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 @@ fn encode_crate_root(&mut self) -> Lazy<CrateRoot> {
lang_items,
lang_items_missing,
native_libraries,
foreign_modules,
codemap,
def_path_table,
impls,
......@@ -1337,6 +1342,11 @@ fn encode_native_libraries(&mut self, _: ()) -> LazySeq<NativeLibrary> {
self.lazy_seq(used_libraries.iter().cloned())
}
fn encode_foreign_modules(&mut self, _: ()) -> LazySeq<ForeignModule> {
let foreign_modules = self.tcx.foreign_modules(LOCAL_CRATE);
self.lazy_seq(foreign_modules.iter().cloned())
}
fn encode_crate_deps(&mut self, _: ()) -> LazySeq<CrateDep> {
let crates = self.tcx.crates();
......
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
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<ForeignModule> {
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<ForeignModule>,
}
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) {}
}
......@@ -56,6 +56,7 @@
mod schema;
mod native_libs;
mod link_args;
mod foreign_modules;
pub mod creader;
pub mod cstore;
......
......@@ -105,14 +105,11 @@ fn visit_item(&mut self, it: &'tcx hir::Item) {
} 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 @@ fn process_command_line(&mut self) {
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);
}
......
......@@ -15,8 +15,8 @@
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<lang_items::LangItem>,
pub native_libraries: LazySeq<NativeLibrary>,
pub foreign_modules: LazySeq<ForeignModule>,
pub codemap: LazySeq<syntax_pos::FileMap>,
pub def_path_table: Lazy<hir::map::definitions::DefPathTable>,
pub impls: LazySeq<TraitImpls>,
......
......@@ -18,6 +18,7 @@
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 @@ fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) {}
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<CString> {
tcx.wasm_import_module_map(id.krate)
.get(&id)
.map(|s| CString::new(&s[..]).unwrap())
}
......@@ -813,6 +813,7 @@ fn escape_string(s: &[u8]) -> String {
}
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);
}
......
......@@ -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<String, Vec<u8>>) {
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<String, String>) {
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<String, String>,
)
-> Vec<u8>
{
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<String, String>) {
// 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<u32>) {
let has_max = self.bool();
(self.u32(), if has_max { Some(self.u32()) } else { None })
}
}
struct WasmEncoder {
data: Vec<u8>,
}
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<u32>)) {
self.bool(limits.1.is_some());
self.u32(limits.0);
if let Some(c) = limits.1 {
self.u32(c);
}
}
}
......@@ -72,6 +72,7 @@
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 @@ pub fn new(tcx: TyCtxt) -> 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 @@ pub fn new(tcx: TyCtxt) -> CrateInfo {
.or_insert(Vec::new())
.extend(contents);
}
info.load_wasm_imports(tcx, LOCAL_CRATE);
}
for &cnum in tcx.crates().iter() {
......@@ -1108,19 +1111,27 @@ pub fn new(tcx: TyCtxt) -> 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::<FxHashMap<_, _>>();
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 {
......
......@@ -216,6 +216,8 @@ fn provide(&self, providers: &mut ty::maps::Providers) {
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<String, Vec<u8>>,
wasm_imports: FxHashMap<String, String>,
}
__build_diagnostic_array! { librustc_trans, DIAGNOSTICS }
......@@ -454,6 +454,9 @@ pub fn walk_feature_fields<F>(&self, mut f: F)
// 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 fn is_builtin_attr(attr: &ast::Attribute) -> bool {
"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]` \
......
......@@ -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);
-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
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![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();
}
}
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
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');
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![crate_type = "rlib"]
#![feature(wasm_import_module)]
#![deny(warnings)]
#[wasm_import_module = "./dep"]
extern {
pub fn dep();
}
......@@ -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`.
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[wasm_import_module = "test"] //~ ERROR: experimental
extern {
}
fn main() {}
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`.
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![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() {}
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
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册