/* * This file is part of libFirm. * Copyright (C) 2018 Christoph Mallon. */ #include "riscv_transform.h" #include "becconv.h" #include "beirg.h" #include "benode.h" #include "betranshlp.h" #include "gen_riscv_new_nodes.h" #include "gen_riscv_regalloc_if.h" #include "irprog_t.h" #include "nodes.h" #include "riscv_bearch_t.h" #include "riscv_cconv.h" #include "util.h" static riscv_calling_convention_t cur_cconv; static be_stack_env_t stack_env; static const unsigned ignore_regs[] = { REG_T0, }; static unsigned const regs_param_gp[] = { REG_A0, REG_A1, REG_A2, REG_A3, REG_A4, REG_A5, REG_A6, REG_A7, }; static ir_node *initial_va_list; static unsigned const callee_saves[] = { REG_S0, REG_S1, REG_S2, REG_S3, REG_S4, REG_S5, REG_S6, REG_S7, REG_S8, REG_S9, REG_S10, REG_S11, }; static unsigned const caller_saves[] = { REG_RA, REG_T1, REG_T2, REG_A0, REG_A1, REG_A2, REG_A3, REG_A4, REG_A5, REG_A6, REG_A7, REG_T3, REG_T4, REG_T5, REG_T6, }; static ir_node *get_Start_sp(ir_graph *const irg) { return be_get_Start_proj(irg, &riscv_registers[REG_SP]); } static ir_node *get_Start_fp(ir_graph *const irg) { return be_get_Start_proj(irg, &riscv_registers[REG_S0]); } ir_node *get_Start_zero(ir_graph *const irg) { return be_get_Start_proj(irg, &riscv_registers[REG_ZERO]); } typedef struct riscv_addr { ir_node *base; ir_entity *ent; int32_t val; } riscv_addr; static riscv_addr make_addr(ir_node *addr) { ir_entity *ent = 0; int32_t val = 0; if (is_Add(addr)) { ir_node *const r = get_Add_right(addr); if (is_Const(r)) { long const v = get_Const_long(r); if (is_simm12(v)) { val = v; addr = get_Add_left(addr); } } } if (is_Member(addr)) { ent = get_Member_entity(addr); addr = get_Member_ptr(addr); assert(is_Proj(addr) && get_Proj_num(addr) == pn_Start_P_frame_base && is_Start(get_Proj_pred(addr))); } ir_node *const base = be_transform_node(addr); return (riscv_addr){ base, ent, val }; } static ir_node *make_address(ir_node const *const node, ir_entity *const ent, int32_t const val) { dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_node *const lui = new_bd_riscv_lui(dbgi, block, ent, val); return new_bd_riscv_addi(dbgi, block, lui, ent, val); } static ir_node *make_extension(dbg_info *const dbgi, ir_node *const op, unsigned const to_size) { ir_node *const new_op = be_transform_node(op); ir_mode *const op_mode = get_irn_mode(op); unsigned const op_size = get_mode_size_bits(op_mode); if (op_size >= to_size) return new_op; /* Check whether the value is correctly extended already. */ if (is_Proj(op)) { ir_node *const pred = get_Proj_pred(op); if (is_Load(pred)) return new_op; } assert(op_size <= 16); ir_node *const block = get_nodes_block(new_op); if (mode_is_signed(op_mode)) { int32_t const val = RISCV_MACHINE_SIZE - op_size; ir_node *const sll = new_bd_riscv_slli(dbgi, block, new_op, NULL, val); ir_node *const sra = new_bd_riscv_srai(dbgi, block, sll, NULL, val); return sra; } else if (op_size == 8) { return new_bd_riscv_andi(dbgi, block, new_op, NULL, (1U << op_size) - 1); } else { int32_t const val = RISCV_MACHINE_SIZE - op_size; ir_node *const sll = new_bd_riscv_slli(dbgi, block, new_op, NULL, val); ir_node *const srl = new_bd_riscv_srli(dbgi, block, sll, NULL, val); return srl; } } static ir_node *extend_value(ir_node *const val) { return make_extension(NULL, val, RISCV_MACHINE_SIZE); } static void riscv_parse_constraint_letter(void const *const env, be_asm_constraint_t* const c, char const l) { (void)env; switch (l) { case 'g': c->all_registers_allowed = true; c->memory_possible = true; /* FALLTHROUGH */ case 'I': case 'J': case 'K': case 'i': case 'n': c->cls = &riscv_reg_classes[CLASS_riscv_gp]; c->immediate_type = l; break; case 'm': c->memory_possible = true; break; case 'r': c->cls = &riscv_reg_classes[CLASS_riscv_gp]; c->all_registers_allowed = true; break; default: panic("unknown asm constraint '%c'", l); } } static bool riscv_check_immediate_constraint(long const val, char const imm_type) { switch (imm_type) { case 'I': return is_simm12(val); case 'J': return val == 0; case 'K': return is_uimm5(val); case 'g': case 'i': case 'n': return true; default: panic("invalid immediate constraint found"); } } static bool riscv_match_immediate(riscv_asm_operand_t *const operand, ir_node *const node, char const imm_type) { ir_tarval *offset; ir_entity *entity; unsigned reloc_kind; if (!be_match_immediate(node, &offset, &entity, &reloc_kind)) return false; assert(reloc_kind == 0); if (entity && imm_type != 'g' && imm_type != 'i') return false; long value; if (offset) { value = get_tarval_long(offset); if (!riscv_check_immediate_constraint(value, imm_type)) return false; } else { value = 0; } operand->val = value; operand->ent = entity; return true; } static ir_node *gen_ASM(ir_node *const node) { be_asm_info_t info = be_asm_prepare_info(node); ir_asm_constraint const *const constraints = get_ASM_constraints(node); size_t const n_constraints = get_ASM_n_constraints(node); ir_graph *const irg = get_irn_irg(node); struct obstack *const obst = get_irg_obstack(irg); riscv_asm_operand_t *const operands = NEW_ARR_DZ(riscv_asm_operand_t, obst, n_constraints); for (size_t i = 0; i != n_constraints; ++i) { ir_asm_constraint const *const c = &constraints[i]; be_asm_constraint_t be_constraint; be_parse_asm_constraints_internal(&be_constraint, c->constraint, &riscv_parse_constraint_letter, NULL); riscv_asm_operand_t *const op = &operands[i]; int const in_pos = c->in_pos; if (in_pos >= 0) { ir_node *const in = get_ASM_input(node, in_pos); char const imm = be_constraint.immediate_type; if (imm != '\0' && riscv_match_immediate(op, in, imm)) { be_asm_add_immediate(&op->op); } else if (be_constraint.same_as >= 0) { int const out_pos = operands[be_constraint.same_as].op.pos; arch_register_req_t const *const ireq = info.out_reqs[out_pos]; be_asm_add_inout(&info, &op->op, obst, in, ireq, out_pos); } else if (be_constraint.cls) { arch_register_req_t const *const ireq = be_make_register_req(obst, &be_constraint); be_asm_add_inout(&info, &op->op, obst, in, ireq, c->out_pos); } else { ir_node *const new_in = be_transform_node(in); arch_register_req_t const *const ireq = arch_get_irn_register_req(new_in)->cls->class_req; be_asm_add_in(&info, &op->op, BE_ASM_OPERAND_MEMORY, new_in, ireq); } } else { be_asm_add_out(&info, &op->op, obst, &be_constraint, c->out_pos); } } return be_make_asm(node, &info, operands); } static ir_node *gen_Add(ir_node *const node) { ir_tarval *tv; ir_entity *ent; unsigned reloc_kind; if (be_match_immediate(node, &tv, &ent, &reloc_kind)) { assert(reloc_kind == 0); int32_t const val = get_tarval_long(tv); return make_address(node, ent, val); } ir_node *const l = get_Add_left(node); ir_node *const r = get_Add_right(node); ir_mode *const mode = get_irn_mode(node); if (be_mode_needs_gp_reg(mode)) { dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_node *const new_l = be_transform_node(l); if (is_Const(r)) { long const val = get_Const_long(r); if (is_simm12(val)) return new_bd_riscv_addi(dbgi, block, new_l, NULL, val); } ir_node *const new_r = be_transform_node(r); return new_bd_riscv_add(dbgi, block, new_l, new_r); } TODO(node); } static ir_node *gen_Address(ir_node *const node) { ir_entity *const ent = get_Address_entity(node); return make_address(node, ent, 0); } static ir_node *gen_Alloc(ir_node *node) { dbg_info *dbgi = get_irn_dbg_info(node); ir_node *new_block = be_transform_nodes_block(node); ir_node *size = get_Alloc_size(node); ir_graph *irg = get_irn_irg(node); ir_node *stack_pred = be_get_Start_proj(irg, &riscv_registers[REG_SP]); ir_node *mem = get_Alloc_mem(node); ir_node *new_mem = be_transform_node(mem); ir_node *subsp; if (is_Const(size)) { long const sizel = get_Const_long(size); assert((sizel & ((1<Alloc"); } typedef ir_node *cons_binop(dbg_info*, ir_node*, ir_node*, ir_node*); typedef ir_node *cons_binop_imm(dbg_info*, ir_node*, ir_node*, ir_entity*, int32_t); static ir_node *gen_logic_op(ir_node *const node, cons_binop *const cons, cons_binop_imm *const cons_imm) { dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_node *const l = get_binop_left(node); ir_node *const new_l = be_transform_node(l); ir_node *const r = get_binop_right(node); if (is_Const(r)) { long const val = get_Const_long(r); if (is_simm12(val)) return cons_imm(dbgi, block, new_l, NULL, val); } ir_node *const new_r = be_transform_node(r); return cons(dbgi, block, new_l, new_r); } static ir_node *gen_And(ir_node *const node) { return gen_logic_op(node, &new_bd_riscv_and, &new_bd_riscv_andi); } static ir_node *gen_saturating_increment(ir_node *const node) { dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_node *const operand = be_transform_node(get_Builtin_param(node, 0)); assert(get_mode_size_bits(get_irn_mode(operand)) == 32); ir_node *const sltiu = new_bd_riscv_sltiu(dbgi, block, operand, NULL, -1); ir_node *const addu = new_bd_riscv_add( dbgi, block, operand, sltiu); return addu; } static ir_node *gen_va_start(ir_node *node) { if (initial_va_list == NULL) { dbg_info *dbgi = get_irn_dbg_info(node); ir_graph *irg = get_irn_irg(node); ir_node *block = get_irg_start_block(irg); ir_entity *entity = cur_cconv.va_start_addr; ir_node *frame = cur_cconv.omit_fp ? get_Start_sp(irg): get_Start_fp(irg); ir_node *ap = new_bd_riscv_FrameAddr(dbgi, block, frame, entity, 0); initial_va_list = ap; } return initial_va_list; } static ir_node *gen_Builtin(ir_node *const node) { ir_builtin_kind const kind = get_Builtin_kind(node); switch (kind) { case ir_bk_saturating_increment: return gen_saturating_increment(node); case ir_bk_bswap: case ir_bk_clz: case ir_bk_compare_swap: case ir_bk_ctz: case ir_bk_debugbreak: case ir_bk_ffs: case ir_bk_frame_address: case ir_bk_inport: case ir_bk_may_alias: case ir_bk_outport: case ir_bk_parity: case ir_bk_popcount: case ir_bk_prefetch: case ir_bk_return_address: case ir_bk_trap: case ir_bk_va_arg: TODO(node); case ir_bk_va_start: return gen_va_start(node); } panic("unexpected Builtin"); } static ir_node *gen_Call(ir_node *const node) { ir_graph *const irg = get_irn_irg(node); unsigned p = n_riscv_jal_first_argument; unsigned const n_params = get_Call_n_params(node); unsigned const n_ins = p + 1 + n_params; arch_register_req_t const **const reqs = be_allocate_in_reqs(irg, n_ins); ir_node *ins[n_ins]; ir_entity *callee; ir_node *const ptr = get_Call_ptr(node); if (is_Address(ptr)) { callee = get_Address_entity(ptr); } else { callee = NULL; ins[p] = be_transform_node(ptr); reqs[p] = &riscv_class_reg_req_gp; ++p; } ir_type *const fun_type = get_Call_type(node); record_returns_twice(irg, fun_type); unsigned named_params = get_method_n_params(fun_type); if (callee) { ir_type *const callee_type = get_entity_type(callee); if (is_Method_type(callee_type) && is_method_variadic(callee_type)) { named_params = get_method_variadic_index(callee_type); } } riscv_calling_convention_t cconv; riscv_determine_calling_convention(&cconv, fun_type, named_params, NULL); ir_node *mems[1 + cconv.n_mem_param]; unsigned m = 0; ir_node *const mem = get_Call_mem(node); mems[m++] = be_transform_node(mem); int const frame_size = cconv.param_stack_size; ir_node *const block = be_transform_nodes_block(node); ir_node *const sp = get_Start_sp(irg); ir_node *const call_frame = be_new_IncSP(block, sp, frame_size, 0); ins[n_riscv_jal_stack] = call_frame; reqs[n_riscv_jal_stack] = &riscv_single_reg_req_gp_sp; dbg_info *const dbgi = get_irn_dbg_info(node); for (size_t i = 0; i != n_params; ++i) { ir_node *const arg = get_Call_param(node, i); ir_node *const val = extend_value(arg); riscv_reg_or_slot_t const *const param = &cconv.parameters[i]; if (param->reg) { ins[p] = val; reqs[p] = param->reg->single_req; ++p; } else { ir_node *const nomem = get_irg_no_mem(irg); mems[m++] = new_bd_riscv_sw(dbgi, block, nomem, call_frame, val, NULL, param->offset); } } riscv_free_calling_convention(&cconv); ins[n_riscv_jal_mem] = be_make_Sync(block, m, mems); reqs[n_riscv_jal_mem] = arch_memory_req; unsigned const n_res = pn_riscv_jal_first_result + ARRAY_SIZE(caller_saves); ir_node *const jal = callee ? new_bd_riscv_jal( dbgi, block, p, ins, reqs, n_res, callee, 0) : new_bd_riscv_jalr(dbgi, block, p, ins, reqs, n_res); arch_set_irn_register_req_out(jal, pn_riscv_jal_M, arch_memory_req); arch_copy_irn_out_info(jal, pn_riscv_jal_stack, sp); for (size_t i = 0; i != ARRAY_SIZE(caller_saves); ++i) { arch_set_irn_register_req_out(jal, pn_riscv_jal_first_result + i, riscv_registers[caller_saves[i]].single_req); } ir_node *const jal_stack = be_new_Proj(jal, pn_riscv_jal_stack); ir_node *const new_stack = be_new_IncSP(block, jal_stack, -frame_size, 0); be_stack_record_chain(&stack_env, call_frame, n_be_IncSP_pred, new_stack); return jal; } static ir_node *gen_Cmp(ir_node *const node) { ir_node *l = get_Cmp_left(node); ir_node *r = get_Cmp_right(node); ir_mode *const mode = get_irn_mode(l); if (be_mode_needs_gp_reg(mode)) { ir_relation const rel = get_Cmp_relation(node) & ir_relation_less_equal_greater; switch (rel) { case ir_relation_greater: { ir_node *const t = l; l = r; r = t; } /* FALLTHROUGH */ case ir_relation_less: { dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_node *const new_l = extend_value(l); ir_node *const new_r = extend_value(r); if (mode_is_signed(mode)) { return new_bd_riscv_slt(dbgi, block, new_l, new_r); } else { return new_bd_riscv_sltu(dbgi, block, new_l, new_r); } } case ir_relation_equal: case ir_relation_false: case ir_relation_greater_equal: case ir_relation_less_equal: case ir_relation_less_equal_greater: case ir_relation_less_greater: case ir_relation_true: case ir_relation_unordered: case ir_relation_unordered_equal: case ir_relation_unordered_greater: case ir_relation_unordered_greater_equal: case ir_relation_unordered_less: case ir_relation_unordered_less_equal: case ir_relation_unordered_less_greater: panic("unexpected relation"); } } TODO(node); } static ir_node *gen_Cond(ir_node *const node) { ir_node *const sel = get_Cond_selector(node); if (is_Cmp(sel)) { ir_node *l = get_Cmp_left(sel); ir_mode *const mode = get_irn_mode(l); if (be_mode_needs_gp_reg(mode)) { ir_relation const rel = get_Cmp_relation(sel) & ir_relation_less_equal_greater; ir_node *r = get_Cmp_right(sel); switch (rel) { { riscv_cond_t cc; case ir_relation_equal: cc = riscv_cc_eq; goto bcc; case ir_relation_less_greater: cc = riscv_cc_ne; goto bcc; case ir_relation_greater: { ir_node *const t = l; l = r; r = t; } /* FALLTHROUGH */ case ir_relation_less: cc = mode_is_signed(mode) ? riscv_cc_lt : riscv_cc_ltu; goto bcc; case ir_relation_less_equal: { ir_node *const t = l; l = r; r = t; } /* FALLTHROUGH */ case ir_relation_greater_equal: cc = mode_is_signed(mode) ? riscv_cc_ge : riscv_cc_geu; goto bcc; bcc:; dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_node *const new_l = extend_value(l); ir_node *const new_r = extend_value(r); return new_bd_riscv_bcc(dbgi, block, new_l, new_r, cc); } case ir_relation_false: case ir_relation_less_equal_greater: case ir_relation_true: case ir_relation_unordered: case ir_relation_unordered_equal: case ir_relation_unordered_greater: case ir_relation_unordered_greater_equal: case ir_relation_unordered_less: case ir_relation_unordered_less_equal: case ir_relation_unordered_less_greater: panic("unexpected relation"); } } } TODO(node); } static ir_node *gen_Const(ir_node *const node) { ir_mode *const mode = get_irn_mode(node); if (be_mode_needs_gp_reg(mode)) { long const val = get_Const_long(node); if (val == 0) { ir_graph *const irg = get_irn_irg(node); return get_Start_zero(irg); } else { dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); riscv_hi_lo_imm imm = calc_hi_lo((int32_t)val); ir_node *res; if (imm.hi != 0) { res = new_bd_riscv_lui(dbgi, block, NULL, imm.hi); } else { ir_graph *const irg = get_irn_irg(node); res = get_Start_zero(irg); } if (imm.lo != 0) res = new_bd_riscv_addi(dbgi, block, res, NULL, imm.lo); return res; } } TODO(node); } static ir_node *gen_Conv(ir_node *const node) { ir_node *const op = get_Conv_op(node); ir_mode *const op_mode = get_irn_mode(op); ir_mode *const mode = get_irn_mode(node); if (be_mode_needs_gp_reg(op_mode) && be_mode_needs_gp_reg(mode)) { dbg_info *const dbgi = get_irn_dbg_info(node); return make_extension(dbgi, op, get_mode_size_bits(mode)); } TODO(node); } static ir_node *gen_Div(ir_node *const node) { ir_mode *const mode = get_Div_resmode(node); if (be_mode_needs_gp_reg(mode) && get_mode_size_bits(mode) == RISCV_MACHINE_SIZE) { dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_node *const l = be_transform_node(get_Div_left(node)); ir_node *const r = be_transform_node(get_Div_right(node)); if (mode_is_signed(mode)) { return new_bd_riscv_div(dbgi, block, l, r); } else { return new_bd_riscv_divu(dbgi, block, l, r); } } TODO(node); } static ir_node *gen_Eor(ir_node *const node) { return gen_logic_op(node, &new_bd_riscv_xor, &new_bd_riscv_xori); } static ir_node *gen_IJmp(ir_node *const node) { dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_node *const tgt = be_transform_node(get_IJmp_target(node)); return new_bd_riscv_ijmp(dbgi, block, tgt); } static ir_node *gen_Jmp(ir_node *const node) { dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); return new_bd_riscv_j(dbgi, block); } typedef ir_node *cons_loadop(dbg_info*, ir_node*, ir_node*, ir_node*, ir_entity*, int32_t); static ir_node *gen_Load(ir_node *const node) { ir_mode *const mode = get_Load_mode(node); if (be_mode_needs_gp_reg(mode)) { cons_loadop *cons; unsigned const size = get_mode_size_bits(mode); if (size == 8) { cons = mode_is_signed(mode) ? &new_bd_riscv_lb : &new_bd_riscv_lbu; } else if (size == 16) { cons = mode_is_signed(mode) ? &new_bd_riscv_lh : &new_bd_riscv_lhu; } else if (size == 32) { cons = new_bd_riscv_lw; } else { panic("invalid load"); } dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_node *const mem = be_transform_node(get_Load_mem(node)); riscv_addr const addr = make_addr(get_Load_ptr(node)); return cons(dbgi, block, mem, addr.base, addr.ent, addr.val); } TODO(node); } static ir_node *gen_Member(ir_node *const node) { ir_node *const ptr = get_Member_ptr(node); assert(is_Proj(ptr) && get_Proj_num(ptr) == pn_Start_P_frame_base && is_Start(get_Proj_pred(ptr))); dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_node *const frame = be_transform_node(ptr); ir_entity *const ent = get_Member_entity(node); return new_bd_riscv_FrameAddr(dbgi, block, frame, ent, 0); } static ir_node *gen_Minus(ir_node *const node) { ir_node *const val = get_Minus_op(node); ir_mode *const mode = get_irn_mode(node); if (be_mode_needs_gp_reg(mode)) { dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_graph *const irg = get_irn_irg(node); ir_node *const new_l = get_Start_zero(irg); ir_node *const new_r = be_transform_node(val); return new_bd_riscv_sub(dbgi, block, new_l, new_r); } TODO(node); } static ir_node *gen_Mod(ir_node *const node) { ir_mode *const mode = get_Mod_resmode(node); if (be_mode_needs_gp_reg(mode) && get_mode_size_bits(mode) == RISCV_MACHINE_SIZE) { dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_node *const l = be_transform_node(get_Mod_left(node)); ir_node *const r = be_transform_node(get_Mod_right(node)); if (mode_is_signed(mode)) { return new_bd_riscv_rem(dbgi, block, l, r); } else { return new_bd_riscv_remu(dbgi, block, l, r); } } TODO(node); } static ir_node *gen_Mul(ir_node *const node) { ir_mode *const mode = get_irn_mode(node); if (be_mode_needs_gp_reg(mode)) { dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_node *const l = be_transform_node(get_Mul_left(node)); ir_node *const r = be_transform_node(get_Mul_right(node)); return new_bd_riscv_mul(dbgi, block, l, r); } TODO(node); } static ir_node *gen_Mulh(ir_node *const node) { ir_mode *const mode = get_irn_mode(node); if (be_mode_needs_gp_reg(mode) && get_mode_size_bits(mode) == RISCV_MACHINE_SIZE) { dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_node *const l = be_transform_node(get_Mulh_left(node)); ir_node *const r = be_transform_node(get_Mulh_right(node)); if (mode_is_signed(mode)) { return new_bd_riscv_mulh(dbgi, block, l, r); } else { return new_bd_riscv_mulhu(dbgi, block, l, r); } } TODO(node); } static ir_node *gen_Mux(ir_node *const node) { ir_mode *const mode = get_irn_mode(node); if (be_mode_needs_gp_reg(mode)) { if (is_irn_null(get_Mux_false(node)) && is_irn_one(get_Mux_true(node))) { ir_node *const sel = get_Mux_sel(node); if (is_Cmp(sel)) { ir_relation const rel = get_Cmp_relation(sel) & ir_relation_less_equal_greater; if (rel == ir_relation_less || rel == ir_relation_greater) return be_transform_node(sel); } } } TODO(node); } static ir_node *gen_Not(ir_node *const node) { /* ~v -> xor(v, -1) */ dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_node *const old_val = get_Not_op(node); ir_node *const val = be_transform_node(old_val); return new_bd_riscv_xori(dbgi, block, val, NULL, -1); } static ir_node *gen_Or(ir_node *const node) { return gen_logic_op(node, &new_bd_riscv_or, &new_bd_riscv_ori); } static ir_node *gen_Phi(ir_node *const node) { arch_register_req_t const *req; ir_mode *const mode = get_irn_mode(node); if (be_mode_needs_gp_reg(mode)) { req = &riscv_class_reg_req_gp; } else if (mode == mode_M) { req = arch_memory_req; } else { panic("unhandled mode"); } return be_transform_phi(node, req); } static ir_node *gen_Proj_Builtin(ir_node *const node) { ir_node *const pred = get_Proj_pred(node); ir_node *const new_pred = be_transform_node(pred); ir_builtin_kind const kind = get_Builtin_kind(pred); unsigned const pn = get_Proj_num(node); switch (kind) { case ir_bk_saturating_increment: assert(get_Proj_num(node) == pn_Builtin_max + 1); return new_pred; case ir_bk_bswap: case ir_bk_clz: case ir_bk_compare_swap: case ir_bk_ctz: case ir_bk_debugbreak: case ir_bk_ffs: case ir_bk_frame_address: case ir_bk_inport: case ir_bk_may_alias: case ir_bk_outport: case ir_bk_parity: case ir_bk_popcount: case ir_bk_prefetch: case ir_bk_return_address: case ir_bk_trap: TODO(node); case ir_bk_va_arg: break; case ir_bk_va_start: if (pn == pn_Builtin_M) { return be_transform_node(get_Builtin_mem(pred)); } else { assert(pn == pn_Builtin_max+1); return new_pred; } } panic("unexpected Builtin"); } static ir_node *gen_Proj_Call(ir_node *const node) { ir_node *const pred = get_Proj_pred(node); ir_node *const call = be_transform_node(pred); unsigned const pn = get_Proj_num(node); switch ((pn_Call)pn) { case pn_Call_M: return be_new_Proj(call, pn_riscv_jal_M); case pn_Call_T_result: case pn_Call_X_regular: case pn_Call_X_except: break; } panic("unexpected Proj-Call"); } static ir_node *gen_Proj_Div(ir_node *const node) { ir_node *const pred = get_Proj_pred(node); unsigned const pn = get_Proj_num(node); switch ((pn_Div)pn) { case pn_Div_M: return get_Div_mem(pred); case pn_Div_res: return be_transform_node(pred); case pn_Div_X_regular: case pn_Div_X_except: break; } TODO(node); } static ir_node *gen_Proj_Load(ir_node *const node) { ir_node *const pred = get_Proj_pred(node); ir_node *const load = be_transform_node(pred); unsigned const pn = get_Proj_num(node); switch ((pn_Load)pn) { case pn_Load_M: return be_new_Proj(load, pn_riscv_lw_M); case pn_Load_res: return be_new_Proj(load, pn_riscv_lw_res); case pn_Load_X_regular: case pn_Load_X_except: break; } TODO(node); } static ir_node *gen_Proj_Mod(ir_node *const node) { ir_node *const pred = get_Proj_pred(node); unsigned const pn = get_Proj_num(node); switch ((pn_Mod)pn) { case pn_Mod_M: return get_Mod_mem(pred); case pn_Mod_res: return be_transform_node(pred); case pn_Mod_X_regular: case pn_Mod_X_except: break; } TODO(node); } static ir_node *gen_Proj_Proj_Call(ir_node *const node) { ir_node *const pred = get_Proj_pred(node); assert(get_Proj_num(pred) == pn_Call_T_result); ir_node *const ocall = get_Proj_pred(pred); ir_type *const fun_type = get_Call_type(ocall); riscv_calling_convention_t cconv; //get_method_n_params is not correct but irrelevant in this case riscv_determine_calling_convention(&cconv, fun_type, get_method_n_params(fun_type), NULL); ir_node *const call = be_transform_node(ocall); unsigned const num = get_Proj_num(node); arch_register_t const *const reg = cconv.results[num].reg; unsigned const pos = be_get_out_for_reg(call, reg); riscv_free_calling_convention(&cconv); return be_new_Proj(call, pos); } static ir_node *gen_Proj_Proj_Start(ir_node *const node) { assert(get_Proj_num(get_Proj_pred(node)) == pn_Start_T_args); ir_graph *const irg = get_irn_irg(node); unsigned const num = get_Proj_num(node); riscv_reg_or_slot_t *const param = &cur_cconv.parameters[num]; if (param->reg) { return be_get_Start_proj(irg, param->reg); } else { dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_node *const mem = be_get_Start_mem(irg); ir_node *const base = cur_cconv.omit_fp ? get_Start_sp(irg) : get_Start_fp(irg); ir_node *const load = new_bd_riscv_lw(dbgi, block, mem, base, param->entity, 0); return be_new_Proj(load, pn_riscv_lw_res); } } static ir_node *gen_Proj_Proj(ir_node *const node) { ir_node *const pred = get_Proj_pred(node); ir_node *const pred_pred = get_Proj_pred(pred); switch (get_irn_opcode(pred_pred)) { case iro_Call: return gen_Proj_Proj_Call(node); case iro_Start: return gen_Proj_Proj_Start(node); default: panic("unexpected Proj-Proj"); } } static ir_node *gen_Proj_Start(ir_node *const node) { ir_graph *const irg = get_irn_irg(node); switch ((pn_Start)get_Proj_num(node)) { case pn_Start_M: return be_get_Start_mem(irg); case pn_Start_P_frame_base: return cur_cconv.omit_fp ? get_Start_sp(irg): get_Start_fp(irg); case pn_Start_T_args: return new_r_Bad(irg, mode_T); } panic("unexpected Proj"); } static ir_node *gen_Proj_Store(ir_node *const node) { ir_node *const pred = get_Proj_pred(node); ir_node *const store = be_transform_node(pred); unsigned const pn = get_Proj_num(node); switch ((pn_Store)pn) { case pn_Store_M: return store; case pn_Store_X_regular: case pn_Store_X_except: break; } TODO(node); } static ir_node *gen_Return(ir_node *const node) { unsigned p = n_riscv_ret_first_result; unsigned const n_res = get_Return_n_ress(node); unsigned const n_ins = p + n_res + ARRAY_SIZE(callee_saves); ir_graph *const irg = get_irn_irg(node); arch_register_req_t const **const reqs = be_allocate_in_reqs(irg, n_ins); ir_node **const in = ALLOCAN(ir_node*, n_ins); ir_node *const mem = get_Return_mem(node); in[n_riscv_ret_mem] = be_transform_node(mem); reqs[n_riscv_ret_mem] = arch_memory_req; in[n_riscv_ret_stack] = get_Start_sp(irg); reqs[n_riscv_ret_stack] = &riscv_single_reg_req_gp_sp; in[n_riscv_ret_addr] = be_get_Start_proj(irg, &riscv_registers[REG_RA]); reqs[n_riscv_ret_addr] = &riscv_single_reg_req_gp_ra; riscv_reg_or_slot_t *const results = cur_cconv.results; for (size_t i = 0; i != n_res; ++i) { ir_node *const res = get_Return_res(node, i); in[p] = be_transform_node(res); reqs[p] = results[i].reg->single_req; ++p; } for (size_t i = 0; i != ARRAY_SIZE(callee_saves); ++i) { arch_register_t const *const reg = &riscv_registers[callee_saves[i]]; in[p] = be_get_Start_proj(irg, reg); reqs[p] = reg->single_req; ++p; } assert(p == n_ins); dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_node *const ret = new_bd_riscv_ret(dbgi, block, n_ins, in, reqs); be_stack_record_chain(&stack_env, ret, n_riscv_ret_stack, NULL); return ret; } static ir_node *gen_shift_op(ir_node *const node, cons_binop *const cons, cons_binop_imm *const cons_imm, bool needs_extension) { dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_node *const l = get_binop_left(node); ir_node *new_l = be_transform_node(l); ir_node *const r = get_binop_right(node); ir_mode *const mode = get_irn_mode(node); unsigned const size = get_mode_size_bits(mode); needs_extension = needs_extension && (size != RISCV_MACHINE_SIZE); int extend = RISCV_MACHINE_SIZE - size; /* right shift operations with mode sizes < 32 Bit require correct sign/zero extension. * This is done by first shifting the operand to the left by 32-size bits and then shifting back to the * right (arithmetic/logical) by the same amount. Finally the actual shift operation is performed. * If the shift amount is known (r is const), the two right shift operations are combined (if possible). */ if (needs_extension) { assert(is_uimm5(extend)); if (!mode_is_signed(mode) && size == 8) { new_l = new_bd_riscv_andi(dbgi, block, new_l, NULL, (1U << size) - 1); needs_extension = false; } else { new_l = new_bd_riscv_slli(dbgi, block, new_l, NULL, extend); } } if (is_Const(r)) { long val = get_Const_long(r); if (needs_extension && is_uimm5(extend + val)) { val = extend + val; needs_extension = false; } if (is_uimm5(val)) { if (needs_extension) { new_l = cons_imm(dbgi, block, new_l, NULL, extend); } return cons_imm(dbgi, block, new_l, NULL, val); } } if (needs_extension) { new_l = cons_imm(dbgi, block, new_l, NULL, extend); } ir_node *const new_r = be_transform_node(r); return cons(dbgi, block, new_l, new_r); } static ir_node *gen_Shl(ir_node *const node) { return gen_shift_op(node, &new_bd_riscv_sll, &new_bd_riscv_slli, false); } static ir_node *gen_Shr(ir_node *const node) { return gen_shift_op(node, &new_bd_riscv_srl, &new_bd_riscv_srli, true); } static ir_node *gen_Shrs(ir_node *const node) { return gen_shift_op(node, &new_bd_riscv_sra, &new_bd_riscv_srai, true); } static ir_node *gen_Start(ir_node *const node) { be_start_out outs[N_RISCV_REGISTERS] = { [REG_ZERO] = BE_START_IGNORE, [REG_RA] = BE_START_REG, [REG_SP] = BE_START_IGNORE, }; for (size_t i = 0; i != ARRAY_SIZE(callee_saves); ++i) { outs[callee_saves[i]] = BE_START_REG; } ir_graph *const irg = get_irn_irg(node); ir_entity *const ent = get_irg_entity(irg); ir_type *const type = get_entity_type(ent); for (size_t i = 0, n = get_method_n_params(type); i != n; ++i) { arch_register_t const *const reg = cur_cconv.parameters[i].reg; if (reg) outs[reg->global_index] = BE_START_REG; } if (!cur_cconv.omit_fp) outs[REG_S0] = BE_START_IGNORE; return be_new_Start(irg, outs); } typedef ir_node *cons_storeop(dbg_info*, ir_node*, ir_node*, ir_node*, ir_node*, ir_entity*, int32_t); static ir_node *gen_Store(ir_node *const node) { ir_node *old_val = get_Store_value(node); ir_mode *const mode = get_irn_mode(old_val); if (be_mode_needs_gp_reg(mode)) { cons_storeop *cons; unsigned const size = get_mode_size_bits(mode); if (size == 8) { cons = &new_bd_riscv_sb; } else if (size == 16) { cons = &new_bd_riscv_sh; } else if (size == 32) { cons = &new_bd_riscv_sw; } else { panic("invalid store"); } old_val = be_skip_downconv(old_val, false); dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_node *const mem = be_transform_node(get_Store_mem(node)); ir_node *const val = be_transform_node(old_val); riscv_addr const addr = make_addr(get_Store_ptr(node)); ir_node *const store = cons(dbgi, block, mem, addr.base, val, addr.ent, addr.val); return store; } TODO(node); } static ir_node *gen_Sub(ir_node *const node) { ir_node *const l = get_Sub_left(node); ir_node *const r = get_Sub_right(node); ir_mode *const mode = get_irn_mode(node); if (be_mode_needs_gp_reg(mode)) { dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_node *const new_l = be_transform_node(l); ir_node *const new_r = be_transform_node(r); return new_bd_riscv_sub(dbgi, block, new_l, new_r); } TODO(node); } static ir_node *gen_Switch(ir_node *const node) { ir_graph *const irg = get_irn_irg(node); ir_switch_table const *const table = ir_switch_table_duplicate(irg, get_Switch_table(node)); ir_type *const utype = get_unknown_type(); ident *const id = id_unique("TBL"); ir_entity *const entity = new_global_entity(irp->dummy_owner, id, utype, ir_visibility_private, IR_LINKAGE_CONSTANT | IR_LINKAGE_NO_IDENTITY); dbg_info *const dbgi = get_irn_dbg_info(node); ir_node *const block = be_transform_nodes_block(node); ir_node *const nomem = get_irg_no_mem(irg); ir_node *const sel = be_transform_node(get_Switch_selector(node)); ir_node *const sll = new_bd_riscv_slli(dbgi, block, sel, NULL, 2); ir_node *const lui = new_bd_riscv_lui(dbgi, block, entity, 0); ir_node *const add = new_bd_riscv_add(dbgi, block, sll, lui); ir_node *const load = new_bd_riscv_lw(dbgi, block, nomem, add, entity, 0); ir_node *const res = be_new_Proj(load, pn_riscv_lw_res); unsigned const n_outs = get_Switch_n_outs(node); return new_bd_riscv_switch(dbgi, block, res, n_outs, table, entity); } static ir_node *gen_Unknown(ir_node *const node) { ir_node *const block = be_transform_nodes_block(node); ir_mode *const mode = get_irn_mode(node); if (be_mode_needs_gp_reg(mode)) { return be_new_Unknown(block, &riscv_class_reg_req_gp); } else { TODO(node); } } static void riscv_register_transformers(void) { be_start_transform_setup(); be_set_transform_function(op_ASM, gen_ASM); be_set_transform_function(op_Add, gen_Add); be_set_transform_function(op_Address, gen_Address); be_set_transform_function(op_Alloc, gen_Alloc); be_set_transform_function(op_And, gen_And); be_set_transform_function(op_Builtin, gen_Builtin); be_set_transform_function(op_Call, gen_Call); be_set_transform_function(op_Cmp, gen_Cmp); be_set_transform_function(op_Cond, gen_Cond); be_set_transform_function(op_Const, gen_Const); be_set_transform_function(op_Conv, gen_Conv); be_set_transform_function(op_Div, gen_Div); be_set_transform_function(op_Eor, gen_Eor); be_set_transform_function(op_IJmp, gen_IJmp); be_set_transform_function(op_Jmp, gen_Jmp); be_set_transform_function(op_Load, gen_Load); be_set_transform_function(op_Member, gen_Member); be_set_transform_function(op_Minus, gen_Minus); be_set_transform_function(op_Mod, gen_Mod); be_set_transform_function(op_Mul, gen_Mul); be_set_transform_function(op_Mulh, gen_Mulh); be_set_transform_function(op_Mux, gen_Mux); be_set_transform_function(op_Not, gen_Not); be_set_transform_function(op_Or, gen_Or); be_set_transform_function(op_Phi, gen_Phi); be_set_transform_function(op_Return, gen_Return); be_set_transform_function(op_Shl, gen_Shl); be_set_transform_function(op_Shr, gen_Shr); be_set_transform_function(op_Shrs, gen_Shrs); be_set_transform_function(op_Start, gen_Start); be_set_transform_function(op_Store, gen_Store); be_set_transform_function(op_Sub, gen_Sub); be_set_transform_function(op_Switch, gen_Switch); be_set_transform_function(op_Unknown, gen_Unknown); be_set_transform_proj_function(op_Alloc, gen_Proj_Alloc); be_set_transform_proj_function(op_Builtin, gen_Proj_Builtin); be_set_transform_proj_function(op_Call, gen_Proj_Call); be_set_transform_proj_function(op_Div, gen_Proj_Div); be_set_transform_proj_function(op_Load, gen_Proj_Load); be_set_transform_proj_function(op_Mod, gen_Proj_Mod); be_set_transform_proj_function(op_Proj, gen_Proj_Proj); be_set_transform_proj_function(op_Start, gen_Proj_Start); be_set_transform_proj_function(op_Store, gen_Proj_Store); } static bool riscv_variadic_fixups(ir_graph *const irg) { ir_entity *entity = get_irg_entity(irg); ir_type *mtp = get_entity_type(entity); if (!is_method_variadic(mtp)) return false; size_t const n_params = get_method_n_params(mtp); if (n_params >= ARRAY_SIZE(regs_param_gp)) return false; size_t const n_ress = get_method_n_ress(mtp); size_t const new_n_params = ARRAY_SIZE(regs_param_gp); 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); // preserve information about the number of named parameters set_method_variadic_index(new_mtp, get_method_variadic_index(mtp)); 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 = riscv_reg_classes[CLASS_riscv_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; } static void riscv_set_allocatable_regs(ir_graph *const irg) { be_irg_t *const birg = be_birg_from_irg(irg); struct obstack *const obst = &birg->obst; unsigned *const a = rbitset_obstack_alloc(obst, N_RISCV_REGISTERS); for (size_t i = 0; i != ARRAY_SIZE(callee_saves); ++i) { rbitset_set(a, callee_saves[i]); } for (size_t i = 0; i != ARRAY_SIZE(caller_saves); ++i) { rbitset_set(a, caller_saves[i]); } birg->allocatable_regs = a; be_cconv_rem_regs(birg->allocatable_regs, ignore_regs, ARRAY_SIZE(ignore_regs)); } void riscv_transform_graph(ir_graph *const irg) { riscv_register_transformers(); riscv_set_allocatable_regs(irg); be_stack_init(&stack_env); ir_entity *const fun_ent = get_irg_entity(irg); ir_type *fun_type = get_entity_type(fun_ent); unsigned named_params = is_method_variadic(fun_type) ? (unsigned) get_method_variadic_index(fun_type) : get_method_n_params(fun_type); if(riscv_variadic_fixups(irg)) { fun_type = get_entity_type(fun_ent); } riscv_determine_calling_convention(&cur_cconv, fun_type, named_params, irg); riscv_layout_parameter_entities(&cur_cconv, irg); ir_entity *need_stores[ARRAY_SIZE(regs_param_gp)]; unsigned n_stores = 0; ir_entity *entity; if (is_method_variadic(fun_type)) { for (size_t i = 0; i < ARRAY_SIZE(regs_param_gp); ++i) { riscv_reg_or_slot_t const *const param = &cur_cconv.parameters[i]; entity = param->entity; if (entity == NULL) continue; assert(n_stores < ARRAY_SIZE(regs_param_gp)); need_stores[n_stores++] = entity; } be_add_parameter_entity_stores_list(irg, n_stores, need_stores); //TODO optimize? } else { be_add_parameter_entity_stores(irg); } be_transform_graph(irg, NULL); riscv_free_calling_convention(&cur_cconv); be_stack_finish(&stack_env); initial_va_list = NULL; }