/* * This file is part of libFirm. * Copyright (C) 2018 Christoph Mallon. */ #include "be_t.h" #include "becconv.h" #include "betranshlp.h" #include "bevarargs.h" #include "gen_riscv_regalloc_if.h" #include "irgwalk.h" #include "riscv_bearch_t.h" #include "riscv_cconv.h" #include "util.h" static unsigned const regs_param_gp[] = { REG_A0, REG_A1, REG_A2, REG_A3, REG_A4, REG_A5, REG_A6, REG_A7, }; static unsigned const regs_result_gp[] = { REG_A0, REG_A1, }; static void check_omit_fp(ir_node *node, void *env) { /* omit-fp is not possible if: * - we have allocations on the stack */ if (is_Alloc(node) || is_Free(node)) { bool *can_omit_fp = (bool*) env; *can_omit_fp = false; } } void riscv_determine_calling_convention(riscv_calling_convention_t *const cconv, ir_type *const fun_type, unsigned named_parameters, ir_graph *const irg) { //int offset = is_method_variadic(fun_type) ? 0 : ARRAY_SIZE(regs_param_gp); int offset = ARRAY_SIZE(regs_param_gp); 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); riscv_get_irg_data(irg)->omit_fp = omit_fp; } /* Handle parameters. */ riscv_reg_or_slot_t *params = NULL; size_t gp_param = 0; size_t const n_params = get_method_n_params(fun_type); assert(is_method_variadic(fun_type) || named_parameters == n_params); if (n_params != 0) { params = XMALLOCNZ(riscv_reg_or_slot_t, n_params); for (size_t i = 0; i != n_params; ++i) { ir_type *const param_type = get_method_param_type(fun_type, i); ir_mode *const param_mode = get_type_mode(param_type); if (!param_mode) { panic("TODO"); } else if (mode_is_float(param_mode)) { panic("TODO"); } else { if (i >= named_parameters && param_type->flags & tf_lowered_dw && gp_param % 2 != 0) { // variadic arguments with 2 x XLEN size have to be passed in an aligned register pair / stack slots ++gp_param; } if (gp_param < ARRAY_SIZE(regs_param_gp)) params[i].reg = &riscv_registers[regs_param_gp[gp_param]]; params[i].offset = (gp_param - offset) * RISCV_REGISTER_SIZE; ++gp_param; } } } unsigned n_mem_param = gp_param > ARRAY_SIZE(regs_param_gp) ? gp_param - offset : 0; cconv->param_stack_size = n_mem_param * RISCV_REGISTER_SIZE; cconv->n_mem_param = n_mem_param; cconv->parameters = params; cconv->va_arg_first_slot = named_parameters; /* Handle results. */ riscv_reg_or_slot_t *results = NULL; size_t const n_results = get_method_n_ress(fun_type); if (n_results != 0) { results = XMALLOCNZ(riscv_reg_or_slot_t, n_results); size_t gp_res = 0; for (size_t i = 0; i != n_results; ++i) { ir_type *const res_type = get_method_res_type(fun_type, i); ir_mode *const res_mode = get_type_mode(res_type); if (!res_mode) { panic("TODO"); } else if (mode_is_float(res_mode)) { panic("TODO"); } else { if (gp_res == ARRAY_SIZE(regs_result_gp)) panic("too many gp results"); results[i].reg = &riscv_registers[regs_result_gp[gp_res++]]; } } } cconv->results = results; if (irg != NULL) { be_irg_t *birg = be_birg_from_irg(irg); if (!omit_fp) rbitset_clear(birg->allocatable_regs, REG_S0); } cconv->omit_fp = omit_fp; } void riscv_layout_parameter_entities(riscv_calling_convention_t *const cconv, ir_graph *const irg) { ir_entity **const param_map = be_collect_parameter_entities(irg); ir_type *const frame_type = get_irg_frame_type(irg); ir_entity *const fun_ent = get_irg_entity(irg); ir_type *const fun_type = get_entity_type(fun_ent); size_t const n_params = get_method_n_params(fun_type); for (size_t i = 0; i != n_params; ++i) { riscv_reg_or_slot_t *const param = &cconv->parameters[i]; ir_type *const param_type = get_method_param_type(fun_type, i); if (!is_atomic_type(param_type)) panic("unhandled parameter type"); ir_entity *param_ent = param_map[i]; if (!param->reg || is_method_variadic(fun_type)) { if (!param_ent) param_ent = new_parameter_entity(frame_type, i, param_type); assert(get_entity_offset(param_ent) == INVALID_OFFSET); set_entity_offset(param_ent, param->offset); } param->entity = param_ent; } free(param_map); if (is_method_variadic(fun_type)) { int const offset = (cconv->va_arg_first_slot - RISCV_N_PARAM_REGS) * RISCV_REGISTER_SIZE; cconv->va_start_addr = be_make_va_start_entity(frame_type, offset); } } void riscv_free_calling_convention(riscv_calling_convention_t *const cconv) { free(cconv->parameters); free(cconv->results); }