1+ use rustc_abi:: { BackendRepr , Float , Integer , Primitive , RegKind } ;
12use rustc_attr_parsing:: InstructionSetAttr ;
3+ use rustc_hir:: def_id:: DefId ;
24use rustc_middle:: mir:: mono:: { Linkage , MonoItem , MonoItemData , Visibility } ;
35use 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} ;
79use rustc_span:: sym;
10+ use rustc_target:: callconv:: { ArgAbi , FnAbi , PassMode } ;
11+ use rustc_target:: spec:: WasmCAbi ;
812
913use crate :: common;
1014use crate :: traits:: { AsmCodegenMethods , BuilderMethods , GlobalAsmOperandRef , MiscCodegenMethods } ;
@@ -32,7 +36,8 @@ pub(crate) fn codegen_naked_asm<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
3236
3337 let item_data = cx. codegen_unit ( ) . items ( ) . get ( & MonoItem :: Fn ( instance) ) . unwrap ( ) ;
3438 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) ;
3641
3742 let mut template_vec = Vec :: new ( ) ;
3843 template_vec. push ( rustc_ast:: ast:: InlineAsmTemplatePiece :: String ( begin. into ( ) ) ) ;
@@ -103,6 +108,7 @@ enum AsmBinaryFormat {
103108 Elf ,
104109 Macho ,
105110 Coff ,
111+ Wasm ,
106112}
107113
108114impl AsmBinaryFormat {
@@ -111,6 +117,8 @@ impl AsmBinaryFormat {
111117 Self :: Coff
112118 } else if target. is_like_osx {
113119 Self :: Macho
120+ } else if target. is_like_wasm {
121+ Self :: Wasm
114122 } else {
115123 Self :: Elf
116124 }
@@ -122,6 +130,7 @@ fn prefix_and_suffix<'tcx>(
122130 instance : Instance < ' tcx > ,
123131 asm_name : & str ,
124132 item_data : & MonoItemData ,
133+ fn_abi : & FnAbi < ' tcx , Ty < ' tcx > > ,
125134) -> ( String , String ) {
126135 use std:: fmt:: Write ;
127136
@@ -169,7 +178,7 @@ fn prefix_and_suffix<'tcx>(
169178 }
170179 Linkage :: LinkOnceAny | Linkage :: LinkOnceODR | Linkage :: WeakAny | Linkage :: WeakODR => {
171180 match asm_binary_format {
172- AsmBinaryFormat :: Elf | AsmBinaryFormat :: Coff => {
181+ AsmBinaryFormat :: Elf | AsmBinaryFormat :: Coff | AsmBinaryFormat :: Wasm => {
173182 writeln ! ( w, ".weak {asm_name}" ) ?;
174183 }
175184 AsmBinaryFormat :: Macho => {
@@ -264,7 +273,161 @@ fn prefix_and_suffix<'tcx>(
264273 writeln ! ( end, "{}" , arch_suffix) . unwrap ( ) ;
265274 }
266275 }
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+ }
267301 }
268302
269303 ( begin, end)
270304}
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