Skip to content

Commit 029b8ff

Browse files
authored
Merge pull request #1675 from alexcrichton/llvm-9-threads
Update 'threads-xform' for LLVM 9
2 parents efacd8b + b9b8c20 commit 029b8ff

File tree

1 file changed

+81
-25
lines changed
  • crates/threads-xform/src

1 file changed

+81
-25
lines changed

crates/threads-xform/src/lib.rs

+81-25
Original file line numberDiff line numberDiff line change
@@ -78,25 +78,57 @@ impl Config {
7878
///
7979
/// More and/or less may happen here over time, stay tuned!
8080
pub fn run(&self, module: &mut Module) -> Result<(), Error> {
81-
let memory = update_memory(module, self.maximum_memory)?;
82-
let segments = switch_data_segments_to_passive(module, memory)?;
8381
let stack_pointer = find_stack_pointer(module)?;
84-
82+
let memory = find_memory(module)?;
83+
let addr = inject_thread_id_counter(module, memory)?;
8584
let zero = InitExpr::Value(Value::I32(0));
8685
let globals = Globals {
8786
thread_id: module.globals.add_local(ValType::I32, true, zero),
8887
thread_tcb: module.globals.add_local(ValType::I32, true, zero),
8988
};
90-
let addr = inject_thread_id_counter(module, memory)?;
91-
start_with_init_memory(
89+
90+
// There was an "inflection point" at the LLVM 9 release where LLD
91+
// started having better support for producing binaries capable of being
92+
// used with multi-threading. Prior to LLVM 9 (e.g. nightly releases
93+
// before July 2019 basically) we had to sort of paper over a lot of
94+
// support that hadn't been added to LLD. With LLVM 9 and onwards though
95+
// we expect Rust binaries to be pretty well formed if prepared for
96+
// threading when they come out of LLD. This `if` statement basically
97+
// switches on these two cases, figuring out if we're "old style" or
98+
// "new style".
99+
let mem = module.memories.get_mut(memory);
100+
let memory_init = if mem.shared {
101+
let prev_max = mem.maximum.unwrap();
102+
assert!(mem.import.is_some());
103+
mem.maximum = Some(cmp::max(self.maximum_memory / PAGE_SIZE, prev_max));
104+
assert!(mem.data.is_empty());
105+
106+
let init_memory = module
107+
.exports
108+
.iter()
109+
.find(|e| e.name == "__wasm_init_memory")
110+
.ok_or_else(|| format_err!("failed to find `__wasm_init_memory`"))?;
111+
let init_memory_id = match init_memory.item {
112+
walrus::ExportItem::Function(f) => f,
113+
_ => bail!("`__wasm_init_memory` must be a function"),
114+
};
115+
let export_id = init_memory.id();
116+
module.exports.delete(export_id);
117+
InitMemory::Call(init_memory_id)
118+
} else {
119+
update_memory(module, memory, self.maximum_memory)?;
120+
InitMemory::Segments(switch_data_segments_to_passive(module, memory)?)
121+
};
122+
inject_start(
92123
module,
93-
&segments,
124+
memory_init,
94125
&globals,
95126
addr,
96127
stack_pointer,
97128
self.thread_stack_size,
98129
memory,
99130
);
131+
100132
implement_thread_intrinsics(module, &globals)?;
101133
Ok(())
102134
}
@@ -124,15 +156,20 @@ fn switch_data_segments_to_passive(
124156
Ok(ret)
125157
}
126158

127-
fn update_memory(module: &mut Module, max: u32) -> Result<MemoryId, Error> {
128-
assert!(max % PAGE_SIZE == 0);
129-
let mut memories = module.memories.iter_mut();
159+
fn find_memory(module: &mut Module) -> Result<MemoryId, Error> {
160+
let mut memories = module.memories.iter();
130161
let memory = memories
131162
.next()
132163
.ok_or_else(|| format_err!("currently incompatible with no memory modules"))?;
133164
if memories.next().is_some() {
134165
bail!("only one memory is currently supported");
135166
}
167+
Ok(memory.id())
168+
}
169+
170+
fn update_memory(module: &mut Module, memory: MemoryId, max: u32) -> Result<MemoryId, Error> {
171+
assert!(max % PAGE_SIZE == 0);
172+
let memory = module.memories.get_mut(memory);
136173

137174
// For multithreading if we want to use the exact same module on all
138175
// threads we'll need to be sure to import memory, so switch it to an
@@ -245,9 +282,14 @@ fn find_stack_pointer(module: &mut Module) -> Result<Option<GlobalId>, Error> {
245282
}
246283
}
247284

248-
fn start_with_init_memory(
285+
enum InitMemory {
286+
Segments(Vec<PassiveSegment>),
287+
Call(walrus::FunctionId),
288+
}
289+
290+
fn inject_start(
249291
module: &mut Module,
250-
segments: &[PassiveSegment],
292+
memory_init: InitMemory,
251293
globals: &Globals,
252294
addr: u32,
253295
stack_pointer: Option<GlobalId>,
@@ -321,6 +363,15 @@ fn start_with_init_memory(
321363
let sp = block.binop(BinaryOp::I32Add, sp_base, stack_size);
322364
let set_stack_pointer = block.global_set(stack_pointer, sp);
323365
block.expr(set_stack_pointer);
366+
367+
// FIXME(WebAssembly/tool-conventions#117) we probably don't want to
368+
// duplicate drop with `if_zero_block` or otherwise just infer to drop
369+
// all these data segments, this seems like something to synthesize in
370+
// the linker...
371+
for segment in module.data.iter() {
372+
let drop = block.data_drop(segment.id());
373+
block.expr(drop);
374+
}
324375
}
325376
let if_nonzero_block = block.id();
326377
drop(block);
@@ -330,27 +381,32 @@ fn start_with_init_memory(
330381
// memory, however, so do that here.
331382
let if_zero_block = {
332383
let mut block = builder.if_else_block(Box::new([]), Box::new([]));
333-
for segment in segments {
334-
let zero = block.i32_const(0);
335-
let offset = match segment.offset {
336-
InitExpr::Global(id) => block.global_get(id),
337-
InitExpr::Value(v) => block.const_(v),
338-
};
339-
let len = block.i32_const(segment.len as i32);
340-
let init = block.memory_init(memory, segment.id, offset, zero, len);
341-
block.expr(init);
384+
match memory_init {
385+
InitMemory::Segments(segments) => {
386+
for segment in segments {
387+
let zero = block.i32_const(0);
388+
let offset = match segment.offset {
389+
InitExpr::Global(id) => block.global_get(id),
390+
InitExpr::Value(v) => block.const_(v),
391+
};
392+
let len = block.i32_const(segment.len as i32);
393+
let init = block.memory_init(memory, segment.id, offset, zero, len);
394+
block.expr(init);
395+
let drop = block.data_drop(segment.id);
396+
block.expr(drop);
397+
}
398+
}
399+
InitMemory::Call(wasm_init_memory) => {
400+
let call = block.call(wasm_init_memory, Box::new([]));
401+
block.expr(call);
402+
}
342403
}
343404
block.id()
344405
};
345406

346407
let block = builder.if_else(thread_id_is_nonzero, if_nonzero_block, if_zero_block);
347408
exprs.push(block);
348409

349-
// On all threads now memory segments are no longer needed
350-
for segment in segments {
351-
exprs.push(builder.data_drop(segment.id));
352-
}
353-
354410
// If a start function previously existed we're done with our own
355411
// initialization so delegate to them now.
356412
if let Some(id) = module.start.take() {

0 commit comments

Comments
 (0)