Rollup merge of #48883 - alexcrichton:wasm-custom-sections, r=nikomatsakis
rustc: Add a `#[wasm_custom_section]` attribute This commit is an implementation of adding custom sections to wasm artifacts in rustc. The intention here is to expose the ability of the wasm binary format to contain custom sections with arbitrary user-defined data. Currently neither our version of LLVM nor LLD supports this so the implementation is currently custom to rustc itself. The implementation here is to attach a `#[wasm_custom_section = "foo"]` attribute to any `const` which has a type like `[u8; N]`. Other types of constants aren't supported yet but may be added one day! This should hopefully be enough to get off the ground with *some* custom section support. The current semantics are that any constant tagged with `#[wasm_custom_section]` section will be *appended* to the corresponding section in the final output wasm artifact (and this affects dependencies linked in as well, not just the final crate). This means that whatever is interpreting the contents must be able to interpret binary-concatenated sections (or each constant needs to be in its own custom section). To test this change the existing `run-make` test suite was moved to a `run-make-fulldeps` folder and a new `run-make` test suite was added which applies to all targets by default. This test suite currently only has one test which only runs for the wasm target (using a node.js script to use `WebAssembly` in JS to parse the wasm output).
This commit is contained in:
commit
f836ae48e6
591 changed files with 1145 additions and 62 deletions
|
@ -313,6 +313,7 @@ impl<'a> Builder<'a> {
|
|||
test::RunPassFullDepsPretty, test::RunFailFullDepsPretty,
|
||||
test::Crate, test::CrateLibrustc, test::CrateRustdoc, test::Linkcheck,
|
||||
test::Cargotest, test::Cargo, test::Rls, test::ErrorIndex, test::Distcheck,
|
||||
test::RunMakeFullDeps,
|
||||
test::Nomicon, test::Reference, test::RustdocBook, test::RustByExample,
|
||||
test::TheBook, test::UnstableBook,
|
||||
test::Rustfmt, test::Miri, test::Clippy, test::RustdocJS, test::RustdocTheme,
|
||||
|
|
|
@ -915,7 +915,7 @@ impl Step for Assemble {
|
|||
}
|
||||
}
|
||||
|
||||
let lld_install = if build.config.lld_enabled && target_compiler.stage > 0 {
|
||||
let lld_install = if build.config.lld_enabled {
|
||||
Some(builder.ensure(native::Lld {
|
||||
target: target_compiler.host,
|
||||
}))
|
||||
|
|
|
@ -759,12 +759,18 @@ test!(RunFailFullDepsPretty {
|
|||
host: true
|
||||
});
|
||||
|
||||
host_test!(RunMake {
|
||||
default_test!(RunMake {
|
||||
path: "src/test/run-make",
|
||||
mode: "run-make",
|
||||
suite: "run-make"
|
||||
});
|
||||
|
||||
host_test!(RunMakeFullDeps {
|
||||
path: "src/test/run-make-fulldeps",
|
||||
mode: "run-make",
|
||||
suite: "run-make-fulldeps"
|
||||
});
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
struct Compiletest {
|
||||
compiler: Compiler,
|
||||
|
@ -827,8 +833,7 @@ impl Step for Compiletest {
|
|||
// FIXME: Does pretty need librustc compiled? Note that there are
|
||||
// fulldeps test suites with mode = pretty as well.
|
||||
mode == "pretty" ||
|
||||
mode == "rustdoc" ||
|
||||
mode == "run-make" {
|
||||
mode == "rustdoc" {
|
||||
builder.ensure(compile::Rustc { compiler, target });
|
||||
}
|
||||
|
||||
|
@ -849,7 +854,7 @@ impl Step for Compiletest {
|
|||
cmd.arg("--rustc-path").arg(builder.rustc(compiler));
|
||||
|
||||
// Avoid depending on rustdoc when we don't need it.
|
||||
if mode == "rustdoc" || mode == "run-make" {
|
||||
if mode == "rustdoc" || (mode == "run-make" && suite.ends_with("fulldeps")) {
|
||||
cmd.arg("--rustdoc-path").arg(builder.rustdoc(compiler.host));
|
||||
}
|
||||
|
||||
|
@ -931,7 +936,7 @@ impl Step for Compiletest {
|
|||
|
||||
// Only pass correct values for these flags for the `run-make` suite as it
|
||||
// requires that a C++ compiler was configured which isn't always the case.
|
||||
if suite == "run-make" {
|
||||
if suite == "run-make-fulldeps" {
|
||||
let llvm_components = output(Command::new(&llvm_config).arg("--components"));
|
||||
let llvm_cxxflags = output(Command::new(&llvm_config).arg("--cxxflags"));
|
||||
cmd.arg("--cc").arg(build.cc(target))
|
||||
|
@ -944,12 +949,12 @@ impl Step for Compiletest {
|
|||
}
|
||||
}
|
||||
}
|
||||
if suite == "run-make" && !build.config.llvm_enabled {
|
||||
if suite == "run-make-fulldeps" && !build.config.llvm_enabled {
|
||||
println!("Ignoring run-make test suite as they generally don't work without LLVM");
|
||||
return;
|
||||
}
|
||||
|
||||
if suite != "run-make" {
|
||||
if suite != "run-make-fulldeps" {
|
||||
cmd.arg("--cc").arg("")
|
||||
.arg("--cxx").arg("")
|
||||
.arg("--cflags").arg("")
|
||||
|
|
|
@ -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 @@ define_dep_nodes!( <'tcx>
|
|||
[] ImplementationsOfTrait { krate: CrateNum, trait_id: DefId },
|
||||
[] AllTraitImplementations(CrateNum),
|
||||
|
||||
[] DllimportForeignItems(CrateNum),
|
||||
[] IsDllimportForeignItem(DefId),
|
||||
[] IsStaticallyIncludedForeignItem(DefId),
|
||||
[] NativeLibraryKind(DefId),
|
||||
|
@ -650,9 +651,13 @@ define_dep_nodes!( <'tcx>
|
|||
|
||||
[] GetSymbolExportLevel(DefId),
|
||||
|
||||
[] WasmCustomSections(CrateNum),
|
||||
|
||||
[input] Features,
|
||||
|
||||
[] ProgramClausesFor(DefId),
|
||||
[] WasmImportModuleMap(CrateNum),
|
||||
[] ForeignModules(CrateNum),
|
||||
);
|
||||
|
||||
trait DepNodeParams<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> : fmt::Debug {
|
||||
|
|
|
@ -25,6 +25,8 @@ enum Target {
|
|||
Struct,
|
||||
Union,
|
||||
Enum,
|
||||
Const,
|
||||
ForeignMod,
|
||||
Other,
|
||||
}
|
||||
|
||||
|
@ -35,6 +37,8 @@ impl Target {
|
|||
hir::ItemStruct(..) => Target::Struct,
|
||||
hir::ItemUnion(..) => Target::Union,
|
||||
hir::ItemEnum(..) => Target::Enum,
|
||||
hir::ItemConst(..) => Target::Const,
|
||||
hir::ItemForeignMod(..) => Target::ForeignMod,
|
||||
_ => Target::Other,
|
||||
}
|
||||
}
|
||||
|
@ -55,14 +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 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 @@ 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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -318,6 +318,11 @@ fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt,
|
|||
return true;
|
||||
}
|
||||
|
||||
// These constants are special for wasm
|
||||
if attr::contains_name(attrs, "wasm_custom_section") {
|
||||
return true;
|
||||
}
|
||||
|
||||
tcx.lint_level_at_node(lint::builtin::DEAD_CODE, id).0 == lint::Allow
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
@ -678,6 +684,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::instance_def_size_estimate<'tcx>
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> QueryDescription<'tcx> for queries::wasm_custom_sections<'tcx> {
|
||||
fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
|
||||
format!("custom wasm sections for a crate")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> QueryDescription<'tcx> for queries::generics_of<'tcx> {
|
||||
#[inline]
|
||||
fn cache_on_disk(def_id: Self::Key) -> bool {
|
||||
|
@ -699,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> {
|
||||
|
|
|
@ -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<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 @@ define_maps! { <'tcx>
|
|||
[] 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)
|
||||
|
@ -424,6 +429,10 @@ define_maps! { <'tcx>
|
|||
[] fn features_query: features_node(CrateNum) -> Lrc<feature_gate::Features>,
|
||||
|
||||
[] 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!());
|
||||
}
|
||||
|
@ -940,6 +943,9 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
|
|||
DepKind::Features => { force!(features_query, LOCAL_CRATE); }
|
||||
|
||||
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 @@ 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)
|
||||
|
|
|
@ -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<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 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();
|
||||
|
@ -271,6 +270,8 @@ provide! { <'tcx> tcx, def_id, other, cdata,
|
|||
|
||||
Arc::new(cdata.exported_symbols())
|
||||
}
|
||||
|
||||
wasm_custom_sections => { Lrc::new(cdata.wasm_custom_sections()) }
|
||||
}
|
||||
|
||||
pub fn provide<'tcx>(providers: &mut Providers<'tcx>) {
|
||||
|
@ -304,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))
|
||||
|
|
|
@ -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<ForeignModule> {
|
||||
self.root.foreign_modules.decode((self, sess)).collect()
|
||||
}
|
||||
|
||||
pub fn get_dylib_dependency_formats(&self) -> Vec<(CrateNum, LinkagePreference)> {
|
||||
self.root
|
||||
.dylib_dependency_formats
|
||||
|
@ -1067,6 +1071,16 @@ impl<'a, 'tcx> CrateMetadata {
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub fn wasm_custom_sections(&self) -> Vec<DefId> {
|
||||
let sections = self.root
|
||||
.wasm_custom_sections
|
||||
.decode(self)
|
||||
.map(|def_index| self.local_def_id(def_index))
|
||||
.collect::<Vec<_>>();
|
||||
info!("loaded wasm sections {:?}", sections);
|
||||
return sections
|
||||
}
|
||||
|
||||
pub fn get_macro(&self, id: DefIndex) -> (InternedString, MacroDef) {
|
||||
let entry = self.entry(id);
|
||||
match entry.kind {
|
||||
|
@ -1093,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>)
|
||||
|
|
|
@ -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();
|
||||
|
@ -435,6 +439,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
&exported_symbols);
|
||||
let exported_symbols_bytes = self.position() - i;
|
||||
|
||||
// encode wasm custom sections
|
||||
let wasm_custom_sections = self.tcx.wasm_custom_sections(LOCAL_CRATE);
|
||||
let wasm_custom_sections = self.tracked(
|
||||
IsolatedEncoder::encode_wasm_custom_sections,
|
||||
&wasm_custom_sections);
|
||||
|
||||
// Encode and index the items.
|
||||
i = self.position();
|
||||
let items = self.encode_info_for_items();
|
||||
|
@ -474,10 +484,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
lang_items,
|
||||
lang_items_missing,
|
||||
native_libraries,
|
||||
foreign_modules,
|
||||
codemap,
|
||||
def_path_table,
|
||||
impls,
|
||||
exported_symbols,
|
||||
wasm_custom_sections,
|
||||
index,
|
||||
});
|
||||
|
||||
|
@ -1330,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<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();
|
||||
|
||||
|
@ -1444,6 +1461,11 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
|
|||
.cloned())
|
||||
}
|
||||
|
||||
fn encode_wasm_custom_sections(&mut self, statics: &[DefId]) -> LazySeq<DefIndex> {
|
||||
info!("encoding custom wasm section constants {:?}", statics);
|
||||
self.lazy_seq(statics.iter().map(|id| id.index))
|
||||
}
|
||||
|
||||
fn encode_dylib_dependency_formats(&mut self, _: ()) -> LazySeq<Option<LinkagePreference>> {
|
||||
match self.tcx.sess.dependency_formats.borrow().get(&config::CrateTypeDylib) {
|
||||
Some(arr) => {
|
||||
|
|
48
src/librustc_metadata/foreign_modules.rs
Normal file
48
src/librustc_metadata/foreign_modules.rs
Normal file
|
@ -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 <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 isolated_encoder;
|
|||
mod schema;
|
||||
mod native_libs;
|
||||
mod link_args;
|
||||
mod foreign_modules;
|
||||
|
||||
pub mod creader;
|
||||
pub mod cstore;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,10 +200,12 @@ 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>,
|
||||
pub exported_symbols: LazySeq<(ExportedSymbol, SymbolExportLevel)>,
|
||||
pub wasm_custom_sections: LazySeq<DefIndex>,
|
||||
|
||||
pub index: LazySeq<index::Index>,
|
||||
}
|
||||
|
|
|
@ -11,11 +11,14 @@
|
|||
|
||||
use std::ffi::{CStr, CString};
|
||||
|
||||
use rustc::hir::TransFnAttrFlags;
|
||||
use rustc::hir::{self, TransFnAttrFlags};
|
||||
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
|
||||
use rustc::hir::itemlikevisit::ItemLikeVisitor;
|
||||
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;
|
||||
|
@ -139,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 {
|
||||
|
@ -161,4 +178,63 @@ pub fn provide(providers: &mut Providers) {
|
|||
.collect())
|
||||
}
|
||||
};
|
||||
|
||||
providers.wasm_custom_sections = |tcx, cnum| {
|
||||
assert_eq!(cnum, LOCAL_CRATE);
|
||||
let mut finder = WasmSectionFinder { tcx, list: Vec::new() };
|
||||
tcx.hir.krate().visit_all_item_likes(&mut finder);
|
||||
Lrc::new(finder.list)
|
||||
};
|
||||
|
||||
provide_extern(providers);
|
||||
}
|
||||
|
||||
struct WasmSectionFinder<'a, 'tcx: 'a> {
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
list: Vec<DefId>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx: 'a> ItemLikeVisitor<'tcx> for WasmSectionFinder<'a, 'tcx> {
|
||||
fn visit_item(&mut self, i: &'tcx hir::Item) {
|
||||
match i.node {
|
||||
hir::ItemConst(..) => {}
|
||||
_ => return,
|
||||
}
|
||||
if i.attrs.iter().any(|i| i.check_name("wasm_custom_section")) {
|
||||
self.list.push(self.tcx.hir.local_def_id(i.id));
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use back::wasm;
|
||||
use cc::windows_registry;
|
||||
use super::archive::{ArchiveBuilder, ArchiveConfig};
|
||||
use super::bytecode::RLIB_BYTECODE_EXTENSION;
|
||||
|
@ -810,6 +811,12 @@ fn link_natively(sess: &Session,
|
|||
Err(e) => sess.fatal(&format!("failed to run dsymutil: {}", e)),
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
fn exec_linker(sess: &Session, cmd: &mut Command, tmpdir: &Path)
|
||||
|
|
261
src/librustc_trans/back/wasm.rs
Normal file
261
src/librustc_trans/back/wasm.rs
Normal file
|
@ -0,0 +1,261 @@
|
|||
// 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.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
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>>) {
|
||||
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
|
||||
wasm.byte(0);
|
||||
|
||||
// figure out how long our name descriptor will be
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct WasmSections<'a>(WasmDecoder<'a>);
|
||||
|
||||
impl<'a> Iterator for WasmSections<'a> {
|
||||
type Item = (u8, &'a [u8]);
|
||||
|
||||
fn next(&mut self) -> Option<(u8, &'a [u8])> {
|
||||
if self.0.data.len() == 0 {
|
||||
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],
|
||||
}
|
||||
|
||||
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,8 +72,10 @@ 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;
|
||||
use std::ffi::CString;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
|
@ -1070,8 +1072,26 @@ impl CrateInfo {
|
|||
used_crates_dynamic: cstore::used_crates(tcx, LinkagePreference::RequireDynamic),
|
||||
used_crates_static: cstore::used_crates(tcx, LinkagePreference::RequireStatic),
|
||||
used_crate_source: FxHashMap(),
|
||||
wasm_custom_sections: BTreeMap::new(),
|
||||
wasm_imports: FxHashMap(),
|
||||
};
|
||||
|
||||
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_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);
|
||||
info.wasm_custom_sections.entry(name)
|
||||
.or_insert(Vec::new())
|
||||
.extend(contents);
|
||||
}
|
||||
info.load_wasm_imports(tcx, LOCAL_CRATE);
|
||||
}
|
||||
|
||||
for &cnum in tcx.crates().iter() {
|
||||
info.native_libraries.insert(cnum, tcx.native_libraries(cnum));
|
||||
info.crate_name.insert(cnum, tcx.crate_name(cnum).to_string());
|
||||
|
@ -1091,11 +1111,27 @@ impl CrateInfo {
|
|||
if tcx.is_no_builtins(cnum) {
|
||||
info.is_no_builtins.insert(cnum);
|
||||
}
|
||||
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 {
|
||||
|
@ -1223,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 {
|
||||
|
@ -1270,3 +1339,44 @@ mod temp_stable_hash_impls {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_wasm_section(tcx: TyCtxt, id: DefId) -> (String, Vec<u8>) {
|
||||
use rustc::mir::interpret::{GlobalId, Value, PrimVal};
|
||||
use rustc::middle::const_val::ConstVal;
|
||||
|
||||
info!("loading wasm section {:?}", id);
|
||||
|
||||
let section = tcx.get_attrs(id)
|
||||
.iter()
|
||||
.find(|a| a.check_name("wasm_custom_section"))
|
||||
.expect("missing #[wasm_custom_section] attribute")
|
||||
.value_str()
|
||||
.expect("malformed #[wasm_custom_section] attribute");
|
||||
|
||||
let instance = ty::Instance::mono(tcx, id);
|
||||
let cid = GlobalId {
|
||||
instance,
|
||||
promoted: None
|
||||
};
|
||||
let param_env = ty::ParamEnv::reveal_all();
|
||||
let val = tcx.const_eval(param_env.and(cid)).unwrap();
|
||||
|
||||
let val = match val.val {
|
||||
ConstVal::Value(val) => val,
|
||||
ConstVal::Unevaluated(..) => bug!("should be evaluated"),
|
||||
};
|
||||
let val = match val {
|
||||
Value::ByRef(ptr, _align) => ptr.into_inner_primval(),
|
||||
ref v => bug!("should be ByRef, was {:?}", v),
|
||||
};
|
||||
let mem = match val {
|
||||
PrimVal::Ptr(mem) => mem,
|
||||
ref v => bug!("should be Ptr, was {:?}", v),
|
||||
};
|
||||
assert_eq!(mem.offset, 0);
|
||||
let alloc = tcx
|
||||
.interpret_interner
|
||||
.get_alloc(mem.alloc_id)
|
||||
.expect("miri allocation never successfully created");
|
||||
(section.to_string(), alloc.bytes.clone())
|
||||
}
|
||||
|
|
|
@ -72,6 +72,7 @@ pub use llvm_util::target_features;
|
|||
use std::any::Any;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::mpsc;
|
||||
use std::collections::BTreeMap;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
|
||||
use rustc::dep_graph::DepGraph;
|
||||
|
@ -98,6 +99,7 @@ mod back {
|
|||
pub mod symbol_export;
|
||||
pub mod write;
|
||||
mod rpath;
|
||||
mod wasm;
|
||||
}
|
||||
|
||||
mod abi;
|
||||
|
@ -214,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>(
|
||||
|
@ -400,6 +404,8 @@ struct CrateInfo {
|
|||
used_crate_source: FxHashMap<CrateNum, Lrc<CrateSource>>,
|
||||
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 }
|
||||
|
|
|
@ -1182,9 +1182,15 @@ pub fn check_item_type<'a,'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &'tcx hir::Item
|
|||
let _indenter = indenter();
|
||||
match it.node {
|
||||
// Consts can play a role in type-checking, so they are included here.
|
||||
hir::ItemStatic(..) |
|
||||
hir::ItemStatic(..) => {
|
||||
tcx.typeck_tables_of(tcx.hir.local_def_id(it.id));
|
||||
}
|
||||
hir::ItemConst(..) => {
|
||||
tcx.typeck_tables_of(tcx.hir.local_def_id(it.id));
|
||||
if it.attrs.iter().any(|a| a.check_name("wasm_custom_section")) {
|
||||
let def_id = tcx.hir.local_def_id(it.id);
|
||||
check_const_is_u8_array(tcx, def_id, it.span);
|
||||
}
|
||||
}
|
||||
hir::ItemEnum(ref enum_definition, _) => {
|
||||
check_enum(tcx,
|
||||
|
@ -1256,6 +1262,21 @@ pub fn check_item_type<'a,'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &'tcx hir::Item
|
|||
}
|
||||
}
|
||||
|
||||
fn check_const_is_u8_array<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
def_id: DefId,
|
||||
span: Span) {
|
||||
match tcx.type_of(def_id).sty {
|
||||
ty::TyArray(t, _) => {
|
||||
match t.sty {
|
||||
ty::TyUint(ast::UintTy::U8) => return,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
tcx.sess.span_err(span, "must be an array of bytes like `[u8; N]`");
|
||||
}
|
||||
|
||||
fn check_on_unimplemented<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
trait_def_id: DefId,
|
||||
item: &hir::Item) {
|
||||
|
|
|
@ -451,6 +451,12 @@ declare_features! (
|
|||
|
||||
// `use path as _;` and `extern crate c as _;`
|
||||
(active, underscore_imports, "1.26.0", Some(48216), None),
|
||||
|
||||
// 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! (
|
||||
|
@ -917,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]` \
|
||||
|
@ -1004,6 +1014,11 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
|
|||
"never will be stable",
|
||||
cfg_fn!(rustc_attrs))),
|
||||
|
||||
("wasm_custom_section", Whitelisted, Gated(Stability::Unstable,
|
||||
"wasm_custom_section",
|
||||
"attribute is currently unstable",
|
||||
cfg_fn!(wasm_custom_section))),
|
||||
|
||||
// Crate level attributes
|
||||
("crate_name", CrateLevel, Ungated),
|
||||
("crate_type", CrateLevel, Ungated),
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue