fn emit_precall_convention(
        &mut self,
        reg_convention: RegisterCallConvention,
        stack_convention: StackCallConvention,
        is_tail: bool,
        args: &Vec<P<Value>>,
        arg_tys: &Vec<P<MuType>>,
        return_size: usize,
        f_context: &mut FunctionContext,
        vm: &VM
    ) -> (usize, Vec<P<Value>>) {
        // If we're tail calling, use the current frame's argument location instead
        let mut arg_regs = Vec::<P<Value>>::new();
        let (_, locations, stack_size) = compute_argument_locations(
            &arg_tys,
            stack_convention.base(),
            stack_convention.offset() as i64,
            reg_convention.is_callee_saved(),
            &vm
        );

        if is_tail {
            if stack_size > self.current_stack_arg_size {
                unimplemented!();
            } else {
                if return_size > 0 {
                    // Restore XR
                    let xr_value = self.current_xr_value.as_ref().unwrap().clone();
                    self.backend.emit_mov(&XR, &xr_value);
                }
            }
        } else {
            if return_size > 0 {
                // Reserve space on the stack for the return value
                emit_sub_u64(self.backend.as_mut(), &SP, &SP, return_size as u64);

                // XR needs to point to where the callee should return arguments
                self.backend.emit_mov(&XR, &SP);
                arg_regs.push(XR.clone());
            }
        }
        if stack_size > 0 && stack_convention.push() {
            // Reserve space on the stack for all stack arguments
            emit_sub_u64(self.backend.as_mut(), &SP, &SP, stack_size as u64);
        }

        // Write the arguments to where they belong on the stack
        for i in 0..args.len() {
            let i = i as usize;
            let ref arg_val = args[i];
            let ref arg_loc = locations[i];
            match arg_val.ty.v {
                MuType_::Hybrid(_) => panic!("hybrid argument not supported"),

                MuType_::Vector(_, _) => unimplemented!(),

                MuType_::Struct(_) | MuType_::Array(_, _) => {
                    unimplemented!(); // Todo (note: these may be passed as IRef's)
                }

                MuType_::Void => panic!("void argument not supported"),

                // Everything else is simple
                _ => {
                    if arg_loc.is_reg() {
                        arg_regs.push(arg_loc.clone());
                    }

                    // Need to pass in two registers
                    if is_int_ex_reg(&arg_val) && arg_loc.is_reg() && reg_convention.is_some() {
                        let arg_val =
                            emit_reg_value(self.backend.as_mut(), &arg_val, f_context, vm);
                        let (val_l, val_h) = split_int128(&arg_val, f_context, vm);

                        let arg_loc_h_id = arg_loc.id() + 2;
                        let (arg_loc_l, arg_loc_h) = match reg_convention {
                            RegisterCallConvention::Memory(ref b) => (
                                make_value_base_offset(
                                    b,
                                    get_argument_reg_offset(arg_loc.id()) as i64,
                                    &arg_loc.ty,
                                    vm
                                ),
                                make_value_base_offset(
                                    b,
                                    get_argument_reg_offset(arg_loc_h_id) as i64,
                                    &arg_loc.ty,
                                    vm
                                )
                            ),
                            _ => (arg_loc.clone(), get_register_from_id(arg_loc_h_id))
                        };

                        arg_regs.push(arg_loc_h.clone());

                        emit_move_value_to_value(
                            self.backend.as_mut(),
                            &arg_loc_l,
                            &val_l,
                            f_context,
                            vm
                        );
                        emit_move_value_to_value(
                            self.backend.as_mut(),
                            &arg_loc_h,
                            &val_h,
                            f_context,
                            vm
                        );
                    } else {
                        if (reg_convention.is_some() && arg_loc.is_reg()) ||
                            (stack_convention.is_some() && !arg_loc.is_reg())
                        {
                            let arg_loc = if arg_loc.is_reg() {
                                match reg_convention {
                                    RegisterCallConvention::Memory(ref b) => {
                                        make_value_base_offset(
                                            b,
                                            get_argument_reg_offset(arg_loc.id()) as i64,
                                            &arg_loc.ty,
                                            vm
                                        )
                                    }
                                    _ => arg_loc.clone()
                                }
                            } else {
                                arg_loc.clone()
                            };

                            emit_move_value_to_value(
                                self.backend.as_mut(),
                                &arg_loc,
                                &arg_val,
                                f_context,
                                vm
                            )
                        }
                    }
                }
            }
        }

        (stack_size, arg_regs)
    }
