int _jit_create_call_return_insns (jit_function_t func, jit_type_t signature, jit_value_t *args, unsigned int num_args, jit_value_t return_value, int is_nested) { jit_type_t return_type; int ptr_return; /* Bail out now if we don't need to worry about return values */ return_type = jit_type_normalize(jit_type_get_return(signature)); ptr_return = jit_type_return_via_pointer(return_type); if(!return_value || ptr_return) { return 0; } /* Structure values must be flushed into the frame, and everything else ends up in a register */ if(jit_type_is_struct(return_type) || jit_type_is_union(return_type)) { if(!jit_insn_flush_struct(func, return_value)) { return 0; } } #ifdef JIT_ARM_HAS_FLOAT_REGS else if(return_type->kind == JIT_TYPE_FLOAT32 || return_type->kind == JIT_TYPE_FLOAT64 || return_type->kind == JIT_TYPE_NFLOAT) { if(!jit_insn_return_reg(func, return_value, 16 /* f0 */)) { return 0; } } #endif else if(return_type->kind != JIT_TYPE_VOID) { if(!jit_insn_return_reg(func, return_value, 0 /* r0 */)) { return 0; } } /* Everything is back where it needs to be */ return 1; }
/* * Calculate the size of the argument area for an interpreted function. */ unsigned int _jit_interp_calculate_arg_size (jit_function_t func, jit_type_t signature) { unsigned int size = 0; jit_type_t type; unsigned int num_params; unsigned int param; /* Determine if we need nested parameter information */ if(func->nested_parent) { size += 2 * sizeof(jit_item); } /* Determine if we need a structure pointer argument */ type = jit_type_get_return(signature); if(jit_type_return_via_pointer(type)) { size += sizeof(jit_item); } /* Calculate the total size of the regular arguments */ num_params = jit_type_num_params(signature); for(param = 0; param < num_params; ++param) { type = jit_type_get_param(signature, param); type = jit_type_remove_tags(type); if(type->kind == JIT_TYPE_STRUCT || type->kind == JIT_TYPE_UNION) { size += JIT_NUM_ITEMS_IN_STRUCT(jit_type_get_size(type)) * sizeof(jit_item); } else { size += sizeof(jit_item); } } /* Return the final size to the caller */ return size; }
/*@ * @deftypefun jit_value_t jit_value_get_struct_pointer (jit_function_t @var{func}) * Get the value that contains the structure return pointer for * a function. If the function does not have a structure return pointer * (i.e. structures are returned in registers), then this returns NULL. * @end deftypefun @*/ jit_value_t jit_value_get_struct_pointer(jit_function_t func) { jit_type_t type; jit_value_t value; /* Ensure that we have a builder for this function */ if(!_jit_function_ensure_builder(func)) { return 0; } type = jit_type_remove_tags(jit_type_get_return(func->signature)); if(jit_type_is_struct(type) || jit_type_is_union(type)) { if(jit_type_return_via_pointer(type)) { if(!func->builder->struct_return) { type = jit_type_create_pointer(type, 1); if(!type) { return 0; } value = jit_value_create(func, type); func->builder->struct_return = value; if(value) { /* The value belongs to the entry block, no matter where it happens to be created */ value->block = func->builder->entry_block; value->is_parameter = 1; } jit_type_free(type); } return func->builder->struct_return; } } return 0; }
/*@ * @deftypefun int _jit_create_call_return_insns (jit_function_t @var{func}, jit_type_t @var{signature}, jit_value_t *@var{args}, unsigned int @var{num_args}, jit_value_t @var{return_value}, int @var{is_nested}) * Create instructions within @var{func} to clean up after a function call * and to place the function's result into @var{return_value}. * This should use @code{jit_insn_pop_stack} to pop values off the system * stack and @code{jit_insn_return_reg} to tell @code{libjit} which * register contains the return value. In the case of a @code{void} * function, @var{return_value} will be NULL. * * Note: the argument values are passed again because it may not be possible * to determine how many bytes to pop from the stack from the @var{signature} * alone; especially if the called function is vararg. * @end deftypefun @*/ int _jit_create_call_return_insns (jit_function_t func, jit_type_t signature, jit_value_t *args, unsigned int num_args, jit_value_t return_value, int is_nested) { jit_nint pop_items; unsigned int size; jit_type_t return_type; int ptr_return; /* Calculate the number of items that we need to pop */ pop_items = 0; while(num_args > 0) { --num_args; size = jit_type_get_size(jit_value_get_type(args[num_args])); pop_items += JIT_NUM_ITEMS_IN_STRUCT(size); } return_type = jit_type_get_return(signature); return_type = jit_type_remove_tags(return_type); ptr_return = jit_type_return_via_pointer(return_type); if(ptr_return) { ++pop_items; } if(is_nested) { /* The interpreter needs two arguments for the parent frame info */ pop_items += 2; } /* Pop the items from the system stack */ if(pop_items > 0) { if(!jit_insn_pop_stack(func, pop_items)) { return 0; } } /* Bail out now if we don't need to worry about return values */ if(!return_value || ptr_return) { return 1; } /* Structure values must be flushed into the frame, and everything else ends up in the top-most stack register */ if(jit_type_is_struct(return_type) || jit_type_is_union(return_type)) { if(!jit_insn_flush_struct(func, return_value)) { return 0; } } else if(return_type->kind != JIT_TYPE_VOID) { if(!jit_insn_return_reg(func, return_value, 0)) { return 0; } } /* Everything is back where it needs to be */ return 1; }
/*@ * @deftypefun int _jit_create_call_setup_insns (jit_function_t @var{func}, jit_type_t @var{signature}, jit_value_t *@var{args}, unsigned int @var{num_args}, int @var{is_nested}, int @var{nested_level}, jit_value_t *@var{struct_return}, int @var{flags}) * Create instructions within @var{func} necessary to set up for a * function call to a function with the specified @var{signature}. * Use @code{jit_insn_push} to push values onto the system stack, * or @code{jit_insn_outgoing_reg} to copy values into call registers. * * If @var{is_nested} is non-zero, then it indicates that we are calling a * nested function within the current function's nested relationship tree. * The @var{nested_level} value will be -1 to call a child, zero to call a * sibling of @var{func}, 1 to call a sibling of the parent, 2 to call * a sibling of the grandparent, etc. The @code{jit_insn_setup_for_nested} * instruction should be used to create the nested function setup code. * * If the function returns a structure by pointer, then @var{struct_return} * must be set to a new local variable that will contain the returned * structure. Otherwise it should be set to NULL. * @end deftypefun @*/ int _jit_create_call_setup_insns (jit_function_t func, jit_type_t signature, jit_value_t *args, unsigned int num_args, int is_nested, int nested_level, jit_value_t *struct_return, int flags) { jit_type_t type; jit_type_t vtype; jit_value_t value; unsigned int arg_num; jit_nint offset; jit_nuint size; /* Regular or tail call? */ if((flags & JIT_CALL_TAIL) == 0) { /* Push all of the arguments in reverse order */ while(num_args > 0) { --num_args; type = jit_type_get_param(signature, num_args); type = jit_type_remove_tags(type); if(type->kind == JIT_TYPE_STRUCT || type->kind == JIT_TYPE_UNION) { /* If the value is a pointer, then we are pushing a structure argument by pointer rather than by local variable */ vtype = jit_type_normalize(jit_value_get_type(args[num_args])); if(vtype->kind <= JIT_TYPE_MAX_PRIMITIVE) { if(!jit_insn_push_ptr(func, args[num_args], type)) { return 0; } continue; } } if(!jit_insn_push(func, args[num_args])) { return 0; } } /* Do we need to add a structure return pointer argument? */ type = jit_type_get_return(signature); if(jit_type_return_via_pointer(type)) { value = jit_value_create(func, type); if(!value) { return 0; } *struct_return = value; value = jit_insn_address_of(func, value); if(!value) { return 0; } if(!jit_insn_push(func, value)) { return 0; } } else if((flags & JIT_CALL_NATIVE) != 0) { /* Native calls always return a return area pointer */ if(!jit_insn_push_return_area_ptr(func)) { return 0; } *struct_return = 0; } else { *struct_return = 0; } /* Do we need to add nested function scope information? */ if(is_nested) { if(!jit_insn_setup_for_nested(func, nested_level, -1)) { return 0; } } } else { /* Copy the arguments into our own parameter slots */ offset = -1; if(func->nested_parent) { offset -= 2; } type = jit_type_get_return(signature); if(jit_type_return_via_pointer(type)) { --offset; } for(arg_num = 0; arg_num < num_args; ++arg_num) { type = jit_type_get_param(signature, arg_num); value = jit_value_create(func, type); if(!value) { return 0; } if(!jit_insn_outgoing_frame_posn(func, value, offset)) { return 0; } type = jit_type_remove_tags(type); size = jit_type_get_size(type); offset -= (jit_nint)(JIT_NUM_ITEMS_IN_STRUCT(size)); if(type->kind == JIT_TYPE_STRUCT || type->kind == JIT_TYPE_UNION) { /* If the value is a pointer, then we are pushing a structure argument by pointer rather than by local variable */ vtype = jit_type_normalize(jit_value_get_type(args[arg_num])); if(vtype->kind <= JIT_TYPE_MAX_PRIMITIVE) { value = jit_insn_address_of(func, value); if(!value) { return 0; } if(!jit_insn_memcpy (func, value, args[arg_num], jit_value_create_nint_constant (func, jit_type_nint, (jit_nint)size))) { return 0; } continue; } } if(!jit_insn_store(func, value, args[arg_num])) { return 0; } } *struct_return = 0; } /* The call is ready to proceed */ return 1; }
void _jit_gen_epilog(jit_gencode_t gen, jit_function_t func) { jit_nint pop_bytes = 0; int reg, offset; unsigned char *inst; int struct_return_offset = 0; void **fixup; void **next; /* Check if there is sufficient space for the epilog */ _jit_cache_check_space(&gen->posn, 48); #if JIT_APPLY_X86_FASTCALL == 1 /* Determine the number of parameter bytes to pop when we return */ { jit_type_t signature; unsigned int num_params; unsigned int param; signature = func->signature; if(jit_type_get_abi(signature) == jit_abi_stdcall || jit_type_get_abi(signature) == jit_abi_thiscall|| jit_type_get_abi(signature) == jit_abi_fastcall) { if(func->nested_parent) { pop_bytes += sizeof(void *); } if(jit_type_return_via_pointer(jit_type_get_return(signature))) { struct_return_offset = 2 * sizeof(void *) + pop_bytes; pop_bytes += sizeof(void *); } num_params = jit_type_num_params(signature); for(param = 0; param < num_params; ++param) { pop_bytes += ROUND_STACK (jit_type_get_size (jit_type_get_param(signature, param))); } if(jit_type_get_abi(signature) == jit_abi_fastcall) { /* The first two words are in fastcall registers */ if(pop_bytes > (2 * sizeof(void *))) { pop_bytes -= 2 * sizeof(void *); } else { pop_bytes = 0; } struct_return_offset = 0; } else if(jit_type_get_abi(signature) == jit_abi_thiscall) { /* The this is in ECX register */ if(pop_bytes > (1 * sizeof(void *))) { pop_bytes -= 1 * sizeof(void *); } else { pop_bytes = 0; } struct_return_offset = 0; } } else if(!(func->nested_parent) && jit_type_return_via_pointer(jit_type_get_return(signature))) { #if JIT_APPLY_X86_POP_STRUCT_RETURN == 1 pop_bytes += sizeof(void *); #endif struct_return_offset = 2 * sizeof(void *); } } #else { /* We only need to pop structure pointers in non-nested functions */ jit_type_t signature; signature = func->signature; if(!(func->nested_parent) && jit_type_return_via_pointer(jit_type_get_return(signature))) { #if JIT_APPLY_X86_POP_STRUCT_RETURN == 1 pop_bytes += sizeof(void *); #endif struct_return_offset = 2 * sizeof(void *); } } #endif /* Perform fixups on any blocks that jump to the epilog */ inst = gen->posn.ptr; fixup = (void **)(gen->epilog_fixup); while(fixup != 0) { next = (void **)(fixup[0]); fixup[0] = (void *)(((jit_nint)inst) - ((jit_nint)fixup) - 4); fixup = next; } gen->epilog_fixup = 0; /* If we are returning a structure via a pointer, then copy the pointer value into EAX when we return */ if(struct_return_offset != 0) { x86_mov_reg_membase(inst, X86_EAX, X86_EBP, struct_return_offset, 4); } /* Restore the callee save registers that we used */ if(gen->stack_changed) { offset = -(func->builder->frame_size); for(reg = 0; reg <= 7; ++reg) { if(jit_reg_is_used(gen->touched, reg) && (_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0) { offset -= sizeof(void *); x86_mov_reg_membase(inst, _jit_reg_info[reg].cpu_reg, X86_EBP, offset, sizeof(void *)); } } } else { for(reg = 7; reg >= 0; --reg) { if(jit_reg_is_used(gen->touched, reg) && (_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0) { x86_pop_reg(inst, _jit_reg_info[reg].cpu_reg); } } } /* Pop the stack frame and restore the saved copy of ebp */ if(gen->stack_changed || func->builder->frame_size > 0) { x86_mov_reg_reg(inst, X86_ESP, X86_EBP, sizeof(void *)); } x86_pop_reg(inst, X86_EBP); /* Return from the current function */ if(pop_bytes > 0) { x86_ret_imm(inst, pop_bytes); } else { x86_ret(inst); } gen->posn.ptr = inst; }
int _jit_create_call_return_insns (jit_function_t func, jit_type_t signature, jit_value_t *args, unsigned int num_args, jit_value_t return_value, int is_nested) { jit_nint pop_bytes; unsigned int size; jit_type_t return_type; int ptr_return; /* Calculate the number of bytes that we need to pop */ return_type = jit_type_normalize(jit_type_get_return(signature)); ptr_return = jit_type_return_via_pointer(return_type); #if JIT_APPLY_X86_FASTCALL == 1 if(jit_type_get_abi(signature) == jit_abi_stdcall || jit_type_get_abi(signature) == jit_abi_thiscall|| jit_type_get_abi(signature) == jit_abi_fastcall) { /* STDCALL, THISCALL and FASTCALL functions pop their own arguments */ pop_bytes = 0; } else #endif { pop_bytes = 0; while(num_args > 0) { --num_args; size = jit_type_get_size(jit_value_get_type(args[num_args])); pop_bytes += ROUND_STACK(size); } #if JIT_APPLY_X86_POP_STRUCT_RETURN == 1 if(ptr_return && is_nested) { /* Note: we only need this for nested functions, because regular functions will pop the structure return for us */ pop_bytes += sizeof(void *); } #else if(ptr_return) { pop_bytes += sizeof(void *); } #endif if(is_nested) { pop_bytes += sizeof(void *); } } /* Pop the bytes from the system stack */ if(pop_bytes > 0) { if(!jit_insn_defer_pop_stack(func, pop_bytes)) { return 0; } } /* Bail out now if we don't need to worry about return values */ if(!return_value || ptr_return) { return 1; } /* Structure values must be flushed into the frame, and everything else ends up in a register */ if(jit_type_is_struct(return_type) || jit_type_is_union(return_type)) { if(!jit_insn_flush_struct(func, return_value)) { return 0; } } else if(return_type == jit_type_float32 || return_type == jit_type_float64 || return_type == jit_type_nfloat || jit_type_get_kind(return_type) == JIT_TYPE_FLOAT32 || jit_type_get_kind(return_type) == JIT_TYPE_FLOAT64 || jit_type_get_kind(return_type) == JIT_TYPE_NFLOAT ) { if(!jit_insn_return_reg(func, return_value, X86_REG_ST0)) { return 0; } } else if(return_type->kind != JIT_TYPE_VOID) { if(!jit_insn_return_reg(func, return_value, X86_REG_EAX)) { return 0; } } /* Everything is back where it needs to be */ return 1; }