/* * The 64-bit version of libgcc does not contain some builtin * functions for 32-bit values (__<builtin>si2) anymore. */ static void widen_builtin(ir_node *node) { ir_type *mtp = get_Builtin_type(node); ir_type *arg1 = get_method_param_type(mtp, 0); // Nothing to do, if argument size is at least machine size. if (get_type_size(arg1) >= ir_target_pointer_size()) return; // Only touch builtins with no 32-bit version. ir_builtin_kind kind = get_Builtin_kind(node); if (kind != ir_bk_clz && kind != ir_bk_ctz && kind != ir_bk_ffs && kind != ir_bk_parity && kind != ir_bk_popcount) { return; } ir_mode *target_mode = get_reference_offset_mode(mode_P); dbg_info *dbgi = get_irn_dbg_info(node); ir_node *block = get_nodes_block(node); ir_node *op = get_irn_n(node, n_Builtin_max + 1); ir_node *conv = new_rd_Conv(dbgi, block, op, target_mode); set_irn_n(node, n_Builtin_max + 1, conv); ir_type *new_arg1 = get_type_for_mode(target_mode); ir_type *new_result = get_method_res_type(mtp, 0); ir_type *new_type = new_type_method(1, 1, false, cc_cdecl_set, mtp_no_property); set_method_param_type(new_type, 0, new_arg1); set_method_res_type(new_type, 0, new_result); set_Builtin_type(node, new_type); }
static ir_type *lower_type_if_needed(ir_type *tp) { bool need_lower = false; size_t const n_params = get_method_n_params(tp); for (size_t p = 0; p < n_params; ++p) { ir_type *ptp = get_method_param_type(tp, p); ir_mode *pmode = get_type_mode(ptp); if (pmode && mode_is_float(pmode)) { need_lower = true; break; } } size_t const n_res = get_method_n_ress(tp); for (size_t i = 0; i < n_res; ++i) { ir_type *rtp = get_method_res_type(tp, i); ir_mode *rmode = get_type_mode(rtp); if (rmode && mode_is_float(rmode)) { need_lower = true; break; } } if (need_lower) { return lower_method_type(tp); } else { return tp; } }
static void replace_with_call(ir_node *node) { widen_builtin(node); ir_type *const mtp = get_Builtin_type(node); ir_builtin_kind const kind = get_Builtin_kind(node); char const *const name = get_builtin_name(kind); ir_type *const arg1 = get_method_param_type(mtp, 0); char const *const machmode = get_gcc_machmode(arg1); ident *const id = new_id_fmt("__%s%s2", name, machmode); ir_entity *const entity = create_compilerlib_entity(get_id_str(id), mtp); dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = get_nodes_block(node); ir_node *const mem = get_Builtin_mem(node); ir_graph *const irg = get_irn_irg(node); ir_node *const callee = new_r_Address(irg, entity); int const n_params = get_Builtin_n_params(node); ir_node **const params = get_Builtin_param_arr(node); ir_node *const call = new_rd_Call(dbgi, block, mem, callee, n_params, params, mtp); ir_node *const call_mem = new_r_Proj(call, mode_M, pn_Call_M); ir_node *const call_ress = new_r_Proj(call, mode_T, pn_Call_T_result); ir_type *const res_type = get_method_res_type(mtp, 0); ir_mode *const res_mode = get_type_mode(res_type); ir_node *const call_res = new_r_Proj(call_ress, res_mode, 0); ir_node *const in[] = { [pn_Builtin_M] = call_mem, [pn_Builtin_max + 1] = call_res, };
/** * Check if a argument of the ir graph with mode * reference is read, write or both. * * @param irg The ir graph to analyze. */ static void analyze_ent_args(ir_entity *ent) { ir_type *mtp = get_entity_type(ent); size_t nparams = get_method_n_params(mtp); ent->attr.mtd_attr.param_access = NEW_ARR_F(ptr_access_kind, nparams); /* If the method haven't parameters we have * nothing to do. */ if (nparams <= 0) return; /* we have not yet analyzed the graph, set ALL access for pointer args */ for (size_t i = nparams; i-- > 0; ) { ir_type *type = get_method_param_type(mtp, i); ent->attr.mtd_attr.param_access[i] = is_Pointer_type(type) ? ptr_access_all : ptr_access_none; } ir_graph *irg = get_entity_irg(ent); if (irg == NULL) { /* no graph, no better info */ return; } assure_irg_outs(irg); ir_node *irg_args = get_irg_args(irg); /* A array to save the information for each argument with mode reference.*/ ptr_access_kind *rw_info; NEW_ARR_A(ptr_access_kind, rw_info, nparams); /* We initialize the element with none state. */ for (size_t i = nparams; i-- > 0; ) rw_info[i] = ptr_access_none; /* search for arguments with mode reference to analyze them.*/ for (int i = get_irn_n_outs(irg_args); i-- > 0; ) { ir_node *arg = get_irn_out(irg_args, i); ir_mode *arg_mode = get_irn_mode(arg); long proj_nr = get_Proj_proj(arg); if (mode_is_reference(arg_mode)) rw_info[proj_nr] |= analyze_arg(arg, rw_info[proj_nr]); } /* copy the temporary info */ memcpy(ent->attr.mtd_attr.param_access, rw_info, nparams * sizeof(ent->attr.mtd_attr.param_access[0])); }
/** * @return The lowered method type. */ static ir_type *lower_method_type(ir_type *mtp) { ir_type *res = pmap_get(ir_type, lowered_type, mtp); if (res != NULL) return res; size_t const n_param = get_method_n_params(mtp); size_t const n_res = get_method_n_ress(mtp); bool const is_variadic = is_method_variadic(mtp); res = new_type_method(n_param, n_res, is_variadic); /* set param types and result types */ for (size_t i = 0; i < n_param; ++i) { ir_type *ptp = get_method_param_type(mtp, i); ir_mode *pmode = get_type_mode(ptp); if (pmode != NULL && mode_is_float(pmode)) { ptp = lower_type(ptp); } set_method_param_type(res, i, ptp); } for (size_t i = 0; i < n_res; ++i) { ir_type *rtp = get_method_res_type(mtp, i); ir_mode *rmode = get_type_mode(rtp); if (rmode != NULL && mode_is_float(rmode)) { rtp = lower_type(rtp); } set_method_res_type(res, i, rtp); } copy_method_properties(res, mtp); set_higher_type(res, mtp); pmap_insert(lowered_type, mtp, res); return res; }
/** * Perform some fixups for variadic functions. * To make the rest of the frontend code easier to understand we add * "dummy" parameters until the number of parameters transmitted in registers. * (because otherwise the backend wouldn't store the value of the register * parameters into memory for the VLA magic) */ static bool sparc_variadic_fixups(ir_graph *const irg, calling_convention_t *const cconv) { ir_entity *entity = get_irg_entity(irg); ir_type *mtp = get_entity_type(entity); if (!is_method_variadic(mtp)) return false; if (cconv->n_param_regs >= SPARC_N_PARAM_REGS) return false; size_t const n_params = get_method_n_params(mtp); size_t const n_ress = get_method_n_ress(mtp); size_t const new_n_params = n_params + (SPARC_N_PARAM_REGS - cconv->n_param_regs); unsigned const cc_mask = get_method_calling_convention(mtp); mtp_additional_properties const props = get_method_additional_properties(mtp); ir_type *const new_mtp = new_type_method(new_n_params, n_ress, true, cc_mask, props); type_dbg_info *const dbgi = get_type_dbg_info(mtp); set_type_dbg_info(new_mtp, dbgi); for (size_t i = 0; i < n_ress; ++i) { ir_type *type = get_method_res_type(mtp, i); set_method_res_type(new_mtp, i, type); } for (size_t i = 0; i < n_params; ++i) { ir_type *type = get_method_param_type(mtp, i); set_method_param_type(new_mtp, i, type); } ir_type *const frame_type = get_irg_frame_type(irg); ir_mode *const gp_reg_mode = sparc_reg_classes[CLASS_sparc_gp].mode; ir_type *const gp_reg_type = get_type_for_mode(gp_reg_mode); for (size_t i = n_params; i < new_n_params; ++i) { set_method_param_type(new_mtp, i, gp_reg_type); new_parameter_entity(frame_type, i, gp_reg_type); } set_entity_type(entity, new_mtp); return true; }
calling_convention_t *sparc_decide_calling_convention(ir_type *function_type, ir_graph *irg) { bool omit_fp = false; if (irg != NULL) { omit_fp = be_options.omit_fp; /* our current vaarg handling needs the standard space to store the * args 0-5 in it */ if (is_method_variadic(function_type)) omit_fp = false; /* The pointer to the aggregate return value belongs to the 92 magic bytes. * Thus, if the called functions increases the stack size, * it must copy the value to the appropriate location. * This is not implemented yet, so we forbid to omit the frame pointer. */ if (get_method_calling_convention(function_type) & cc_compound_ret) omit_fp = false; if (omit_fp) irg_walk_graph(irg, check_omit_fp, NULL, &omit_fp); sparc_get_irg_data(irg)->omit_fp = omit_fp; } mtp_additional_properties mtp = get_method_additional_properties(function_type); unsigned *caller_saves = rbitset_malloc(N_SPARC_REGISTERS); if (mtp & mtp_property_returns_twice) { rbitset_copy(caller_saves, default_returns_twice_saves, N_SPARC_REGISTERS); } else { rbitset_copy(caller_saves, default_caller_saves, N_SPARC_REGISTERS); } /* determine how parameters are passed */ int n_params = get_method_n_params(function_type); int regnum = 0; reg_or_stackslot_t *params = XMALLOCNZ(reg_or_stackslot_t, n_params); int n_param_regs = ARRAY_SIZE(param_regs); unsigned stack_offset = !omit_fp ? SPARC_MIN_STACKSIZE : 0; for (int i = 0; i < n_params; ++i) { ir_type *param_type = get_method_param_type(function_type,i); ir_mode *mode; int bits; reg_or_stackslot_t *param; if (is_compound_type(param_type)) panic("compound arguments not supported yet"); mode = get_type_mode(param_type); bits = get_mode_size_bits(mode); param = ¶ms[i]; if (i == 0 && (get_method_calling_convention(function_type) & cc_compound_ret)) { assert(mode_is_reference(mode) && bits == 32); /* special case, we have reserved space for this on the between * type */ param->type = param_type; param->offset = SPARC_AGGREGATE_RETURN_OFFSET; param->already_stored = true; continue; } if (regnum < n_param_regs) { param->offset = SPARC_PARAMS_SPILL_OFFSET + regnum * SPARC_REGISTER_SIZE; param->type = param_type; arch_register_t const *reg = param_regs[regnum++]; if (irg == NULL || omit_fp) reg = map_i_to_o_reg(reg); param->reg0 = reg; param->req0 = reg->single_req; } else { param->type = param_type; param->offset = stack_offset; param->already_stored = true; /* increase offset by at least SPARC_REGISTER_SIZE bytes so * everything is aligned */ stack_offset += MAX(bits / 8, SPARC_REGISTER_SIZE); continue; } /* we might need a 2nd 32bit component (for 64bit or double values) */ if (bits > 32) { if (bits > 64) panic("only 32 and 64bit modes supported"); if (regnum < n_param_regs) { param->offset = SPARC_PARAMS_SPILL_OFFSET + regnum * SPARC_REGISTER_SIZE; arch_register_t const *reg = param_regs[regnum++]; if (irg == NULL || omit_fp) reg = map_i_to_o_reg(reg); param->reg1 = reg; param->req1 = reg->single_req; } else { ir_mode *regmode = param_regs[0]->cls->mode; ir_type *type = get_type_for_mode(regmode); param->type = type; param->offset = stack_offset; assert(get_mode_size_bits(regmode) == 32); stack_offset += SPARC_REGISTER_SIZE; } } } unsigned n_param_regs_used = regnum; /* determine how results are passed */ int n_results = get_method_n_ress(function_type); unsigned float_regnum = 0; unsigned n_reg_results = 0; unsigned n_float_result_regs = ARRAY_SIZE(float_result_regs); reg_or_stackslot_t *results = XMALLOCNZ(reg_or_stackslot_t, n_results); regnum = 0; for (int i = 0; i < n_results; ++i) { ir_type *result_type = get_method_res_type(function_type, i); ir_mode *result_mode = get_type_mode(result_type); reg_or_stackslot_t *result = &results[i]; if (mode_is_float(result_mode)) { unsigned n_regs = determine_n_float_regs(result_mode); unsigned next_reg = round_up2(float_regnum, n_regs); if (next_reg >= n_float_result_regs) { panic("too many float results"); } else { const arch_register_t *reg = float_result_regs[next_reg]; rbitset_clear(caller_saves, reg->global_index); if (n_regs == 1) { result->req0 = reg->single_req; } else if (n_regs == 2) { result->req0 = &float_result_reqs_double[next_reg]; rbitset_clear(caller_saves, reg->global_index+1); } else if (n_regs == 4) { result->req0 = &float_result_reqs_quad[next_reg]; rbitset_clear(caller_saves, reg->global_index+1); rbitset_clear(caller_saves, reg->global_index+2); rbitset_clear(caller_saves, reg->global_index+3); } else { panic("invalid number of registers in result"); } float_regnum = next_reg + n_regs; ++n_reg_results; } } else { if (get_mode_size_bits(result_mode) > 32) { panic("results with more than 32bits not supported yet"); } if (regnum >= n_param_regs) { panic("too many results"); } else { const arch_register_t *reg = param_regs[regnum++]; if (irg == NULL || omit_fp) reg = map_i_to_o_reg(reg); result->req0 = reg->single_req; rbitset_clear(caller_saves, reg->global_index); ++n_reg_results; } } } calling_convention_t *cconv = XMALLOCZ(calling_convention_t); cconv->n_parameters = n_params; cconv->parameters = params; cconv->param_stack_size = stack_offset - SPARC_MIN_STACKSIZE; cconv->n_param_regs = n_param_regs_used; cconv->results = results; cconv->omit_fp = omit_fp; cconv->caller_saves = caller_saves; cconv->n_reg_results = n_reg_results; /* setup ignore register array */ if (irg != NULL) { be_irg_t *birg = be_birg_from_irg(irg); birg->allocatable_regs = be_cconv_alloc_all_regs(&birg->obst, N_SPARC_REGISTERS); be_cconv_rem_regs(birg->allocatable_regs, ignore_regs, ARRAY_SIZE(ignore_regs)); } return cconv; }
x86_cconv_t *ia32_decide_calling_convention(ir_type *function_type, ir_graph *irg) { bool omit_fp = false; if (irg != NULL) { omit_fp = be_options.omit_fp; if (omit_fp) irg_walk_graph(irg, check_omit_fp, NULL, &omit_fp); } mtp_additional_properties mtp = get_method_additional_properties(function_type); (void)mtp; /* TODO: do something with cc_reg_param/cc_this_call */ unsigned *caller_saves = rbitset_malloc(N_IA32_REGISTERS); unsigned *callee_saves = rbitset_malloc(N_IA32_REGISTERS); rbitset_copy(caller_saves, default_caller_saves, N_IA32_REGISTERS); rbitset_copy(callee_saves, default_callee_saves, N_IA32_REGISTERS); /* determine how parameters are passed */ unsigned n_params = get_method_n_params(function_type); unsigned param_regnum = 0; unsigned float_param_regnum = 0; reg_or_stackslot_t *params = XMALLOCNZ(reg_or_stackslot_t, n_params); unsigned n_param_regs = ARRAY_SIZE(default_param_regs); unsigned n_float_param_regs = ARRAY_SIZE(float_param_regs); unsigned stack_offset = 0; for (unsigned i = 0; i < n_params; ++i) { ir_type *param_type = get_method_param_type(function_type, i); reg_or_stackslot_t *param = ¶ms[i]; if (is_aggregate_type(param_type)) { param->type = param_type; param->offset = stack_offset; stack_offset += get_type_size_bytes(param_type); goto align_stack; } ir_mode *mode = get_type_mode(param_type); if (mode_is_float(mode) && float_param_regnum < n_float_param_regs) { param->reg = float_param_regs[float_param_regnum++]; } else if (!mode_is_float(mode) && param_regnum < n_param_regs) { param->reg = default_param_regs[param_regnum++]; } else { param->type = param_type; param->offset = stack_offset; stack_offset += get_type_size_bytes(param_type); align_stack:; /* increase offset by at least IA32_REGISTER_SIZE bytes so * everything is aligned */ unsigned misalign = stack_offset % IA32_REGISTER_SIZE; if (misalign > 0) stack_offset += IA32_REGISTER_SIZE - misalign; } } unsigned n_param_regs_used = param_regnum + float_param_regnum; /* determine how results are passed */ unsigned n_results = get_method_n_ress(function_type); unsigned n_reg_results = 0; reg_or_stackslot_t *results = XMALLOCNZ(reg_or_stackslot_t, n_results); unsigned res_regnum = 0; unsigned res_float_regnum = 0; unsigned n_result_regs = ARRAY_SIZE(result_regs); unsigned n_float_result_regs = ARRAY_SIZE(float_result_regs); for (size_t i = 0; i < n_results; ++i) { ir_type *result_type = get_method_res_type(function_type, i); ir_mode *result_mode = get_type_mode(result_type); reg_or_stackslot_t *result = &results[i]; const arch_register_t *reg; if (mode_is_float(result_mode)) { if (res_float_regnum >= n_float_result_regs) { panic("too many floating points results"); } reg = float_result_regs[res_float_regnum++]; } else { if (res_regnum >= n_result_regs) { panic("too many results"); } reg = result_regs[res_regnum++]; } result->reg = reg; rbitset_clear(caller_saves, reg->global_index); ++n_reg_results; } calling_convention cc = get_method_calling_convention(function_type); x86_cconv_t *cconv = XMALLOCZ(x86_cconv_t); cconv->sp_delta = (cc & cc_compound_ret) && !(cc & cc_reg_param) ? IA32_REGISTER_SIZE : 0; cconv->parameters = params; cconv->n_parameters = n_params; cconv->callframe_size = stack_offset; cconv->n_param_regs = n_param_regs_used; cconv->n_xmm_regs = float_param_regnum; cconv->results = results; cconv->omit_fp = omit_fp; cconv->caller_saves = caller_saves; cconv->callee_saves = callee_saves; cconv->n_reg_results = n_reg_results; if (irg != NULL) { be_irg_t *birg = be_birg_from_irg(irg); size_t n_ignores = ARRAY_SIZE(ignore_regs); struct obstack *obst = &birg->obst; birg->allocatable_regs = rbitset_obstack_alloc(obst, N_IA32_REGISTERS); rbitset_set_all(birg->allocatable_regs, N_IA32_REGISTERS); for (size_t r = 0; r < n_ignores; ++r) { rbitset_clear(birg->allocatable_regs, ignore_regs[r]); } if (!omit_fp) rbitset_clear(birg->allocatable_regs, REG_EBP); } return cconv; }
calling_convention_t *arm_decide_calling_convention(const ir_graph *irg, ir_type *function_type) { /* determine how parameters are passed */ unsigned stack_offset = 0; size_t const n_param_regs = ARRAY_SIZE(param_regs); size_t const n_params = get_method_n_params(function_type); size_t regnum = 0; reg_or_stackslot_t *params = XMALLOCNZ(reg_or_stackslot_t, n_params); for (size_t i = 0; i < n_params; ++i) { ir_type *param_type = get_method_param_type(function_type,i); ir_mode *mode = get_type_mode(param_type); int bits = get_mode_size_bits(mode); reg_or_stackslot_t *param = ¶ms[i]; param->type = param_type; /* doubleword modes need to be passed in even registers */ if (param_type->flags & tf_lowered_dw) { if (regnum < n_param_regs) { if ((regnum & 1) != 0) ++regnum; } else { unsigned misalign = stack_offset % 8; if (misalign > 0) stack_offset += 8 - misalign; } } if (regnum < n_param_regs) { param->reg0 = param_regs[regnum++]; } else { param->offset = stack_offset; /* increase offset 4 bytes so everything is aligned */ stack_offset += MAX(bits / 8, 4); continue; } /* we might need a 2nd 32bit component (for 64bit or double values) */ if (bits > 32) { if (bits > 64) panic("only 32 and 64bit modes supported"); if (regnum < n_param_regs) { const arch_register_t *reg = param_regs[regnum++]; param->reg1 = reg; } else { ir_mode *pmode = param_regs[0]->cls->mode; ir_type *type = get_type_for_mode(pmode); param->type = type; param->offset = stack_offset; assert(get_mode_size_bits(pmode) == 32); stack_offset += 4; } } } unsigned const n_param_regs_used = regnum; size_t const n_result_regs= ARRAY_SIZE(result_regs); size_t const n_float_result_regs = ARRAY_SIZE(float_result_regs); size_t n_results = get_method_n_ress(function_type); size_t float_regnum = 0; reg_or_stackslot_t *results = XMALLOCNZ(reg_or_stackslot_t, n_results); regnum = 0; for (size_t i = 0; i < n_results; ++i) { ir_type *result_type = get_method_res_type(function_type, i); ir_mode *result_mode = get_type_mode(result_type); reg_or_stackslot_t *result = &results[i]; if (mode_is_float(result_mode)) { if (float_regnum >= n_float_result_regs) { panic("too many float results"); } else { const arch_register_t *reg = float_result_regs[float_regnum++]; result->reg0 = reg; } } else { if (get_mode_size_bits(result_mode) > 32) { panic("results with more than 32bits not supported yet"); } if (regnum >= n_result_regs) { panic("too many results"); } else { const arch_register_t *reg = result_regs[regnum++]; result->reg0 = reg; } } } calling_convention_t *cconv = XMALLOCZ(calling_convention_t); cconv->parameters = params; cconv->n_parameters = n_params; cconv->param_stack_size = stack_offset; cconv->n_param_regs = n_param_regs_used; cconv->results = results; /* setup allocatable registers */ if (irg != NULL) { be_irg_t *birg = be_birg_from_irg(irg); assert(birg->allocatable_regs == NULL); birg->allocatable_regs = be_cconv_alloc_all_regs(&birg->obst, N_ARM_REGISTERS); be_cconv_rem_regs(birg->allocatable_regs, ignore_regs, ARRAY_SIZE(ignore_regs)); arm_get_irg_data(irg)->omit_fp = true; } return cconv; }
/** * @return An Address representing the function that replaces the given node. */ static ir_node *create_softfloat_address(const ir_node *n, const char *name) { ir_type *const method = get_softfloat_type(n); /* Parameter types. */ char const *first_param = ""; char const *second_param = ""; unsigned float_types = 0; unsigned double_types = 0; switch (get_method_n_params(method)) { case 2: { ir_type *const param_type = get_method_param_type(method, 1); ir_mode *const mode = get_type_mode(param_type); if (mode == mode_F) { second_param = "sf"; float_types++; } else if (mode == mode_D) { second_param = "df"; double_types++; } else if (mode == mode_Iu || mode == mode_Is) { second_param = "si"; } else if (mode == mode_Lu || mode == mode_Ls) { second_param = "di"; } } /* FALLTHROUGH */ case 1: { ir_type *const param_type = get_method_param_type(method, 0); ir_mode *const mode = get_type_mode(param_type); if (mode == mode_F) { first_param = float_types > 0 ? "" : "sf"; float_types++; } else if (mode == mode_D) { first_param = double_types > 0 ? "" : "df"; double_types++; } else if (mode == mode_Iu || mode == mode_Is) { first_param = "si"; } else if (mode == mode_Lu || mode == mode_Ls) { first_param = "di"; } break; } default: break; } /* Result type. */ char const *result = ""; ir_mode *const mode = is_Div(n) ? get_Div_resmode(n) : get_irn_mode(n); if (mode == mode_F) { result = float_types > 0 ? "" : "sf"; float_types++; } else if (mode == mode_D) { result = double_types > 0 ? "" : "df"; double_types++; } else if (mode == mode_Iu || mode == mode_Hu || mode == mode_Bu || mode == mode_Is || mode == mode_Hs || mode == mode_Bs) result = "si"; else if (mode == mode_Lu || mode == mode_Ls) result = "di"; assert(float_types <= 3); assert(double_types <= 3); ident *const id = float_types + double_types > 1 ? new_id_fmt("__%s%s%s%s%u", name, first_param, second_param, result, float_types + double_types) : new_id_fmt("__%s%s%s%s", name, first_param, second_param, result); ir_graph *const irg = get_irn_irg(n); ir_entity *const ent = create_compilerlib_entity(id, method); return new_r_Address(irg, ent); }
x86_cconv_t *amd64_decide_calling_convention(ir_type *function_type, ir_graph *irg) { bool omit_fp = false; if (irg != NULL) { omit_fp = be_options.omit_fp; if (omit_fp) irg_walk_graph(irg, check_omit_fp, NULL, &omit_fp); amd64_get_irg_data(irg)->omit_fp = omit_fp; } unsigned *caller_saves = rbitset_malloc(N_AMD64_REGISTERS); unsigned *callee_saves = rbitset_malloc(N_AMD64_REGISTERS); rbitset_copy(caller_saves, default_caller_saves, N_AMD64_REGISTERS); rbitset_copy(callee_saves, default_callee_saves, N_AMD64_REGISTERS); /* determine how parameters are passed */ size_t n_params = get_method_n_params(function_type); size_t param_regnum = 0; size_t float_param_regnum = 0; reg_or_stackslot_t *params = XMALLOCNZ(reg_or_stackslot_t, n_params); /* x64 always reserves space to spill the first 4 arguments to have it * easy in case of variadic functions. */ unsigned stack_offset = amd64_use_x64_abi ? 32 : 0; for (size_t i = 0; i < n_params; ++i) { ir_type *param_type = get_method_param_type(function_type,i); if (is_compound_type(param_type)) panic("compound arguments NIY"); ir_mode *mode = get_type_mode(param_type); int bits = get_mode_size_bits(mode); reg_or_stackslot_t *param = ¶ms[i]; if (mode_is_float(mode) && float_param_regnum < n_float_param_regs && mode != x86_mode_E) { param->reg = float_param_regs[float_param_regnum++]; if (amd64_use_x64_abi) { ++param_regnum; } } else if (!mode_is_float(mode) && param_regnum < n_param_regs) { param->reg = param_regs[param_regnum++]; if (amd64_use_x64_abi) { ++float_param_regnum; } } else { param->type = param_type; param->offset = stack_offset; /* increase offset by at least AMD64_REGISTER_SIZE bytes so * everything is aligned */ stack_offset += round_up2(bits / 8, AMD64_REGISTER_SIZE); } } /* If the function is variadic, we add all unused parameter * passing registers to the end of the params array, first GP, * then XMM. */ if (irg && is_method_variadic(function_type)) { if (amd64_use_x64_abi) { panic("Variadic functions on Windows ABI not supported"); } int params_remaining = (n_param_regs - param_regnum) + (n_float_param_regs - float_param_regnum); params = XREALLOC(params, reg_or_stackslot_t, n_params + params_remaining); size_t i = n_params; for (; param_regnum < n_param_regs; param_regnum++, i++) { params[i].reg = param_regs[param_regnum]; } for (; float_param_regnum < n_float_param_regs; float_param_regnum++, i++) { params[i].reg = float_param_regs[float_param_regnum]; } } unsigned n_param_regs_used = amd64_use_x64_abi ? param_regnum : param_regnum + float_param_regnum; /* determine how results are passed */ size_t n_results = get_method_n_ress(function_type); unsigned n_reg_results = 0; reg_or_stackslot_t *results = XMALLOCNZ(reg_or_stackslot_t, n_results); unsigned res_regnum = 0; unsigned res_float_regnum = 0; unsigned res_x87_regnum = 0; size_t n_result_regs = ARRAY_SIZE(result_regs); size_t n_float_result_regs = ARRAY_SIZE(float_result_regs); size_t n_x87_result_regs = ARRAY_SIZE(x87_result_regs); for (size_t i = 0; i < n_results; ++i) { ir_type *result_type = get_method_res_type(function_type, i); ir_mode *result_mode = get_type_mode(result_type); reg_or_stackslot_t *result = &results[i]; const arch_register_t *reg; if (result_mode == x86_mode_E) { if (res_x87_regnum >= n_x87_result_regs) panic("too manu x87 floating point results"); reg = x87_result_regs[res_x87_regnum++]; } else if (mode_is_float(result_mode)) { if (res_float_regnum >= n_float_result_regs) { panic("too many floating points results"); } reg = float_result_regs[res_float_regnum++]; } else { if (res_regnum >= n_result_regs) { panic("too many results"); } reg = result_regs[res_regnum++]; } result->reg = reg; rbitset_clear(caller_saves, reg->global_index); ++n_reg_results; } x86_cconv_t *cconv = XMALLOCZ(x86_cconv_t); cconv->parameters = params; cconv->n_parameters = n_params; cconv->param_stacksize = stack_offset; cconv->n_param_regs = n_param_regs_used; cconv->n_xmm_regs = float_param_regnum; cconv->results = results; cconv->omit_fp = omit_fp; cconv->caller_saves = caller_saves; cconv->callee_saves = callee_saves; cconv->n_reg_results = n_reg_results; if (irg != NULL) { be_irg_t *birg = be_birg_from_irg(irg); birg->allocatable_regs = be_cconv_alloc_all_regs(&birg->obst, N_AMD64_REGISTERS); be_cconv_rem_regs(birg->allocatable_regs, ignore_regs, ARRAY_SIZE(ignore_regs)); if (!omit_fp) rbitset_clear(birg->allocatable_regs, REG_RBP); } return cconv; }
x86_cconv_t *amd64_decide_calling_convention(ir_type *function_type, ir_graph *irg) { bool omit_fp = false; if (irg != NULL) { omit_fp = be_options.omit_fp; if (omit_fp) irg_walk_graph(irg, check_omit_fp, NULL, &omit_fp); } mtp_additional_properties mtp = get_method_additional_properties(function_type); unsigned *caller_saves = rbitset_malloc(N_AMD64_REGISTERS); unsigned *callee_saves = rbitset_malloc(N_AMD64_REGISTERS); if (mtp & mtp_property_returns_twice) panic("amd64: returns_twice calling convention NIY"); rbitset_copy(caller_saves, default_caller_saves, N_AMD64_REGISTERS); rbitset_copy(callee_saves, default_callee_saves, N_AMD64_REGISTERS); /* determine how parameters are passed */ size_t n_params = get_method_n_params(function_type); size_t param_regnum = 0; size_t float_param_regnum = 0; reg_or_stackslot_t *params = XMALLOCNZ(reg_or_stackslot_t, n_params); /* x64 always reserves space to spill the first 4 arguments to have it * easy in case of variadic functions. */ unsigned stack_offset = amd64_use_x64_abi ? 32 : 0; for (size_t i = 0; i < n_params; ++i) { ir_type *param_type = get_method_param_type(function_type,i); if (is_compound_type(param_type)) panic("amd64: compound arguments NIY"); ir_mode *mode = get_type_mode(param_type); int bits = get_mode_size_bits(mode); reg_or_stackslot_t *param = ¶ms[i]; if (mode_is_float(mode) && float_param_regnum < n_float_param_regs) { param->reg = float_param_regs[float_param_regnum++]; if (amd64_use_x64_abi) ++param_regnum; } else if (!mode_is_float(mode) && param_regnum < n_param_regs) { param->reg = param_regs[param_regnum++]; if (amd64_use_x64_abi) ++float_param_regnum; } else { param->type = param_type; param->offset = stack_offset; /* increase offset by at least AMD64_REGISTER_SIZE bytes so * everything is aligned */ stack_offset += MAX(bits / 8, AMD64_REGISTER_SIZE); continue; } } unsigned n_param_regs_used = amd64_use_x64_abi ? param_regnum : param_regnum + float_param_regnum; /* determine how results are passed */ size_t n_results = get_method_n_ress(function_type); unsigned n_reg_results = 0; reg_or_stackslot_t *results = XMALLOCNZ(reg_or_stackslot_t, n_results); unsigned res_regnum = 0; unsigned res_float_regnum = 0; size_t n_result_regs = ARRAY_SIZE(result_regs); size_t n_float_result_regs = ARRAY_SIZE(float_result_regs); for (size_t i = 0; i < n_results; ++i) { ir_type *result_type = get_method_res_type(function_type, i); ir_mode *result_mode = get_type_mode(result_type); reg_or_stackslot_t *result = &results[i]; const arch_register_t *reg; if (mode_is_float(result_mode)) { if (res_float_regnum >= n_float_result_regs) { panic("too many floating points results"); } reg = float_result_regs[res_float_regnum++]; } else { if (res_regnum >= n_result_regs) { panic("too many results"); } reg = result_regs[res_regnum++]; } result->reg = reg; rbitset_clear(caller_saves, reg->global_index); ++n_reg_results; } x86_cconv_t *cconv = XMALLOCZ(x86_cconv_t); cconv->parameters = params; cconv->callframe_size = stack_offset; cconv->n_param_regs = n_param_regs_used; cconv->n_xmm_regs = float_param_regnum; cconv->results = results; cconv->omit_fp = omit_fp; cconv->caller_saves = caller_saves; cconv->callee_saves = callee_saves; cconv->n_reg_results = n_reg_results; if (irg != NULL) { be_irg_t *birg = be_birg_from_irg(irg); size_t n_ignores = ARRAY_SIZE(ignore_regs); struct obstack *obst = &birg->obst; birg->allocatable_regs = rbitset_obstack_alloc(obst, N_AMD64_REGISTERS); rbitset_set_all(birg->allocatable_regs, N_AMD64_REGISTERS); for (size_t r = 0; r < n_ignores; ++r) { rbitset_clear(birg->allocatable_regs, ignore_regs[r]); } if (!omit_fp) rbitset_clear(birg->allocatable_regs, REG_RBP); } return cconv; }