1
+ use rustc_abi:: { BackendRepr , Float , Integer , Primitive , RegKind } ;
1
2
use rustc_attr_parsing:: InstructionSetAttr ;
3
+ use rustc_hir:: def_id:: DefId ;
2
4
use rustc_middle:: mir:: mono:: { Linkage , MonoItem , MonoItemData , Visibility } ;
3
5
use rustc_middle:: mir:: { Body , InlineAsmOperand } ;
4
- use rustc_middle:: ty:: layout:: { HasTyCtxt , HasTypingEnv , LayoutOf } ;
5
- use rustc_middle:: ty:: { Instance , TyCtxt } ;
6
- use rustc_middle:: { bug, ty} ;
6
+ use rustc_middle:: ty:: layout:: { FnAbiOf , HasTyCtxt , HasTypingEnv , LayoutOf } ;
7
+ use rustc_middle:: ty:: { Instance , Ty , TyCtxt } ;
8
+ use rustc_middle:: { bug, span_bug , ty} ;
7
9
use rustc_span:: sym;
10
+ use rustc_target:: callconv:: { ArgAbi , FnAbi , PassMode } ;
11
+ use rustc_target:: spec:: WasmCAbi ;
8
12
9
13
use crate :: common;
10
14
use crate :: traits:: { AsmCodegenMethods , BuilderMethods , GlobalAsmOperandRef , MiscCodegenMethods } ;
@@ -32,7 +36,8 @@ pub(crate) fn codegen_naked_asm<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
32
36
33
37
let item_data = cx. codegen_unit ( ) . items ( ) . get ( & MonoItem :: Fn ( instance) ) . unwrap ( ) ;
34
38
let name = cx. mangled_name ( instance) ;
35
- let ( begin, end) = prefix_and_suffix ( cx. tcx ( ) , instance, & name, item_data) ;
39
+ let fn_abi = cx. fn_abi_of_instance ( instance, ty:: List :: empty ( ) ) ;
40
+ let ( begin, end) = prefix_and_suffix ( cx. tcx ( ) , instance, & name, item_data, fn_abi) ;
36
41
37
42
let mut template_vec = Vec :: new ( ) ;
38
43
template_vec. push ( rustc_ast:: ast:: InlineAsmTemplatePiece :: String ( begin. into ( ) ) ) ;
@@ -103,6 +108,7 @@ enum AsmBinaryFormat {
103
108
Elf ,
104
109
Macho ,
105
110
Coff ,
111
+ Wasm ,
106
112
}
107
113
108
114
impl AsmBinaryFormat {
@@ -111,6 +117,8 @@ impl AsmBinaryFormat {
111
117
Self :: Coff
112
118
} else if target. is_like_osx {
113
119
Self :: Macho
120
+ } else if target. is_like_wasm {
121
+ Self :: Wasm
114
122
} else {
115
123
Self :: Elf
116
124
}
@@ -122,6 +130,7 @@ fn prefix_and_suffix<'tcx>(
122
130
instance : Instance < ' tcx > ,
123
131
asm_name : & str ,
124
132
item_data : & MonoItemData ,
133
+ fn_abi : & FnAbi < ' tcx , Ty < ' tcx > > ,
125
134
) -> ( String , String ) {
126
135
use std:: fmt:: Write ;
127
136
@@ -169,7 +178,7 @@ fn prefix_and_suffix<'tcx>(
169
178
}
170
179
Linkage :: LinkOnceAny | Linkage :: LinkOnceODR | Linkage :: WeakAny | Linkage :: WeakODR => {
171
180
match asm_binary_format {
172
- AsmBinaryFormat :: Elf | AsmBinaryFormat :: Coff => {
181
+ AsmBinaryFormat :: Elf | AsmBinaryFormat :: Coff | AsmBinaryFormat :: Wasm => {
173
182
writeln ! ( w, ".weak {asm_name}" ) ?;
174
183
}
175
184
AsmBinaryFormat :: Macho => {
@@ -264,7 +273,161 @@ fn prefix_and_suffix<'tcx>(
264
273
writeln ! ( end, "{}" , arch_suffix) . unwrap ( ) ;
265
274
}
266
275
}
276
+ AsmBinaryFormat :: Wasm => {
277
+ let section = link_section. unwrap_or ( format ! ( ".text.{asm_name}" ) ) ;
278
+
279
+ writeln ! ( begin, ".section {section},\" \" ,@" ) . unwrap ( ) ;
280
+ // wasm functions cannot be aligned, so skip
281
+ write_linkage ( & mut begin) . unwrap ( ) ;
282
+ if let Visibility :: Hidden = item_data. visibility {
283
+ writeln ! ( begin, ".hidden {asm_name}" ) . unwrap ( ) ;
284
+ }
285
+ writeln ! ( begin, ".type {asm_name}, @function" ) . unwrap ( ) ;
286
+ if !arch_prefix. is_empty ( ) {
287
+ writeln ! ( begin, "{}" , arch_prefix) . unwrap ( ) ;
288
+ }
289
+ writeln ! ( begin, "{asm_name}:" ) . unwrap ( ) ;
290
+ writeln ! (
291
+ begin,
292
+ ".functype {asm_name} {}" ,
293
+ wasm_functype( tcx, fn_abi, instance. def_id( ) )
294
+ )
295
+ . unwrap ( ) ;
296
+
297
+ writeln ! ( end) . unwrap ( ) ;
298
+ // .size is ignored for function symbols, so we can skip it
299
+ writeln ! ( end, "end_function" ) . unwrap ( ) ;
300
+ }
267
301
}
268
302
269
303
( begin, end)
270
304
}
305
+
306
+ /// The webassembly type signature for the given function.
307
+ ///
308
+ /// Used by the `.functype` directive on wasm targets.
309
+ fn wasm_functype < ' tcx > ( tcx : TyCtxt < ' tcx > , fn_abi : & FnAbi < ' tcx , Ty < ' tcx > > , def_id : DefId ) -> String {
310
+ let mut signature = String :: with_capacity ( 64 ) ;
311
+
312
+ let ptr_type = match tcx. data_layout . pointer_size . bits ( ) {
313
+ 32 => "i32" ,
314
+ 64 => "i64" ,
315
+ other => bug ! ( "wasm pointer size cannot be {other} bits" ) ,
316
+ } ;
317
+
318
+ // FIXME: remove this once the wasm32-unknown-unknown ABI is fixed
319
+ // please also add `wasm32-unknown-unknown` back in `tests/assembly/wasm32-naked-fn.rs`
320
+ // basically the commit introducing this comment should be reverted
321
+ if let PassMode :: Pair { .. } = fn_abi. ret . mode {
322
+ let _ = WasmCAbi :: Legacy ;
323
+ span_bug ! (
324
+ tcx. def_span( def_id) ,
325
+ "cannot return a pair (the wasm32-unknown-unknown ABI is broken, see https://github.com/rust-lang/rust/issues/115666"
326
+ ) ;
327
+ }
328
+
329
+ let hidden_return = matches ! ( fn_abi. ret. mode, PassMode :: Indirect { .. } ) ;
330
+
331
+ signature. push ( '(' ) ;
332
+
333
+ if hidden_return {
334
+ signature. push_str ( ptr_type) ;
335
+ if !fn_abi. args . is_empty ( ) {
336
+ signature. push_str ( ", " ) ;
337
+ }
338
+ }
339
+
340
+ let mut it = fn_abi. args . iter ( ) . peekable ( ) ;
341
+ while let Some ( arg_abi) = it. next ( ) {
342
+ wasm_type ( tcx, & mut signature, arg_abi, ptr_type, def_id) ;
343
+ if it. peek ( ) . is_some ( ) {
344
+ signature. push_str ( ", " ) ;
345
+ }
346
+ }
347
+
348
+ signature. push_str ( ") -> (" ) ;
349
+
350
+ if !hidden_return {
351
+ wasm_type ( tcx, & mut signature, & fn_abi. ret , ptr_type, def_id) ;
352
+ }
353
+
354
+ signature. push ( ')' ) ;
355
+
356
+ signature
357
+ }
358
+
359
+ fn wasm_type < ' tcx > (
360
+ tcx : TyCtxt < ' tcx > ,
361
+ signature : & mut String ,
362
+ arg_abi : & ArgAbi < ' _ , Ty < ' tcx > > ,
363
+ ptr_type : & ' static str ,
364
+ def_id : DefId ,
365
+ ) {
366
+ match arg_abi. mode {
367
+ PassMode :: Ignore => { /* do nothing */ }
368
+ PassMode :: Direct ( _) => {
369
+ let direct_type = match arg_abi. layout . backend_repr {
370
+ BackendRepr :: Scalar ( scalar) => wasm_primitive ( scalar. primitive ( ) , ptr_type) ,
371
+ BackendRepr :: Vector { .. } => "v128" ,
372
+ BackendRepr :: Memory { .. } => {
373
+ // FIXME: remove this branch once the wasm32-unknown-unknown ABI is fixed
374
+ let _ = WasmCAbi :: Legacy ;
375
+ span_bug ! (
376
+ tcx. def_span( def_id) ,
377
+ "cannot use memory args (the wasm32-unknown-unknown ABI is broken, see https://github.com/rust-lang/rust/issues/115666"
378
+ ) ;
379
+ }
380
+ other => unreachable ! ( "unexpected BackendRepr: {:?}" , other) ,
381
+ } ;
382
+
383
+ signature. push_str ( direct_type) ;
384
+ }
385
+ PassMode :: Pair ( _, _) => match arg_abi. layout . backend_repr {
386
+ BackendRepr :: ScalarPair ( a, b) => {
387
+ signature. push_str ( wasm_primitive ( a. primitive ( ) , ptr_type) ) ;
388
+ signature. push_str ( ", " ) ;
389
+ signature. push_str ( wasm_primitive ( b. primitive ( ) , ptr_type) ) ;
390
+ }
391
+ other => unreachable ! ( "{other:?}" ) ,
392
+ } ,
393
+ PassMode :: Cast { pad_i32, ref cast } => {
394
+ // For wasm, Cast is used for single-field primitive wrappers like `struct Wrapper(i64);`
395
+ assert ! ( !pad_i32, "not currently used by wasm calling convention" ) ;
396
+ assert ! ( cast. prefix[ 0 ] . is_none( ) , "no prefix" ) ;
397
+ assert_eq ! ( cast. rest. total, arg_abi. layout. size, "single item" ) ;
398
+
399
+ let wrapped_wasm_type = match cast. rest . unit . kind {
400
+ RegKind :: Integer => match cast. rest . unit . size . bytes ( ) {
401
+ ..=4 => "i32" ,
402
+ ..=8 => "i64" ,
403
+ _ => ptr_type,
404
+ } ,
405
+ RegKind :: Float => match cast. rest . unit . size . bytes ( ) {
406
+ ..=4 => "f32" ,
407
+ ..=8 => "f64" ,
408
+ _ => ptr_type,
409
+ } ,
410
+ RegKind :: Vector => "v128" ,
411
+ } ;
412
+
413
+ signature. push_str ( wrapped_wasm_type) ;
414
+ }
415
+ PassMode :: Indirect { .. } => signature. push_str ( ptr_type) ,
416
+ }
417
+ }
418
+
419
+ fn wasm_primitive ( primitive : Primitive , ptr_type : & ' static str ) -> & ' static str {
420
+ match primitive {
421
+ Primitive :: Int ( integer, _) => match integer {
422
+ Integer :: I8 | Integer :: I16 | Integer :: I32 => "i32" ,
423
+ Integer :: I64 => "i64" ,
424
+ Integer :: I128 => "i64, i64" ,
425
+ } ,
426
+ Primitive :: Float ( float) => match float {
427
+ Float :: F16 | Float :: F32 => "f32" ,
428
+ Float :: F64 => "f64" ,
429
+ Float :: F128 => "i64, i64" ,
430
+ } ,
431
+ Primitive :: Pointer ( _) => ptr_type,
432
+ }
433
+ }
0 commit comments