@@ -78,25 +78,57 @@ impl Config {
78
78
///
79
79
/// More and/or less may happen here over time, stay tuned!
80
80
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) ?;
83
81
let stack_pointer = find_stack_pointer ( module) ?;
84
-
82
+ let memory = find_memory ( module) ?;
83
+ let addr = inject_thread_id_counter ( module, memory) ?;
85
84
let zero = InitExpr :: Value ( Value :: I32 ( 0 ) ) ;
86
85
let globals = Globals {
87
86
thread_id : module. globals . add_local ( ValType :: I32 , true , zero) ,
88
87
thread_tcb : module. globals . add_local ( ValType :: I32 , true , zero) ,
89
88
} ;
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 (
92
123
module,
93
- & segments ,
124
+ memory_init ,
94
125
& globals,
95
126
addr,
96
127
stack_pointer,
97
128
self . thread_stack_size ,
98
129
memory,
99
130
) ;
131
+
100
132
implement_thread_intrinsics ( module, & globals) ?;
101
133
Ok ( ( ) )
102
134
}
@@ -124,15 +156,20 @@ fn switch_data_segments_to_passive(
124
156
Ok ( ret)
125
157
}
126
158
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 ( ) ;
130
161
let memory = memories
131
162
. next ( )
132
163
. ok_or_else ( || format_err ! ( "currently incompatible with no memory modules" ) ) ?;
133
164
if memories. next ( ) . is_some ( ) {
134
165
bail ! ( "only one memory is currently supported" ) ;
135
166
}
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) ;
136
173
137
174
// For multithreading if we want to use the exact same module on all
138
175
// 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> {
245
282
}
246
283
}
247
284
248
- fn start_with_init_memory (
285
+ enum InitMemory {
286
+ Segments ( Vec < PassiveSegment > ) ,
287
+ Call ( walrus:: FunctionId ) ,
288
+ }
289
+
290
+ fn inject_start (
249
291
module : & mut Module ,
250
- segments : & [ PassiveSegment ] ,
292
+ memory_init : InitMemory ,
251
293
globals : & Globals ,
252
294
addr : u32 ,
253
295
stack_pointer : Option < GlobalId > ,
@@ -321,6 +363,15 @@ fn start_with_init_memory(
321
363
let sp = block. binop ( BinaryOp :: I32Add , sp_base, stack_size) ;
322
364
let set_stack_pointer = block. global_set ( stack_pointer, sp) ;
323
365
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
+ }
324
375
}
325
376
let if_nonzero_block = block. id ( ) ;
326
377
drop ( block) ;
@@ -330,27 +381,32 @@ fn start_with_init_memory(
330
381
// memory, however, so do that here.
331
382
let if_zero_block = {
332
383
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
+ }
342
403
}
343
404
block. id ( )
344
405
} ;
345
406
346
407
let block = builder. if_else ( thread_id_is_nonzero, if_nonzero_block, if_zero_block) ;
347
408
exprs. push ( block) ;
348
409
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
-
354
410
// If a start function previously existed we're done with our own
355
411
// initialization so delegate to them now.
356
412
if let Some ( id) = module. start . take ( ) {
0 commit comments