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