// returns the stack arg offset - we will need this to collapse stack after the call
// as well as a list of argument registers
returns the stack arg offset - we will need this to collapse stack after the call
as well as a list of argument registers
returns the stack arg offset - we will need this to collapse stack after the call as well as a list of argument registers
fn emit_precall_convention(
        &mut self,
        reg_convention: RegisterCallConvention,
        stack_convention: StackCallConvention,
        is_tail: bool,
        args: &Vec<P<Value>>,
        arg_tys: &Vec<P<MuType>>,
        return_size: usize,
        f_context: &mut FunctionContext,
        vm: &VM
    ) -> (usize, Vec<P<Value>>) {
        // If we're tail calling, use the current frame's argument location instead
        let mut arg_regs = Vec::<P<Value>>::new();
        let (_, locations, stack_size) = compute_argument_locations(
            &arg_tys,
            stack_convention.base(),
            stack_convention.offset() as i64,
            reg_convention.is_callee_saved(),
            &vm
        );
        if is_tail {
            if stack_size > self.current_stack_arg_size {
                unimplemented!();
            } else {
                if return_size > 0 {
                    // Restore XR
                    let xr_value = self.current_xr_value.as_ref().unwrap().clone();
                    self.backend.emit_mov(&XR, &xr_value);
                }
            }
        } else {
            if return_size > 0 {
                // Reserve space on the stack for the return value
                emit_sub_u64(self.backend.as_mut(), &SP, &SP, return_size as u64);
                // XR needs to point to where the callee should return arguments
                self.backend.emit_mov(&XR, &SP);
                arg_regs.push(XR.clone());
            }
        }
        if stack_size > 0 && stack_convention.push() {
            // Reserve space on the stack for all stack arguments
            emit_sub_u64(self.backend.as_mut(), &SP, &SP, stack_size as u64);
        }
        // Write the arguments to where they belong on the stack
        for i in 0..args.len() {
            let i = i as usize;
            let ref arg_val = args[i];
            let ref arg_loc = locations[i];
            match arg_val.ty.v {
                MuType_::Hybrid(_) => panic!("hybrid argument not supported"),
                MuType_::Vector(_, _) => unimplemented!(),
                MuType_::Struct(_) | MuType_::Array(_, _) => {
                    unimplemented!(); // Todo (note: these may be passed as IRef's)
                }
                MuType_::Void => panic!("void argument not supported"),
                // Everything else is simple
                _ => {
                    if arg_loc.is_reg() {
                        arg_regs.push(arg_loc.clone());
                    }
                    // Need to pass in two registers
                    if is_int_ex_reg(&arg_val) && arg_loc.is_reg() && reg_convention.is_some() {
                        let arg_val =
                            emit_reg_value(self.backend.as_mut(), &arg_val, f_context, vm);
                        let (val_l, val_h) = split_int128(&arg_val, f_context, vm);
                        let arg_loc_h_id = arg_loc.id() + 2;
                        let (arg_loc_l, arg_loc_h) = match reg_convention {
                            RegisterCallConvention::Memory(ref b) => (
                                make_value_base_offset(
                                    b,
                                    get_argument_reg_offset(arg_loc.id()) as i64,
                                    &arg_loc.ty,
                                    vm
                                ),
                                make_value_base_offset(
                                    b,
                                    get_argument_reg_offset(arg_loc_h_id) as i64,
                                    &arg_loc.ty,
                                    vm
                                )
                            ),
                            _ => (arg_loc.clone(), get_register_from_id(arg_loc_h_id))
                        };
                        arg_regs.push(arg_loc_h.clone());
                        emit_move_value_to_value(
                            self.backend.as_mut(),
                            &arg_loc_l,
                            &val_l,
                            f_context,
                            vm
                        );
                        emit_move_value_to_value(
                            self.backend.as_mut(),
                            &arg_loc_h,
                            &val_h,
                            f_context,
                            vm
                        );
                    } else {
                        if (reg_convention.is_some() && arg_loc.is_reg()) ||
                            (stack_convention.is_some() && !arg_loc.is_reg())
                        {
                            let arg_loc = if arg_loc.is_reg() {
                                match reg_convention {
                                    RegisterCallConvention::Memory(ref b) => {
                                        make_value_base_offset(
                                            b,
                                            get_argument_reg_offset(arg_loc.id()) as i64,
                                            &arg_loc.ty,
                                            vm
                                        )
                                    }
                                    _ => arg_loc.clone()
                                }
                            } else {
                                arg_loc.clone()
                            };
                            emit_move_value_to_value(
                                self.backend.as_mut(),
                                &arg_loc,
                                &arg_val,
                                f_context,
                                vm
                            )
                        }
                    }
                }
            }
        }
        (stack_size, arg_regs)
    }
