mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-27 09:14:20 +00:00
rustc: Update wasm32 support for LLVM 9
This commit brings in a number of minor updates for rustc's support for the wasm target which has changed in the LLVM 9 update. Notable updates include: * The compiler now no longer manually inserts the `producers` section, instead relying on LLVM to do so. LLVM uses the `llvm.ident` metadata for the `processed-by` directive (which is now emitted on the wasm target in this PR) and it uses debuginfo to figure out what `language` to put in the `producers` section. * Threaded WebAssembly code now requires different flags to be passed with LLD. In LLD we now pass: * `--shared-memory` - required since objects are compiled with atomics. This also means that the generated memory will be marked as `shared`. * `--max-memory=1GB` - required with the `--shared-memory` argument since shared memories in WebAssembly must have a maximum size. The 1GB number is intended to be a conservative estimate for rustc, but it should be overridable with `-C link-arg` if necessary. * `--passive-segments` - this has become the default for multithreaded memory, but when compiling a threaded module all data segments need to be marked as passive to ensure they don't re-initialize memory for each thread. This will also cause LLD to emit a synthetic function to initialize memory which users will have to arrange to call. * The `__heap_base` and `__data_end` globals are explicitly exported since they're now hidden by default due to the `--export` flags we pass to LLD.
This commit is contained in:
parent
eedf6ce4ef
commit
a120caf8b4
@ -913,9 +913,12 @@ pub fn compile_unit_metadata(
|
|||||||
}
|
}
|
||||||
|
|
||||||
debug!("compile_unit_metadata: {:?}", name_in_debuginfo);
|
debug!("compile_unit_metadata: {:?}", name_in_debuginfo);
|
||||||
|
let rustc_producer = format!(
|
||||||
|
"rustc version {}",
|
||||||
|
option_env!("CFG_VERSION").expect("CFG_VERSION"),
|
||||||
|
);
|
||||||
// FIXME(#41252) Remove "clang LLVM" if we can get GDB and LLVM to play nice.
|
// FIXME(#41252) Remove "clang LLVM" if we can get GDB and LLVM to play nice.
|
||||||
let producer = format!("clang LLVM (rustc version {})",
|
let producer = format!("clang LLVM ({})", rustc_producer);
|
||||||
(option_env!("CFG_VERSION")).expect("CFG_VERSION"));
|
|
||||||
|
|
||||||
let name_in_debuginfo = name_in_debuginfo.to_string_lossy();
|
let name_in_debuginfo = name_in_debuginfo.to_string_lossy();
|
||||||
let name_in_debuginfo = SmallCStr::new(&name_in_debuginfo);
|
let name_in_debuginfo = SmallCStr::new(&name_in_debuginfo);
|
||||||
@ -980,6 +983,21 @@ pub fn compile_unit_metadata(
|
|||||||
gcov_metadata);
|
gcov_metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Insert `llvm.ident` metadata on the wasm32 targets since that will
|
||||||
|
// get hooked up to the "producer" sections `processed-by` information.
|
||||||
|
if tcx.sess.opts.target_triple.triple().starts_with("wasm32") {
|
||||||
|
let name_metadata = llvm::LLVMMDStringInContext(
|
||||||
|
debug_context.llcontext,
|
||||||
|
rustc_producer.as_ptr() as *const _,
|
||||||
|
rustc_producer.as_bytes().len() as c_uint,
|
||||||
|
);
|
||||||
|
llvm::LLVMAddNamedMetadataOperand(
|
||||||
|
debug_context.llmod,
|
||||||
|
const_cstr!("llvm.ident").as_ptr(),
|
||||||
|
llvm::LLVMMDNodeInContext(debug_context.llcontext, &name_metadata, 1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return unit_metadata;
|
return unit_metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -678,14 +678,6 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(sess: &'a Session,
|
|||||||
sess.fatal(&format!("failed to run dsymutil: {}", e))
|
sess.fatal(&format!("failed to run dsymutil: {}", e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if sess.opts.target_triple.triple() == "wasm32-unknown-unknown" {
|
|
||||||
super::wasm::add_producer_section(
|
|
||||||
&out_filename,
|
|
||||||
&sess.edition().to_string(),
|
|
||||||
option_env!("CFG_VERSION").unwrap_or("unknown"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a boolean indicating whether the specified crate should be ignored
|
/// Returns a boolean indicating whether the specified crate should be ignored
|
||||||
|
@ -881,7 +881,38 @@ pub struct WasmLd<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> WasmLd<'a> {
|
impl<'a> WasmLd<'a> {
|
||||||
fn new(cmd: Command, sess: &'a Session, info: &'a LinkerInfo) -> WasmLd<'a> {
|
fn new(mut cmd: Command, sess: &'a Session, info: &'a LinkerInfo) -> WasmLd<'a> {
|
||||||
|
// If the atomics feature is enabled for wasm then we need a whole bunch
|
||||||
|
// of flags:
|
||||||
|
//
|
||||||
|
// * `--shared-memory` - the link won't even succeed without this, flags
|
||||||
|
// the one linear memory as `shared`
|
||||||
|
//
|
||||||
|
// * `--max-memory=1G` - when specifying a shared memory this must also
|
||||||
|
// be specified. We conservatively choose 1GB but users should be able
|
||||||
|
// to override this with `-C link-arg`.
|
||||||
|
//
|
||||||
|
// * `--import-memory` - it doesn't make much sense for memory to be
|
||||||
|
// exported in a threaded module because typically you're
|
||||||
|
// sharing memory and instantiating the module multiple times. As a
|
||||||
|
// result if it were exported then we'd just have no sharing.
|
||||||
|
//
|
||||||
|
// * `--passive-segments` - all memory segments should be passive to
|
||||||
|
// prevent each module instantiation from reinitializing memory.
|
||||||
|
//
|
||||||
|
// * `--export=__wasm_init_memory` - when using `--passive-segments` the
|
||||||
|
// linker will synthesize this function, and so we need to make sure
|
||||||
|
// that our usage of `--export` below won't accidentally cause this
|
||||||
|
// function to get deleted.
|
||||||
|
let atomics = sess.opts.cg.target_feature.contains("+atomics") ||
|
||||||
|
sess.target.target.options.features.contains("+atomics");
|
||||||
|
if atomics {
|
||||||
|
cmd.arg("--shared-memory");
|
||||||
|
cmd.arg("--max-memory=1073741824");
|
||||||
|
cmd.arg("--import-memory");
|
||||||
|
cmd.arg("--passive-segments");
|
||||||
|
cmd.arg("--export=__wasm_init_memory");
|
||||||
|
}
|
||||||
WasmLd { cmd, sess, info }
|
WasmLd { cmd, sess, info }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -984,6 +1015,13 @@ impl<'a> Linker for WasmLd<'a> {
|
|||||||
for sym in self.info.exports[&crate_type].iter() {
|
for sym in self.info.exports[&crate_type].iter() {
|
||||||
self.cmd.arg("--export").arg(&sym);
|
self.cmd.arg("--export").arg(&sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LLD will hide these otherwise-internal symbols since our `--export`
|
||||||
|
// list above is a whitelist of what to export. Various bits and pieces
|
||||||
|
// of tooling use this, so be sure these symbols make their way out of
|
||||||
|
// the linker as well.
|
||||||
|
self.cmd.arg("--export=__heap_base");
|
||||||
|
self.cmd.arg("--export=__data_end");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subsystem(&mut self, _subsystem: &str) {
|
fn subsystem(&mut self, _subsystem: &str) {
|
||||||
|
@ -6,4 +6,3 @@ pub mod command;
|
|||||||
pub mod symbol_export;
|
pub mod symbol_export;
|
||||||
pub mod archive;
|
pub mod archive;
|
||||||
pub mod rpath;
|
pub mod rpath;
|
||||||
pub mod wasm;
|
|
||||||
|
@ -1,191 +0,0 @@
|
|||||||
use std::fs;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::str;
|
|
||||||
|
|
||||||
use rustc_serialize::leb128;
|
|
||||||
|
|
||||||
// https://webassembly.github.io/spec/core/binary/modules.html#binary-importsec
|
|
||||||
const WASM_CUSTOM_SECTION_ID: u8 = 0;
|
|
||||||
|
|
||||||
/// Adds or augment the existing `producers` section to encode information about
|
|
||||||
/// the Rust compiler used to produce the wasm file.
|
|
||||||
pub fn add_producer_section(
|
|
||||||
path: &Path,
|
|
||||||
rust_version: &str,
|
|
||||||
rustc_version: &str,
|
|
||||||
) {
|
|
||||||
struct Field<'a> {
|
|
||||||
name: &'a str,
|
|
||||||
values: Vec<FieldValue<'a>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
struct FieldValue<'a> {
|
|
||||||
name: &'a str,
|
|
||||||
version: &'a str,
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
let rustc_value = FieldValue {
|
|
||||||
name: "rustc",
|
|
||||||
version: rustc_version,
|
|
||||||
};
|
|
||||||
let rust_value = FieldValue {
|
|
||||||
name: "Rust",
|
|
||||||
version: rust_version,
|
|
||||||
};
|
|
||||||
let mut fields = Vec::new();
|
|
||||||
let mut wrote_rustc = false;
|
|
||||||
let mut wrote_rust = false;
|
|
||||||
|
|
||||||
// Move all sections from the original wasm file to our output, skipping
|
|
||||||
// everything except the producers section
|
|
||||||
for (id, raw) in WasmSections(WasmDecoder::new(&wasm[8..])) {
|
|
||||||
if id != WASM_CUSTOM_SECTION_ID {
|
|
||||||
ret.byte(id);
|
|
||||||
ret.bytes(raw);
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
let mut decoder = WasmDecoder::new(raw);
|
|
||||||
if decoder.str() != "producers" {
|
|
||||||
ret.byte(id);
|
|
||||||
ret.bytes(raw);
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read off the producers section into our fields outside the loop,
|
|
||||||
// we'll re-encode the producers section when we're done (to handle an
|
|
||||||
// entirely missing producers section as well).
|
|
||||||
info!("rewriting existing producers section");
|
|
||||||
|
|
||||||
for _ in 0..decoder.u32() {
|
|
||||||
let name = decoder.str();
|
|
||||||
let mut values = Vec::new();
|
|
||||||
for _ in 0..decoder.u32() {
|
|
||||||
let name = decoder.str();
|
|
||||||
let version = decoder.str();
|
|
||||||
values.push(FieldValue { name, version });
|
|
||||||
}
|
|
||||||
|
|
||||||
if name == "language" {
|
|
||||||
values.push(rust_value);
|
|
||||||
wrote_rust = true;
|
|
||||||
} else if name == "processed-by" {
|
|
||||||
values.push(rustc_value);
|
|
||||||
wrote_rustc = true;
|
|
||||||
}
|
|
||||||
fields.push(Field { name, values });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !wrote_rust {
|
|
||||||
fields.push(Field {
|
|
||||||
name: "language",
|
|
||||||
values: vec![rust_value],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if !wrote_rustc {
|
|
||||||
fields.push(Field {
|
|
||||||
name: "processed-by",
|
|
||||||
values: vec![rustc_value],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append the producers section to the end of the wasm file.
|
|
||||||
let mut section = WasmEncoder::new();
|
|
||||||
section.str("producers");
|
|
||||||
section.u32(fields.len() as u32);
|
|
||||||
for field in fields {
|
|
||||||
section.str(field.name);
|
|
||||||
section.u32(field.values.len() as u32);
|
|
||||||
for value in field.values {
|
|
||||||
section.str(value.name);
|
|
||||||
section.str(value.version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret.byte(WASM_CUSTOM_SECTION_ID);
|
|
||||||
ret.bytes(§ion.data);
|
|
||||||
|
|
||||||
fs::write(path, &ret.data).expect("failed to write wasm output");
|
|
||||||
}
|
|
||||||
|
|
||||||
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.is_empty() {
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct WasmEncoder {
|
|
||||||
data: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WasmEncoder {
|
|
||||||
fn new() -> WasmEncoder {
|
|
||||||
WasmEncoder { data: Vec::new() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn u32(&mut self, val: u32) {
|
|
||||||
leb128::write_u32_leb128(&mut self.data, 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())
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user