2021-12-13 14:40:17 -06:00
//! Codegen of `asm!` invocations.
2020-09-23 08:13:49 -05:00
2020-07-10 07:45:45 -05:00
use std ::fmt ::Write ;
2024-04-23 04:37:28 -05:00
use cranelift_codegen ::isa ::CallConv ;
2020-08-28 05:10:48 -05:00
use rustc_ast ::ast ::{ InlineAsmOptions , InlineAsmTemplatePiece } ;
2024-06-14 13:46:32 -05:00
use rustc_hir ::LangItem ;
2022-01-10 08:32:45 -06:00
use rustc_span ::sym ;
2020-07-10 07:45:45 -05:00
use rustc_target ::asm ::* ;
2023-10-09 03:52:46 -05:00
use target_lexicon ::BinaryFormat ;
use crate ::prelude ::* ;
2020-07-10 07:45:45 -05:00
2023-11-16 15:15:07 -06:00
pub ( crate ) enum CInlineAsmOperand < ' tcx > {
2023-02-09 05:38:16 -06:00
In {
reg : InlineAsmRegOrRegClass ,
2023-11-10 05:30:51 -06:00
value : Value ,
2023-02-09 05:38:16 -06:00
} ,
Out {
reg : InlineAsmRegOrRegClass ,
late : bool ,
place : Option < CPlace < ' tcx > > ,
} ,
InOut {
reg : InlineAsmRegOrRegClass ,
_late : bool ,
2023-11-10 05:30:51 -06:00
in_value : Value ,
2023-02-09 05:38:16 -06:00
out_place : Option < CPlace < ' tcx > > ,
} ,
Const {
value : String ,
} ,
Symbol {
symbol : String ,
} ,
}
2023-11-16 15:15:07 -06:00
pub ( crate ) fn codegen_inline_asm_terminator < ' tcx > (
2021-03-05 12:12:59 -06:00
fx : & mut FunctionCx < '_ , '_ , ' tcx > ,
2023-02-09 05:38:16 -06:00
span : Span ,
2020-07-10 07:45:45 -05:00
template : & [ InlineAsmTemplatePiece ] ,
operands : & [ InlineAsmOperand < ' tcx > ] ,
options : InlineAsmOptions ,
2022-08-24 11:40:58 -05:00
destination : Option < mir ::BasicBlock > ,
2020-07-10 07:45:45 -05:00
) {
2023-10-29 15:30:50 -05:00
// Used by panic_abort on Windows, but uses a syntax which only happens to work with
// asm!() by accident and breaks with the GNU assembler as well as global_asm!() for
// the LLVM backend.
2023-11-10 05:30:51 -06:00
if template . len ( ) = = 1
& & template [ 0 ] = = InlineAsmTemplatePiece ::String ( " int $$0x29 " . to_string ( ) )
{
2023-10-29 15:30:50 -05:00
fx . bcx . ins ( ) . trap ( TrapCode ::User ( 1 ) ) ;
2023-10-21 14:54:51 -05:00
return ;
2020-07-10 07:45:45 -05:00
}
2023-02-09 05:38:16 -06:00
let operands = operands
2024-01-26 12:33:45 -06:00
. iter ( )
2023-02-09 05:38:16 -06:00
. map ( | operand | match * operand {
2023-11-10 05:30:51 -06:00
InlineAsmOperand ::In { reg , ref value } = > CInlineAsmOperand ::In {
reg ,
value : crate ::base ::codegen_operand ( fx , value ) . load_scalar ( fx ) ,
} ,
2023-02-09 05:38:16 -06:00
InlineAsmOperand ::Out { reg , late , ref place } = > CInlineAsmOperand ::Out {
reg ,
late ,
place : place . map ( | place | crate ::base ::codegen_place ( fx , place ) ) ,
} ,
InlineAsmOperand ::InOut { reg , late , ref in_value , ref out_place } = > {
CInlineAsmOperand ::InOut {
reg ,
_late : late ,
2023-11-10 05:30:51 -06:00
in_value : crate ::base ::codegen_operand ( fx , in_value ) . load_scalar ( fx ) ,
2023-02-09 05:38:16 -06:00
out_place : out_place . map ( | place | crate ::base ::codegen_place ( fx , place ) ) ,
}
}
InlineAsmOperand ::Const { ref value } = > {
2023-09-11 02:52:45 -05:00
let ( const_value , ty ) = crate ::constant ::eval_mir_constant ( fx , value ) ;
2023-02-09 05:38:16 -06:00
let value = rustc_codegen_ssa ::common ::asm_const_to_str (
fx . tcx ,
span ,
const_value ,
fx . layout_of ( ty ) ,
) ;
CInlineAsmOperand ::Const { value }
}
InlineAsmOperand ::SymFn { ref value } = > {
2023-10-29 15:30:50 -05:00
if cfg! ( not ( feature = " inline_asm_sym " ) ) {
fx . tcx
2023-12-18 05:21:37 -06:00
. dcx ( )
2023-10-29 15:30:50 -05:00
. span_err ( span , " asm! and global_asm! sym operands are not yet supported " ) ;
}
2023-09-20 13:51:14 -05:00
let const_ = fx . monomorphize ( value . const_ ) ;
if let ty ::FnDef ( def_id , args ) = * const_ . ty ( ) . kind ( ) {
2023-02-09 05:38:16 -06:00
let instance = ty ::Instance ::resolve_for_fn_ptr (
fx . tcx ,
ty ::ParamEnv ::reveal_all ( ) ,
def_id ,
2023-07-11 16:35:29 -05:00
args ,
2023-02-09 05:38:16 -06:00
)
. unwrap ( ) ;
let symbol = fx . tcx . symbol_name ( instance ) ;
// Pass a wrapper rather than the function itself as the function itself may not
// be exported from the main codegen unit and may thus be unreachable from the
// object file created by an external assembler.
let inline_asm_index = fx . cx . inline_asm_index . get ( ) ;
fx . cx . inline_asm_index . set ( inline_asm_index + 1 ) ;
let wrapper_name = format! (
" __inline_asm_{}_wrapper_n{} " ,
fx . cx . cgu_name . as_str ( ) . replace ( '.' , " __ " ) . replace ( '-' , " _ " ) ,
inline_asm_index
) ;
let sig =
get_function_sig ( fx . tcx , fx . target_config . default_call_conv , instance ) ;
2024-06-28 06:50:15 -05:00
create_wrapper_function ( fx . module , sig , & wrapper_name , symbol . name ) ;
2023-02-09 05:38:16 -06:00
CInlineAsmOperand ::Symbol { symbol : wrapper_name }
} else {
span_bug! ( span , " invalid type for asm sym (fn) " ) ;
}
}
InlineAsmOperand ::SymStatic { def_id } = > {
assert! ( fx . tcx . is_static ( def_id ) ) ;
2024-05-13 08:26:33 -05:00
let instance = Instance ::mono ( fx . tcx , def_id ) ;
2023-02-09 05:38:16 -06:00
CInlineAsmOperand ::Symbol { symbol : fx . tcx . symbol_name ( instance ) . name . to_owned ( ) }
}
2023-12-26 10:07:35 -06:00
InlineAsmOperand ::Label { .. } = > {
span_bug! ( span , " asm! label operands are not yet supported " ) ;
}
2023-02-09 05:38:16 -06:00
} )
. collect ::< Vec < _ > > ( ) ;
2023-11-16 15:15:07 -06:00
codegen_inline_asm_inner ( fx , template , & operands , options ) ;
match destination {
Some ( destination ) = > {
let destination_block = fx . get_block ( destination ) ;
fx . bcx . ins ( ) . jump ( destination_block , & [ ] ) ;
}
None = > {
fx . bcx . ins ( ) . trap ( TrapCode ::UnreachableCodeReached ) ;
}
}
}
pub ( crate ) fn codegen_inline_asm_inner < ' tcx > (
fx : & mut FunctionCx < '_ , '_ , ' tcx > ,
template : & [ InlineAsmTemplatePiece ] ,
operands : & [ CInlineAsmOperand < ' tcx > ] ,
options : InlineAsmOptions ,
) {
// FIXME add .eh_frame unwind info directives
2020-07-10 07:45:45 -05:00
2021-12-20 11:56:35 -06:00
let mut asm_gen = InlineAssemblyGenerator {
tcx : fx . tcx ,
arch : fx . tcx . sess . asm_arch . unwrap ( ) ,
2022-02-17 12:16:04 -06:00
enclosing_def_id : fx . instance . def_id ( ) ,
2021-12-20 11:56:35 -06:00
template ,
2023-11-16 15:15:07 -06:00
operands ,
2021-12-20 11:56:35 -06:00
options ,
registers : Vec ::new ( ) ,
stack_slots_clobber : Vec ::new ( ) ,
stack_slots_input : Vec ::new ( ) ,
stack_slots_output : Vec ::new ( ) ,
stack_slot_size : Size ::from_bytes ( 0 ) ,
2024-05-13 08:26:33 -05:00
is_naked : false ,
2020-07-10 07:45:45 -05:00
} ;
2021-12-20 11:56:35 -06:00
asm_gen . allocate_registers ( ) ;
asm_gen . allocate_stack_slots ( ) ;
let inline_asm_index = fx . cx . inline_asm_index . get ( ) ;
fx . cx . inline_asm_index . set ( inline_asm_index + 1 ) ;
let asm_name = format! (
" __inline_asm_{}_n{} " ,
fx . cx . cgu_name . as_str ( ) . replace ( '.' , " __ " ) . replace ( '-' , " _ " ) ,
inline_asm_index
) ;
let generated_asm = asm_gen . generate_asm_wrapper ( & asm_name ) ;
fx . cx . global_asm . push_str ( & generated_asm ) ;
2020-07-10 07:45:45 -05:00
2023-11-16 15:15:07 -06:00
let mut inputs = Vec ::new ( ) ;
let mut outputs = Vec ::new ( ) ;
2021-12-20 11:56:35 -06:00
for ( i , operand ) in operands . iter ( ) . enumerate ( ) {
2023-02-09 05:38:16 -06:00
match operand {
CInlineAsmOperand ::In { reg : _ , value } = > {
2023-11-10 05:30:51 -06:00
inputs . push ( ( asm_gen . stack_slots_input [ i ] . unwrap ( ) , * value ) ) ;
2020-07-10 07:45:45 -05:00
}
2023-02-09 05:38:16 -06:00
CInlineAsmOperand ::Out { reg : _ , late : _ , place } = > {
2020-07-10 07:45:45 -05:00
if let Some ( place ) = place {
2023-03-15 09:41:48 -05:00
outputs . push ( ( asm_gen . stack_slots_output [ i ] . unwrap ( ) , * place ) ) ;
2020-07-10 07:45:45 -05:00
}
}
2023-02-09 05:38:16 -06:00
CInlineAsmOperand ::InOut { reg : _ , _late : _ , in_value , out_place } = > {
2023-11-10 05:30:51 -06:00
inputs . push ( ( asm_gen . stack_slots_input [ i ] . unwrap ( ) , * in_value ) ) ;
2020-07-10 07:45:45 -05:00
if let Some ( out_place ) = out_place {
2023-03-15 09:41:48 -05:00
outputs . push ( ( asm_gen . stack_slots_output [ i ] . unwrap ( ) , * out_place ) ) ;
2020-07-10 07:45:45 -05:00
}
}
2023-02-09 05:38:16 -06:00
CInlineAsmOperand ::Const { value : _ } | CInlineAsmOperand ::Symbol { symbol : _ } = > { }
2020-07-10 07:45:45 -05:00
}
}
2021-12-20 11:56:35 -06:00
call_inline_asm ( fx , & asm_name , asm_gen . stack_slot_size , inputs , outputs ) ;
2020-07-10 07:45:45 -05:00
}
2024-05-13 08:26:33 -05:00
pub ( crate ) fn codegen_naked_asm < ' tcx > (
tcx : TyCtxt < ' tcx > ,
cx : & mut crate ::CodegenCx ,
module : & mut dyn Module ,
instance : Instance < ' tcx > ,
span : Span ,
symbol_name : & str ,
template : & [ InlineAsmTemplatePiece ] ,
operands : & [ InlineAsmOperand < ' tcx > ] ,
options : InlineAsmOptions ,
) {
// FIXME add .eh_frame unwind info directives
let operands = operands
. iter ( )
. map ( | operand | match * operand {
InlineAsmOperand ::In { .. }
| InlineAsmOperand ::Out { .. }
| InlineAsmOperand ::InOut { .. } = > {
span_bug! ( span , " invalid operand type for naked asm " )
}
InlineAsmOperand ::Const { ref value } = > {
let cv = instance . instantiate_mir_and_normalize_erasing_regions (
tcx ,
ty ::ParamEnv ::reveal_all ( ) ,
ty ::EarlyBinder ::bind ( value . const_ ) ,
) ;
let const_value = cv
. eval ( tcx , ty ::ParamEnv ::reveal_all ( ) , value . span )
. expect ( " erroneous constant missed by mono item collection " ) ;
let value = rustc_codegen_ssa ::common ::asm_const_to_str (
tcx ,
span ,
const_value ,
RevealAllLayoutCx ( tcx ) . layout_of ( cv . ty ( ) ) ,
) ;
CInlineAsmOperand ::Const { value }
}
InlineAsmOperand ::SymFn { ref value } = > {
if cfg! ( not ( feature = " inline_asm_sym " ) ) {
tcx . dcx ( )
. span_err ( span , " asm! and global_asm! sym operands are not yet supported " ) ;
}
let const_ = instance . instantiate_mir_and_normalize_erasing_regions (
tcx ,
ty ::ParamEnv ::reveal_all ( ) ,
ty ::EarlyBinder ::bind ( value . const_ ) ,
) ;
if let ty ::FnDef ( def_id , args ) = * const_ . ty ( ) . kind ( ) {
let instance = ty ::Instance ::resolve_for_fn_ptr (
tcx ,
ty ::ParamEnv ::reveal_all ( ) ,
def_id ,
args ,
)
. unwrap ( ) ;
let symbol = tcx . symbol_name ( instance ) ;
// Pass a wrapper rather than the function itself as the function itself may not
// be exported from the main codegen unit and may thus be unreachable from the
// object file created by an external assembler.
let inline_asm_index = cx . inline_asm_index . get ( ) ;
cx . inline_asm_index . set ( inline_asm_index + 1 ) ;
let wrapper_name = format! (
" __inline_asm_{}_wrapper_n{} " ,
cx . cgu_name . as_str ( ) . replace ( '.' , " __ " ) . replace ( '-' , " _ " ) ,
inline_asm_index
) ;
let sig =
get_function_sig ( tcx , module . target_config ( ) . default_call_conv , instance ) ;
2024-06-28 06:50:15 -05:00
create_wrapper_function ( module , sig , & wrapper_name , symbol . name ) ;
2024-05-13 08:26:33 -05:00
CInlineAsmOperand ::Symbol { symbol : wrapper_name }
} else {
span_bug! ( span , " invalid type for asm sym (fn) " ) ;
}
}
InlineAsmOperand ::SymStatic { def_id } = > {
assert! ( tcx . is_static ( def_id ) ) ;
let instance = Instance ::mono ( tcx , def_id ) ;
CInlineAsmOperand ::Symbol { symbol : tcx . symbol_name ( instance ) . name . to_owned ( ) }
}
InlineAsmOperand ::Label { .. } = > {
span_bug! ( span , " asm! label operands are not yet supported " ) ;
}
} )
. collect ::< Vec < _ > > ( ) ;
let asm_gen = InlineAssemblyGenerator {
tcx ,
arch : tcx . sess . asm_arch . unwrap ( ) ,
enclosing_def_id : instance . def_id ( ) ,
template ,
operands : & operands ,
options ,
registers : Vec ::new ( ) ,
stack_slots_clobber : Vec ::new ( ) ,
stack_slots_input : Vec ::new ( ) ,
stack_slots_output : Vec ::new ( ) ,
stack_slot_size : Size ::from_bytes ( 0 ) ,
is_naked : true ,
} ;
let generated_asm = asm_gen . generate_asm_wrapper ( symbol_name ) ;
cx . global_asm . push_str ( & generated_asm ) ;
}
2021-12-20 11:56:35 -06:00
struct InlineAssemblyGenerator < ' a , ' tcx > {
tcx : TyCtxt < ' tcx > ,
2020-07-10 07:45:45 -05:00
arch : InlineAsmArch ,
2022-02-17 12:16:04 -06:00
enclosing_def_id : DefId ,
2021-12-20 11:56:35 -06:00
template : & ' a [ InlineAsmTemplatePiece ] ,
2023-02-09 05:38:16 -06:00
operands : & ' a [ CInlineAsmOperand < ' tcx > ] ,
2020-07-10 07:45:45 -05:00
options : InlineAsmOptions ,
2021-12-20 11:56:35 -06:00
registers : Vec < Option < InlineAsmReg > > ,
stack_slots_clobber : Vec < Option < Size > > ,
stack_slots_input : Vec < Option < Size > > ,
stack_slots_output : Vec < Option < Size > > ,
stack_slot_size : Size ,
2024-05-13 08:26:33 -05:00
is_naked : bool ,
2021-12-20 11:56:35 -06:00
}
impl < ' tcx > InlineAssemblyGenerator < '_ , ' tcx > {
fn allocate_registers ( & mut self ) {
2024-05-13 08:26:33 -05:00
assert! ( ! self . is_naked ) ;
2021-12-20 11:56:35 -06:00
let sess = self . tcx . sess ;
2022-02-17 08:16:52 -06:00
let map = allocatable_registers (
self . arch ,
sess . relocation_model ( ) ,
2022-02-17 12:16:04 -06:00
self . tcx . asm_target_features ( self . enclosing_def_id ) ,
2022-02-17 08:16:52 -06:00
& sess . target ,
) ;
2021-12-20 11:56:35 -06:00
let mut allocated = FxHashMap ::< _ , ( bool , bool ) > ::default ( ) ;
let mut regs = vec! [ None ; self . operands . len ( ) ] ;
// Add explicit registers to the allocated set.
for ( i , operand ) in self . operands . iter ( ) . enumerate ( ) {
match * operand {
2023-02-09 05:38:16 -06:00
CInlineAsmOperand ::In { reg : InlineAsmRegOrRegClass ::Reg ( reg ) , .. } = > {
2021-12-20 11:56:35 -06:00
regs [ i ] = Some ( reg ) ;
allocated . entry ( reg ) . or_default ( ) . 0 = true ;
}
2023-02-09 05:38:16 -06:00
CInlineAsmOperand ::Out {
reg : InlineAsmRegOrRegClass ::Reg ( reg ) ,
late : true ,
..
2021-12-20 11:56:35 -06:00
} = > {
regs [ i ] = Some ( reg ) ;
allocated . entry ( reg ) . or_default ( ) . 1 = true ;
}
2023-02-09 05:38:16 -06:00
CInlineAsmOperand ::Out { reg : InlineAsmRegOrRegClass ::Reg ( reg ) , .. }
| CInlineAsmOperand ::InOut { reg : InlineAsmRegOrRegClass ::Reg ( reg ) , .. } = > {
2021-12-20 11:56:35 -06:00
regs [ i ] = Some ( reg ) ;
allocated . insert ( reg , ( true , true ) ) ;
}
_ = > ( ) ,
}
2020-07-10 07:45:45 -05:00
}
2021-12-20 11:56:35 -06:00
// Allocate out/inout/inlateout registers first because they are more constrained.
for ( i , operand ) in self . operands . iter ( ) . enumerate ( ) {
match * operand {
2023-02-09 05:38:16 -06:00
CInlineAsmOperand ::Out {
2021-12-20 11:56:35 -06:00
reg : InlineAsmRegOrRegClass ::RegClass ( class ) ,
late : false ,
..
}
2023-02-09 05:38:16 -06:00
| CInlineAsmOperand ::InOut {
2021-12-20 11:56:35 -06:00
reg : InlineAsmRegOrRegClass ::RegClass ( class ) , ..
} = > {
let mut alloc_reg = None ;
for & reg in & map [ & class ] {
let mut used = false ;
reg . overlapping_regs ( | r | {
if allocated . contains_key ( & r ) {
used = true ;
}
} ) ;
if ! used {
alloc_reg = Some ( reg ) ;
break ;
}
}
let reg = alloc_reg . expect ( " cannot allocate registers " ) ;
regs [ i ] = Some ( reg ) ;
allocated . insert ( reg , ( true , true ) ) ;
}
_ = > ( ) ,
}
}
// Allocate in/lateout.
for ( i , operand ) in self . operands . iter ( ) . enumerate ( ) {
match * operand {
2023-02-09 05:38:16 -06:00
CInlineAsmOperand ::In { reg : InlineAsmRegOrRegClass ::RegClass ( class ) , .. } = > {
2021-12-20 11:56:35 -06:00
let mut alloc_reg = None ;
for & reg in & map [ & class ] {
let mut used = false ;
reg . overlapping_regs ( | r | {
if allocated . get ( & r ) . copied ( ) . unwrap_or_default ( ) . 0 {
used = true ;
}
} ) ;
if ! used {
alloc_reg = Some ( reg ) ;
break ;
}
}
let reg = alloc_reg . expect ( " cannot allocate registers " ) ;
regs [ i ] = Some ( reg ) ;
allocated . entry ( reg ) . or_default ( ) . 0 = true ;
}
2023-02-09 05:38:16 -06:00
CInlineAsmOperand ::Out {
2021-12-20 11:56:35 -06:00
reg : InlineAsmRegOrRegClass ::RegClass ( class ) ,
late : true ,
..
} = > {
let mut alloc_reg = None ;
for & reg in & map [ & class ] {
let mut used = false ;
reg . overlapping_regs ( | r | {
if allocated . get ( & r ) . copied ( ) . unwrap_or_default ( ) . 1 {
used = true ;
}
} ) ;
if ! used {
alloc_reg = Some ( reg ) ;
break ;
}
}
let reg = alloc_reg . expect ( " cannot allocate registers " ) ;
regs [ i ] = Some ( reg ) ;
allocated . entry ( reg ) . or_default ( ) . 1 = true ;
}
_ = > ( ) ,
}
}
2020-07-10 07:45:45 -05:00
2021-12-20 11:56:35 -06:00
self . registers = regs ;
2020-07-10 07:45:45 -05:00
}
2021-12-20 11:56:35 -06:00
fn allocate_stack_slots ( & mut self ) {
2024-05-13 08:26:33 -05:00
assert! ( ! self . is_naked ) ;
2021-12-20 11:56:35 -06:00
let mut slot_size = Size ::from_bytes ( 0 ) ;
let mut slots_clobber = vec! [ None ; self . operands . len ( ) ] ;
let mut slots_input = vec! [ None ; self . operands . len ( ) ] ;
let mut slots_output = vec! [ None ; self . operands . len ( ) ] ;
let new_slot_fn = | slot_size : & mut Size , reg_class : InlineAsmRegClass | {
let reg_size =
reg_class . supported_types ( self . arch ) . iter ( ) . map ( | ( ty , _ ) | ty . size ( ) ) . max ( ) . unwrap ( ) ;
let align = rustc_target ::abi ::Align ::from_bytes ( reg_size . bytes ( ) ) . unwrap ( ) ;
let offset = slot_size . align_to ( align ) ;
* slot_size = offset + reg_size ;
offset
} ;
let mut new_slot = | x | new_slot_fn ( & mut slot_size , x ) ;
// Allocate stack slots for saving clobbered registers
2022-02-17 12:16:04 -06:00
let abi_clobber = InlineAsmClobberAbi ::parse ( self . arch , & self . tcx . sess . target , sym ::C )
. unwrap ( )
. clobbered_regs ( ) ;
2021-12-20 11:56:35 -06:00
for ( i , reg ) in self . registers . iter ( ) . enumerate ( ) . filter_map ( | ( i , r ) | r . map ( | r | ( i , r ) ) ) {
let mut need_save = true ;
// If the register overlaps with a register clobbered by function call, then
// we don't need to save it.
for r in abi_clobber {
r . overlapping_regs ( | r | {
if r = = reg {
need_save = false ;
}
} ) ;
if ! need_save {
break ;
}
}
if need_save {
slots_clobber [ i ] = Some ( new_slot ( reg . reg_class ( ) ) ) ;
2020-07-10 07:45:45 -05:00
}
}
2021-12-20 11:56:35 -06:00
// Allocate stack slots for inout
for ( i , operand ) in self . operands . iter ( ) . enumerate ( ) {
match * operand {
2023-02-09 05:38:16 -06:00
CInlineAsmOperand ::InOut { reg , out_place : Some ( _ ) , .. } = > {
2021-12-20 11:56:35 -06:00
let slot = new_slot ( reg . reg_class ( ) ) ;
slots_input [ i ] = Some ( slot ) ;
slots_output [ i ] = Some ( slot ) ;
}
_ = > ( ) ,
}
}
let slot_size_before_input = slot_size ;
let mut new_slot = | x | new_slot_fn ( & mut slot_size , x ) ;
// Allocate stack slots for input
for ( i , operand ) in self . operands . iter ( ) . enumerate ( ) {
match * operand {
2023-02-09 05:38:16 -06:00
CInlineAsmOperand ::In { reg , .. }
| CInlineAsmOperand ::InOut { reg , out_place : None , .. } = > {
2021-12-20 11:56:35 -06:00
slots_input [ i ] = Some ( new_slot ( reg . reg_class ( ) ) ) ;
}
_ = > ( ) ,
}
}
// Reset slot size to before input so that input and output operands can overlap
// and save some memory.
let slot_size_after_input = slot_size ;
slot_size = slot_size_before_input ;
let mut new_slot = | x | new_slot_fn ( & mut slot_size , x ) ;
// Allocate stack slots for output
for ( i , operand ) in self . operands . iter ( ) . enumerate ( ) {
match * operand {
2023-02-09 05:38:16 -06:00
CInlineAsmOperand ::Out { reg , place : Some ( _ ) , .. } = > {
2021-12-20 11:56:35 -06:00
slots_output [ i ] = Some ( new_slot ( reg . reg_class ( ) ) ) ;
}
_ = > ( ) ,
}
}
slot_size = slot_size . max ( slot_size_after_input ) ;
self . stack_slots_clobber = slots_clobber ;
self . stack_slots_input = slots_input ;
self . stack_slots_output = slots_output ;
self . stack_slot_size = slot_size ;
2020-07-10 07:45:45 -05:00
}
2021-12-20 11:56:35 -06:00
fn generate_asm_wrapper ( & self , asm_name : & str ) -> String {
2023-10-09 03:52:46 -05:00
let binary_format = crate ::target_triple ( self . tcx . sess ) . binary_format ;
2021-12-20 11:56:35 -06:00
let mut generated_asm = String ::new ( ) ;
2023-10-09 03:52:46 -05:00
match binary_format {
BinaryFormat ::Elf = > {
writeln! ( generated_asm , " .globl {} " , asm_name ) . unwrap ( ) ;
writeln! ( generated_asm , " .type {},@function " , asm_name ) . unwrap ( ) ;
writeln! ( generated_asm , " .section .text.{}, \" ax \" ,@progbits " , asm_name ) . unwrap ( ) ;
writeln! ( generated_asm , " {}: " , asm_name ) . unwrap ( ) ;
}
BinaryFormat ::Macho = > {
writeln! ( generated_asm , " .globl _{} " , asm_name ) . unwrap ( ) ;
writeln! ( generated_asm , " _{}: " , asm_name ) . unwrap ( ) ;
}
BinaryFormat ::Coff = > {
writeln! ( generated_asm , " .globl {} " , asm_name ) . unwrap ( ) ;
writeln! ( generated_asm , " {}: " , asm_name ) . unwrap ( ) ;
}
_ = > self
. tcx
2023-12-18 05:21:37 -06:00
. dcx ( )
2023-10-09 03:52:46 -05:00
. fatal ( format! ( " Unsupported binary format for inline asm: {binary_format:?} " ) ) ,
}
2021-12-20 11:56:35 -06:00
let is_x86 = matches! ( self . arch , InlineAsmArch ::X86 | InlineAsmArch ::X86_64 ) ;
if is_x86 {
generated_asm . push_str ( " .intel_syntax noprefix \n " ) ;
}
2024-05-13 08:26:33 -05:00
if ! self . is_naked {
Self ::prologue ( & mut generated_asm , self . arch ) ;
// Save clobbered registers
if ! self . options . contains ( InlineAsmOptions ::NORETURN ) {
for ( reg , slot ) in self
. registers
. iter ( )
. zip ( self . stack_slots_clobber . iter ( ) . copied ( ) )
. filter_map ( | ( r , s ) | r . zip ( s ) )
{
Self ::save_register ( & mut generated_asm , self . arch , reg , slot ) ;
}
}
2021-12-20 11:56:35 -06:00
2024-05-13 08:26:33 -05:00
// Write input registers
2021-12-20 11:56:35 -06:00
for ( reg , slot ) in self
. registers
. iter ( )
2024-05-13 08:26:33 -05:00
. zip ( self . stack_slots_input . iter ( ) . copied ( ) )
2021-12-20 11:56:35 -06:00
. filter_map ( | ( r , s ) | r . zip ( s ) )
{
2024-05-13 08:26:33 -05:00
Self ::restore_register ( & mut generated_asm , self . arch , reg , slot ) ;
2021-12-20 11:56:35 -06:00
}
}
if is_x86 & & self . options . contains ( InlineAsmOptions ::ATT_SYNTAX ) {
generated_asm . push_str ( " .att_syntax \n " ) ;
}
// The actual inline asm
for piece in self . template {
match piece {
InlineAsmTemplatePiece ::String ( s ) = > {
generated_asm . push_str ( s ) ;
}
InlineAsmTemplatePiece ::Placeholder { operand_idx , modifier , span : _ } = > {
2023-02-09 05:38:16 -06:00
match self . operands [ * operand_idx ] {
CInlineAsmOperand ::In { .. }
| CInlineAsmOperand ::Out { .. }
| CInlineAsmOperand ::InOut { .. } = > {
if self . options . contains ( InlineAsmOptions ::ATT_SYNTAX ) {
generated_asm . push ( '%' ) ;
}
2024-01-26 12:33:45 -06:00
let reg = self . registers [ * operand_idx ] . unwrap ( ) ;
match self . arch {
InlineAsmArch ::X86_64 = > match reg {
InlineAsmReg ::X86 ( reg )
if reg as u32 > = X86InlineAsmReg ::xmm0 as u32
& & reg as u32 < = X86InlineAsmReg ::xmm15 as u32 = >
{
// rustc emits x0 rather than xmm0
let class = match * modifier {
None | Some ( 'x' ) = > " xmm " ,
Some ( 'y' ) = > " ymm " ,
Some ( 'z' ) = > " zmm " ,
_ = > unreachable! ( ) ,
} ;
write! (
generated_asm ,
" {class}{} " ,
reg as u32 - X86InlineAsmReg ::xmm0 as u32
)
. unwrap ( ) ;
}
_ = > reg
. emit ( & mut generated_asm , InlineAsmArch ::X86_64 , * modifier )
. unwrap ( ) ,
} ,
_ = > reg . emit ( & mut generated_asm , self . arch , * modifier ) . unwrap ( ) ,
}
2023-02-09 05:38:16 -06:00
}
CInlineAsmOperand ::Const { ref value } = > {
generated_asm . push_str ( value ) ;
}
CInlineAsmOperand ::Symbol { ref symbol } = > generated_asm . push_str ( symbol ) ,
2021-12-20 11:56:35 -06:00
}
}
}
}
generated_asm . push ( '\n' ) ;
if is_x86 & & self . options . contains ( InlineAsmOptions ::ATT_SYNTAX ) {
generated_asm . push_str ( " .intel_syntax noprefix \n " ) ;
}
2024-05-13 08:26:33 -05:00
if ! self . is_naked {
if ! self . options . contains ( InlineAsmOptions ::NORETURN ) {
// Read output registers
for ( reg , slot ) in self
. registers
. iter ( )
. zip ( self . stack_slots_output . iter ( ) . copied ( ) )
. filter_map ( | ( r , s ) | r . zip ( s ) )
{
Self ::save_register ( & mut generated_asm , self . arch , reg , slot ) ;
}
2021-12-20 11:56:35 -06:00
2024-05-13 08:26:33 -05:00
// Restore clobbered registers
for ( reg , slot ) in self
. registers
. iter ( )
. zip ( self . stack_slots_clobber . iter ( ) . copied ( ) )
. filter_map ( | ( r , s ) | r . zip ( s ) )
{
Self ::restore_register ( & mut generated_asm , self . arch , reg , slot ) ;
}
2021-12-20 11:56:35 -06:00
2024-05-13 08:26:33 -05:00
Self ::epilogue ( & mut generated_asm , self . arch ) ;
} else {
Self ::epilogue_noreturn ( & mut generated_asm , self . arch ) ;
}
2021-12-20 11:56:35 -06:00
}
if is_x86 {
generated_asm . push_str ( " .att_syntax \n " ) ;
}
2023-10-09 03:52:46 -05:00
match binary_format {
BinaryFormat ::Elf = > {
writeln! ( generated_asm , " .size {name}, .-{name} " , name = asm_name ) . unwrap ( ) ;
generated_asm . push_str ( " .text \n " ) ;
}
BinaryFormat ::Macho | BinaryFormat ::Coff = > { }
_ = > self
. tcx
2023-12-18 05:21:37 -06:00
. dcx ( )
2023-10-09 03:52:46 -05:00
. fatal ( format! ( " Unsupported binary format for inline asm: {binary_format:?} " ) ) ,
}
2021-12-20 11:56:35 -06:00
generated_asm . push_str ( " \n \n " ) ;
generated_asm
2020-07-10 07:45:45 -05:00
}
2021-12-20 11:56:35 -06:00
fn prologue ( generated_asm : & mut String , arch : InlineAsmArch ) {
match arch {
InlineAsmArch ::X86_64 = > {
generated_asm . push_str ( " push rbp \n " ) ;
2023-10-09 03:52:46 -05:00
generated_asm . push_str ( " mov rbp,rsp \n " ) ;
generated_asm . push_str ( " push rbx \n " ) ; // rbx is callee saved
// rbx is reserved by LLVM for the "base pointer", so rustc doesn't allow using it
generated_asm . push_str ( " mov rbx,rdi \n " ) ;
}
InlineAsmArch ::AArch64 = > {
generated_asm . push_str ( " stp fp, lr, [sp, #-32]! \n " ) ;
generated_asm . push_str ( " mov fp, sp \n " ) ;
generated_asm . push_str ( " str x19, [sp, #24] \n " ) ; // x19 is callee saved
// x19 is reserved by LLVM for the "base pointer", so rustc doesn't allow using it
generated_asm . push_str ( " mov x19, x0 \n " ) ;
2021-12-20 11:56:35 -06:00
}
2023-10-21 14:54:51 -05:00
InlineAsmArch ::RiscV64 = > {
generated_asm . push_str ( " addi sp, sp, -16 \n " ) ;
generated_asm . push_str ( " sd ra, 8(sp) \n " ) ;
generated_asm . push_str ( " sd s1, 0(sp) \n " ) ; // s1 is callee saved
// s1/x9 is reserved by LLVM for the "base pointer", so rustc doesn't allow using it
generated_asm . push_str ( " mv s1, a0 \n " ) ;
}
2021-12-20 11:56:35 -06:00
_ = > unimplemented! ( " prologue for {:?} " , arch ) ,
2020-07-10 07:45:45 -05:00
}
2021-12-20 11:56:35 -06:00
}
2020-07-10 07:45:45 -05:00
2021-12-20 11:56:35 -06:00
fn epilogue ( generated_asm : & mut String , arch : InlineAsmArch ) {
match arch {
InlineAsmArch ::X86_64 = > {
2023-10-09 03:52:46 -05:00
generated_asm . push_str ( " pop rbx \n " ) ;
2021-12-20 11:56:35 -06:00
generated_asm . push_str ( " pop rbp \n " ) ;
generated_asm . push_str ( " ret \n " ) ;
}
2023-10-09 03:52:46 -05:00
InlineAsmArch ::AArch64 = > {
generated_asm . push_str ( " ldr x19, [sp, #24] \n " ) ;
generated_asm . push_str ( " ldp fp, lr, [sp], #32 \n " ) ;
2021-12-20 11:56:35 -06:00
generated_asm . push_str ( " ret \n " ) ;
}
2023-10-21 14:54:51 -05:00
InlineAsmArch ::RiscV64 = > {
generated_asm . push_str ( " ld s1, 0(sp) \n " ) ;
generated_asm . push_str ( " ld ra, 8(sp) \n " ) ;
generated_asm . push_str ( " addi sp, sp, 16 \n " ) ;
generated_asm . push_str ( " ret \n " ) ;
}
2021-12-20 11:56:35 -06:00
_ = > unimplemented! ( " epilogue for {:?} " , arch ) ,
2020-07-10 07:45:45 -05:00
}
2021-12-20 11:56:35 -06:00
}
2020-07-10 07:45:45 -05:00
2021-12-20 11:56:35 -06:00
fn epilogue_noreturn ( generated_asm : & mut String , arch : InlineAsmArch ) {
match arch {
2023-10-09 03:52:46 -05:00
InlineAsmArch ::X86_64 = > {
2021-12-20 11:56:35 -06:00
generated_asm . push_str ( " ud2 \n " ) ;
}
2023-10-09 03:52:46 -05:00
InlineAsmArch ::AArch64 = > {
2023-10-21 14:54:51 -05:00
generated_asm . push_str ( " brk #0x1 \n " ) ;
}
InlineAsmArch ::RiscV64 = > {
generated_asm . push_str ( " ebreak \n " ) ;
2021-12-20 11:56:35 -06:00
}
_ = > unimplemented! ( " epilogue_noreturn for {:?} " , arch ) ,
}
2020-07-10 07:45:45 -05:00
}
2021-12-20 11:56:35 -06:00
fn save_register (
generated_asm : & mut String ,
arch : InlineAsmArch ,
reg : InlineAsmReg ,
offset : Size ,
) {
match arch {
InlineAsmArch ::X86_64 = > {
2023-11-16 15:15:07 -06:00
match reg {
InlineAsmReg ::X86 ( reg )
if reg as u32 > = X86InlineAsmReg ::xmm0 as u32
& & reg as u32 < = X86InlineAsmReg ::xmm15 as u32 = >
{
// rustc emits x0 rather than xmm0
write! ( generated_asm , " movups [rbx+0x{:x}], " , offset . bytes ( ) ) . unwrap ( ) ;
write! ( generated_asm , " xmm{} " , reg as u32 - X86InlineAsmReg ::xmm0 as u32 )
. unwrap ( ) ;
}
_ = > {
write! ( generated_asm , " mov [rbx+0x{:x}], " , offset . bytes ( ) ) . unwrap ( ) ;
reg . emit ( generated_asm , InlineAsmArch ::X86_64 , None ) . unwrap ( ) ;
}
}
2021-12-20 11:56:35 -06:00
generated_asm . push ( '\n' ) ;
}
2023-10-09 03:52:46 -05:00
InlineAsmArch ::AArch64 = > {
generated_asm . push_str ( " str " ) ;
reg . emit ( generated_asm , InlineAsmArch ::AArch64 , None ) . unwrap ( ) ;
writeln! ( generated_asm , " , [x19, 0x{:x}] " , offset . bytes ( ) ) . unwrap ( ) ;
2021-12-20 11:56:35 -06:00
}
2023-10-21 14:54:51 -05:00
InlineAsmArch ::RiscV64 = > {
generated_asm . push_str ( " sd " ) ;
reg . emit ( generated_asm , InlineAsmArch ::RiscV64 , None ) . unwrap ( ) ;
writeln! ( generated_asm , " , 0x{:x}(s1) " , offset . bytes ( ) ) . unwrap ( ) ;
}
2021-12-20 11:56:35 -06:00
_ = > unimplemented! ( " save_register for {:?} " , arch ) ,
}
}
2020-07-10 07:45:45 -05:00
2021-12-20 11:56:35 -06:00
fn restore_register (
generated_asm : & mut String ,
arch : InlineAsmArch ,
reg : InlineAsmReg ,
offset : Size ,
) {
match arch {
InlineAsmArch ::X86_64 = > {
2023-11-16 15:15:07 -06:00
match reg {
InlineAsmReg ::X86 ( reg )
if reg as u32 > = X86InlineAsmReg ::xmm0 as u32
& & reg as u32 < = X86InlineAsmReg ::xmm15 as u32 = >
{
// rustc emits x0 rather than xmm0
write! (
generated_asm ,
" movups xmm{} " ,
reg as u32 - X86InlineAsmReg ::xmm0 as u32
)
. unwrap ( ) ;
}
_ = > {
generated_asm . push_str ( " mov " ) ;
reg . emit ( generated_asm , InlineAsmArch ::X86_64 , None ) . unwrap ( )
}
}
2023-10-09 03:52:46 -05:00
writeln! ( generated_asm , " , [rbx+0x{:x}] " , offset . bytes ( ) ) . unwrap ( ) ;
2021-12-20 11:56:35 -06:00
}
2023-10-09 03:52:46 -05:00
InlineAsmArch ::AArch64 = > {
generated_asm . push_str ( " ldr " ) ;
reg . emit ( generated_asm , InlineAsmArch ::AArch64 , None ) . unwrap ( ) ;
writeln! ( generated_asm , " , [x19, 0x{:x}] " , offset . bytes ( ) ) . unwrap ( ) ;
2021-12-20 11:56:35 -06:00
}
2023-10-21 14:54:51 -05:00
InlineAsmArch ::RiscV64 = > {
generated_asm . push_str ( " ld " ) ;
reg . emit ( generated_asm , InlineAsmArch ::RiscV64 , None ) . unwrap ( ) ;
writeln! ( generated_asm , " , 0x{:x}(s1) " , offset . bytes ( ) ) . unwrap ( ) ;
}
2021-12-20 11:56:35 -06:00
_ = > unimplemented! ( " restore_register for {:?} " , arch ) ,
}
}
2020-07-10 07:45:45 -05:00
}
fn call_inline_asm < ' tcx > (
2021-03-05 12:12:59 -06:00
fx : & mut FunctionCx < '_ , '_ , ' tcx > ,
2020-07-10 07:45:45 -05:00
asm_name : & str ,
slot_size : Size ,
2021-12-20 11:56:35 -06:00
inputs : Vec < ( Size , Value ) > ,
outputs : Vec < ( Size , CPlace < ' tcx > ) > ,
2020-07-10 07:45:45 -05:00
) {
2023-10-24 07:22:23 -05:00
let stack_slot = fx . create_stack_slot ( u32 ::try_from ( slot_size . bytes ( ) ) . unwrap ( ) , 16 ) ;
2020-07-10 07:45:45 -05:00
2020-08-28 05:10:48 -05:00
let inline_asm_func = fx
. module
. declare_function (
asm_name ,
Linkage ::Import ,
& Signature {
call_conv : CallConv ::SystemV ,
params : vec ! [ AbiParam ::new ( fx . pointer_type ) ] ,
returns : vec ! [ ] ,
} ,
)
. unwrap ( ) ;
2024-01-26 12:33:45 -06:00
let inline_asm_func = fx . module . declare_func_in_func ( inline_asm_func , fx . bcx . func ) ;
2021-03-29 03:45:09 -05:00
if fx . clif_comments . enabled ( ) {
fx . add_comment ( inline_asm_func , asm_name ) ;
}
2020-07-10 07:45:45 -05:00
2021-12-20 11:56:35 -06:00
for ( offset , value ) in inputs {
2023-10-24 07:22:23 -05:00
stack_slot . offset ( fx , i32 ::try_from ( offset . bytes ( ) ) . unwrap ( ) . into ( ) ) . store (
fx ,
value ,
MemFlags ::trusted ( ) ,
) ;
2020-07-10 07:45:45 -05:00
}
2023-10-24 07:22:23 -05:00
let stack_slot_addr = stack_slot . get_addr ( fx ) ;
2020-07-10 07:45:45 -05:00
fx . bcx . ins ( ) . call ( inline_asm_func , & [ stack_slot_addr ] ) ;
2021-12-20 11:56:35 -06:00
for ( offset , place ) in outputs {
2023-11-16 15:15:07 -06:00
let ty = if place . layout ( ) . ty . is_simd ( ) {
let ( lane_count , lane_type ) = place . layout ( ) . ty . simd_size_and_type ( fx . tcx ) ;
2024-04-23 04:37:28 -05:00
asm_clif_type ( fx , lane_type ) . unwrap ( ) . by ( lane_count . try_into ( ) . unwrap ( ) ) . unwrap ( )
2023-11-16 15:15:07 -06:00
} else {
2024-04-23 04:37:28 -05:00
asm_clif_type ( fx , place . layout ( ) . ty ) . unwrap ( )
2023-11-16 15:15:07 -06:00
} ;
2023-10-24 07:22:23 -05:00
let value = stack_slot . offset ( fx , i32 ::try_from ( offset . bytes ( ) ) . unwrap ( ) . into ( ) ) . load (
fx ,
ty ,
MemFlags ::trusted ( ) ,
) ;
2020-07-10 07:45:45 -05:00
place . write_cvalue ( fx , CValue ::by_val ( value , place . layout ( ) ) ) ;
}
}
2024-04-23 04:37:28 -05:00
fn asm_clif_type < ' tcx > ( fx : & FunctionCx < '_ , '_ , ' tcx > , ty : Ty < ' tcx > ) -> Option < types ::Type > {
match ty . kind ( ) {
// Adapted from https://github.com/rust-lang/rust/blob/f3c66088610c1b80110297c2d9a8b5f9265b013f/compiler/rustc_hir_analysis/src/check/intrinsicck.rs#L136-L151
2024-06-14 13:46:32 -05:00
ty ::Adt ( adt , args ) if fx . tcx . is_lang_item ( adt . did ( ) , LangItem ::MaybeUninit ) = > {
2024-04-23 04:37:28 -05:00
let fields = & adt . non_enum_variant ( ) . fields ;
let ty = fields [ FieldIdx ::from_u32 ( 1 ) ] . ty ( fx . tcx , args ) ;
let ty ::Adt ( ty , args ) = ty . kind ( ) else {
unreachable! ( " expected first field of `MaybeUninit` to be an ADT " )
} ;
assert! (
ty . is_manually_drop ( ) ,
" expected first field of `MaybeUninit` to be `ManuallyDrop` "
) ;
let fields = & ty . non_enum_variant ( ) . fields ;
let ty = fields [ FieldIdx ::ZERO ] . ty ( fx . tcx , args ) ;
fx . clif_type ( ty )
}
_ = > fx . clif_type ( ty ) ,
}
}