fn emit_precall_convention ( & mut self , reg_convention : RegisterCallConvention , stack_convention : StackCallConvention , is_tail : bool , args : & Vec < P < Value > > , arg_tys : & Vec < P < MuType > > , return_size : usize , f_context : & mut FunctionContext , vm : & VM ) -> ( usize , Vec < P < Value > > ) { // If we're tail calling, use the current frame's argument location instead let mut arg_regs = Vec :: < P < Value > > :: new ( ) ; let ( _ , locations , stack_size ) = compute_argument_locations ( & arg_tys , stack_convention . base ( ) , stack_convention . offset ( ) as i64 , reg_convention . is_callee_saved ( ) , & vm ) ; if is_tail { if stack_size > self . current_stack_arg_size { unimplemented ! ( ) ; } else { if return_size > 0 { // Restore XR let xr_value = self . current_xr_value . as_ref ( ) . unwrap ( ) . clone ( ) ; self . backend . emit_mov ( & XR , & xr_value ) ; } } } else { if return_size > 0 { // Reserve space on the stack for the return value emit_sub_u64 ( self . backend . as_mut ( ) , & SP , & SP , return_size as u64 ) ; // XR needs to point to where the callee should return arguments self . backend . emit_mov ( & XR , & SP ) ; arg_regs . push ( XR . clone ( ) ) ; } } if stack_size > 0 && stack_convention . push ( ) { // Reserve space on the stack for all stack arguments emit_sub_u64 ( self . backend . as_mut ( ) , & SP , & SP , stack_size as u64 ) ; } // Write the arguments to where they belong on the stack for i in 0 .. args . len ( ) { let i = i as usize ; let ref arg_val = args [ i ] ; let ref arg_loc = locations [ i ] ; match arg_val . ty . v { MuType_ :: Hybrid ( _ ) => panic ! ( " " ) , MuType_ :: Vector ( _ , _ ) => unimplemented ! ( ) , MuType_ :: Struct ( _ ) | MuType_ :: Array ( _ , _ ) => { unimplemented ! ( ) ; // Todo (note: these may be passed as IRef's) } MuType_ :: Void => panic ! ( " " ) , // Everything else is simple _ => { if arg_loc . is_reg ( ) { arg_regs . push ( arg_loc . clone ( ) ) ; } // Need to pass in two registers if is_int_ex_reg ( & arg_val ) && arg_loc . is_reg ( ) && reg_convention . is_some ( ) { let arg_val = emit_reg_value ( self . backend . as_mut ( ) , & arg_val , f_context , vm ) ; let ( val_l , val_h ) = split_int128 ( & arg_val , f_context , vm ) ; let arg_loc_h_id = arg_loc . id ( ) + 2 ; let ( arg_loc_l , arg_loc_h ) = match reg_convention { RegisterCallConvention :: Memory ( ref b ) => ( make_value_base_offset ( b , get_argument_reg_offset ( arg_loc . id ( ) ) as i64 , & arg_loc . ty , vm ) , make_value_base_offset ( b , get_argument_reg_offset ( arg_loc_h_id ) as i64 , & arg_loc . ty , vm ) ) , _ => ( arg_loc . clone ( ) , get_register_from_id ( arg_loc_h_id ) ) } ; arg_regs . push ( arg_loc_h . clone ( ) ) ; emit_move_value_to_value ( self . backend . as_mut ( ) , & arg_loc_l , & val_l , f_context , vm ) ; emit_move_value_to_value ( self . backend . as_mut ( ) , & arg_loc_h , & val_h , f_context , vm ) ; } else { if ( reg_convention . is_some ( ) && arg_loc . is_reg ( ) ) || ( stack_convention . is_some ( ) && ! arg_loc . is_reg ( ) ) { let arg_loc = if arg_loc . is_reg ( ) { match reg_convention { RegisterCallConvention :: Memory ( ref b ) => { make_value_base_offset ( b , get_argument_reg_offset ( arg_loc . id ( ) ) as i64 , & arg_loc . ty , vm ) } _ => arg_loc . clone ( ) } } else { arg_loc . clone ( ) } ; emit_move_value_to_value ( self . backend . as_mut ( ) , & arg_loc , & arg_val , f_context , vm ) } } } } } ( stack_size , arg_regs ) }
returns the stack arg offset - we will need this to collapse stack after the call
as well as a list of argument registers
returns the stack arg offset - we will need this to collapse stack after the call as well as a list of argument registers
&mut self
reg_convention
RegisterCallConvention
stack_convention
StackCallConvention
is_tail
bool
args
&Vec<P<Value>>
arg_tys
&Vec<P<MuType>>
return_size
usize
f_context
&mut FunctionContext
vm
&VM
&mut self
reg_convention
RegisterCallConvention
stack_convention
StackCallConvention
is_tail
bool
args
&Vec<P<Value>>
arg_tys
&Vec<P<MuType>>
return_size
usize
f_context
&mut FunctionContext
vm
&VM