From: Michael Beck Date: Mon, 27 Mar 2006 16:08:13 +0000 (+0000) Subject: added mips backend from the backend praktikum X-Git-Url: http://nsz.repo.hu/git/?a=commitdiff_plain;h=18dfdacb6faf2f93d4f6f9a6362584f8c7eb30a1;p=libfirm added mips backend from the backend praktikum --- diff --git a/ir/be/mips/bearch_mips.c b/ir/be/mips/bearch_mips.c new file mode 100644 index 000000000..8c3e0072f --- /dev/null +++ b/ir/be/mips/bearch_mips.c @@ -0,0 +1,928 @@ +/* The main mips backend driver file. */ +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pseudo_irg.h" +#include "irgwalk.h" +#include "irprog.h" +#include "irprintf.h" +#include "ircons.h" +#include "irgmod.h" +#include "irgopt.h" +#include "irgwalk.h" +#include "iredges.h " +#include "irdump.h" +#include "irextbb.h" + +#include "bitset.h" +#include "debug.h" + +#include "../bearch.h" /* the general register allocator interface */ +#include "../benode_t.h" +#include "../belower.h" +#include "../besched_t.h" +#include "../be.h" +#include "../beabi.h" + +#include "bearch_mips_t.h" + +#include "mips_new_nodes.h" /* mips nodes interface */ +#include "gen_mips_regalloc_if.h" /* the generated interface (register type and class defenitions) */ +#include "mips_gen_decls.h" /* interface declaration emitter */ +#include "mips_transform.h" +#include "mips_emitter.h" +#include "mips_map_regs.h" +#include "mips_util.h" +#include "mips_scheduler.h" + +#define DEBUG_MODULE "firm.be.mips.isa" + +/* TODO: ugly, but we need it to get access to the registers assigned to Phi nodes */ +static set *cur_reg_set = NULL; + +/************************************************** + * _ _ _ __ + * | | | (_)/ _| + * _ __ ___ __ _ __ _| | | ___ ___ _| |_ + * | '__/ _ \/ _` | / _` | | |/ _ \ / __| | | _| + * | | | __/ (_| | | (_| | | | (_) | (__ | | | + * |_| \___|\__, | \__,_|_|_|\___/ \___| |_|_| + * __/ | + * |___/ + **************************************************/ + +static ir_node *my_skip_proj(const ir_node *n) { + while (is_Proj(n)) + n = get_Proj_pred(n); + return (ir_node *)n; +} + +/** + * Return register requirements for a mips node. + * If the node returns a tuple (mode_T) then the proj's + * will be asked for this information. + */ +static const arch_register_req_t *mips_get_irn_reg_req(const void *self, arch_register_req_t *req, const ir_node *irn, int pos) { + const mips_register_req_t *irn_req; + long node_pos = pos == -1 ? 0 : pos; + ir_mode *mode = get_irn_mode(irn); + firm_dbg_module_t *mod = firm_dbg_register(DEBUG_MODULE); + + if (is_Block(irn) || mode == mode_X || mode == mode_M) { + DBG((mod, LEVEL_1, "ignoring mode_T, mode_M node %+F\n", irn)); + return NULL; + } + + if (mode == mode_T && pos < 0) { + DBG((mod, LEVEL_1, "ignoring request for OUT requirements at %+F\n", irn)); + return NULL; + } + + DBG((mod, LEVEL_1, "get requirements at pos %d for %+F ... ", pos, irn)); + + if (is_Proj(irn)) { + /* in case of a proj, we need to get the correct OUT slot */ + /* of the node corresponding to the proj number */ + if (pos == -1) { + node_pos = mips_translate_proj_pos(irn); + } + else { + node_pos = pos; + } + + irn = my_skip_proj(irn); + + DB((mod, LEVEL_1, "skipping Proj, going to %+F at pos %d ... ", irn, node_pos)); + } + + /* get requirements for our own nodes */ + if (is_mips_irn(irn)) { + if (pos >= 0) { + irn_req = get_mips_in_req(irn, pos); + } + else { + irn_req = get_mips_out_req(irn, node_pos); + } + + DB((mod, LEVEL_1, "returning reqs for %+F at pos %d\n", irn, pos)); + + memcpy(req, &(irn_req->req), sizeof(*req)); + + if (arch_register_req_is(&(irn_req->req), should_be_same)) { + assert(irn_req->same_pos >= 0 && "should be same constraint for in -> out NYI"); + req->other_same = get_irn_n(irn, irn_req->same_pos); + } + + if (arch_register_req_is(&(irn_req->req), should_be_different)) { + assert(irn_req->different_pos >= 0 && "should be different constraint for in -> out NYI"); + req->other_different = get_irn_n(irn, irn_req->different_pos); + } + } + /* get requirements for FIRM nodes */ + else { + /* treat Phi like Const with default requirements */ + if (is_Phi(irn)) { + DB((mod, LEVEL_1, "returning standard reqs for %+F\n", irn)); + + if (mode_is_float(mode)) { + //memcpy(req, &(mips_default_req_mips_floating_point.req), sizeof(*req)); + assert(0 && "floating point not supported (yet)"); + } + else if (mode_is_int(mode) || mode_is_reference(mode)) { + memcpy(req, &(mips_default_req_mips_general_purpose.req), sizeof(*req)); + } + else if (mode == mode_T || mode == mode_M) { + DBG((mod, LEVEL_1, "ignoring Phi node %+F\n", irn)); + return NULL; + } + else { + assert(0 && "unsupported Phi-Mode"); + } + } + else { + DB((mod, LEVEL_1, "returning NULL for %+F (node not supported)\n", irn)); + req = NULL; + } + } + + return req; +} + +static void mips_set_irn_reg(const void *self, ir_node *irn, const arch_register_t *reg) { + int pos = 0; + + if (is_Proj(irn)) { + + if (get_irn_mode(irn) == mode_X) { + return; + } + + pos = mips_translate_proj_pos(irn); + irn = my_skip_proj(irn); + } + + if (is_mips_irn(irn)) { + const arch_register_t **slots; + + slots = get_mips_slots(irn); + slots[pos] = reg; + } + else { + /* here we set the registers for the Phi nodes */ + mips_set_firm_reg(irn, reg, cur_reg_set); + } +} + +static const arch_register_t *mips_get_irn_reg(const void *self, const ir_node *irn) { + int pos = 0; + const arch_register_t *reg = NULL; + + if (is_Proj(irn)) { + + if (get_irn_mode(irn) == mode_X) { + return NULL; + } + + pos = mips_translate_proj_pos(irn); + irn = my_skip_proj(irn); + } + + if (is_mips_irn(irn)) { + const arch_register_t **slots; + slots = get_mips_slots(irn); + reg = slots[pos]; + } + else { + reg = mips_get_firm_reg(irn, cur_reg_set); + } + + return reg; +} + +static arch_irn_class_t mips_classify(const void *self, const ir_node *irn) { + irn = my_skip_proj(irn); + + if (is_cfop(irn)) { + return arch_irn_class_branch; + } else if (is_mips_irn(irn)) { + return arch_irn_class_normal; + } + + return 0; +} + +static arch_irn_flags_t mips_get_flags(const void *self, const ir_node *irn) { + irn = my_skip_proj(irn); + + if (is_mips_irn(irn)) { + return get_mips_flags(irn); + } + else if (is_Unknown(irn)) { + return arch_irn_flags_ignore; + } + + return 0; +} + +static entity *mips_get_frame_entity(const void *self, const ir_node *irn) { + if(is_mips_load_r(irn) || is_mips_store_r(irn)) { + mips_attr_t *attr = get_mips_attr(irn); + + return attr->stack_entity; + } + + return NULL; +} + +/** + * This function is called by the generic backend to correct offsets for + * nodes accessing the stack. + */ +static void mips_set_frame_offset(const void *self, ir_node *irn, int offset) { + mips_attr_t *attr = get_mips_attr(irn); + assert(is_mips_load_r(irn) || is_mips_store_r(irn)); + + attr->stack_entity_offset = offset; +} + +/* fill register allocator interface */ + +static const arch_irn_ops_if_t mips_irn_ops_if = { + mips_get_irn_reg_req, + mips_set_irn_reg, + mips_get_irn_reg, + mips_classify, + mips_get_flags, + mips_get_frame_entity, + mips_set_frame_offset +}; + +mips_irn_ops_t mips_irn_ops = { + &mips_irn_ops_if, + NULL +}; + + + +/************************************************** + * _ _ __ + * | | (_)/ _| + * ___ ___ __| | ___ __ _ ___ _ __ _| |_ + * / __/ _ \ / _` |/ _ \/ _` |/ _ \ '_ \ | | _| + * | (_| (_) | (_| | __/ (_| | __/ | | | | | | + * \___\___/ \__,_|\___|\__, |\___|_| |_| |_|_| + * __/ | + * |___/ + **************************************************/ + + +typedef struct { + ir_node *start; + ir_node *end; + unsigned cnt; +} anchor; + +/** + * Ext-Block walker: create a block schedule + */ +static void create_block_list(ir_extblk *blk, void *env) { + anchor *list = env; + int i, n; + + for (i = 0, n = get_extbb_n_blocks(blk); i < n; ++i) { + ir_node *block = get_extbb_block(blk, i); + + set_irn_link(block, NULL); + if (list->start) + set_irn_link(list->end, block); + else + list->start = block; + + list->end = block; + list->cnt += 1; + } +} + +/* return the scheduled block at position pos */ +ir_node *mips_get_sched_block(const mips_code_gen_t *cg, int pos) { + if (0 <= pos && pos < ARR_LEN(cg->bl_list)) + return cg->bl_list[pos]; + return NULL; +} + +/* return the number of scheduled blocks */ +int mips_get_sched_n_blocks(const mips_code_gen_t *cg) { + return ARR_LEN(cg->bl_list); +} + +/* set a block schedule number */ +void mips_set_block_sched_nr(ir_node *block, int nr) { + set_irn_link(block, INT_TO_PTR(nr)); +} + +/* get a block schedule number */ +int mips_get_block_sched_nr(ir_node *block) { + return PTR_TO_INT(get_irn_link(block)); +} + +/** + * Creates a block schedule for the given graph. + */ +static void mips_create_block_sched(mips_code_gen_t *cg) { + anchor list; + ir_node **bl_list, *block; + unsigned i; + + if (cg->bl_list) { + DEL_ARR_F(cg->bl_list); + free_survive_dce(cg->bl_list_sdce); + } + + /* calculate the block schedule here */ + compute_extbb(cg->irg); + + list.start = NULL; + list.end = NULL; + list.cnt = 0; + irg_extblock_walk_graph(cg->irg, NULL, create_block_list, &list); + + + bl_list = NEW_ARR_F(ir_node *, list.cnt); + cg->bl_list_sdce = new_survive_dce(); + for (i = 0, block = list.start; block; block = get_irn_link(block)) { + bl_list[i] = block; + survive_dce_register_irn(cg->bl_list_sdce, &bl_list[i]); + i++; + } + + cg->bl_list = bl_list; +} + +typedef struct _wenv_t { + ir_node *list; +} wenv_t; + +/** + * Walker: link all CopyB nodes + */ +static void collect_copyb_nodes(ir_node *node, void *env) { + wenv_t *wenv = env; + + if (get_irn_op(node) == op_CopyB) { + set_irn_link(node, wenv->list); + wenv->list = node; + } +} + +static void replace_copyb_nodes(mips_code_gen_t *cg) { + wenv_t env; + ir_node *copy, *next; + ir_node *old_bl, *new_bl, *jmp, *new_jmp, *mem; + const ir_edge_t *edge; + + /* build code for all copyB */ + env.list = NULL; + irg_walk_graph(cg->irg, NULL, collect_copyb_nodes, &env); + + for (copy = env.list; copy; copy = next) { + next = get_irn_link(copy); + + old_bl = get_nodes_block(copy); + part_block(copy); + jmp = get_Block_cfgpred(old_bl, 0); + new_jmp = new_r_Jmp(cg->irg, get_nodes_block(copy)); + + new_bl = new_r_Block(cg->irg, 1, &new_jmp); + set_nodes_block(jmp, new_bl); + + mem = gen_code_for_CopyB(new_bl, copy); + + /* fix copyB's out edges */ + foreach_out_edge(copy, edge) { + ir_node *succ = get_edge_src_irn(edge); + + assert(is_Proj(succ)); + switch (get_Proj_proj(succ)) { + case pn_CopyB_M_regular: + case pn_CopyB_M_except: + exchange(succ, mem); + break; + default: + exchange(succ, get_irg_bad(cg->irg)); + } + } + } +} + +/** + * Transforms the standard firm graph into + * a mips firm graph + */ +static void mips_prepare_graph(void *self) { + mips_code_gen_t *cg = self; + int bl_nr, n; + + // replace all copyb nodes in the block with a loop + // and mips store/load nodes + replace_copyb_nodes(cg); + + // Calculate block schedule + mips_create_block_sched(cg); + + /* enter the block number into every blocks link field */ + for (bl_nr = 0, n = mips_get_sched_n_blocks(cg); bl_nr < n; ++bl_nr) { + ir_node *bl = mips_get_sched_block(cg, bl_nr); + mips_set_block_sched_nr(bl, bl_nr); + } + + // walk the graph and transform firm nodes into mips nodes where possible + irg_walk_blkwise_graph(cg->irg, mips_pre_transform_node, mips_transform_node, cg); + + dump_ir_block_graph_sched(cg->irg, "-transformed"); +} + +/** + * Called immediately before emit phase. + */ +static void mips_finish_irg(ir_graph *irg, mips_code_gen_t *cg) { + /* TODO: - fix offsets for nodes accessing stack + - ... + */ +} + + +/** + * These are some hooks which must be filled but are probably not needed. + */ +static void mips_before_sched(void *self) { + /* Some stuff you need to do after scheduling but before register allocation */ +} + +static void mips_before_ra(void *self) { + /* Some stuff you need to do immediately after register allocation */ +} + +static void mips_after_ra(void* self) { + mips_code_gen_t *cg = self; + irg_walk_blkwise_graph(cg->irg, NULL, mips_after_ra_walker, self); +} + +/** + * Emits the code, closes the output file and frees + * the code generator interface. + */ +static void mips_emit_and_done(void *self) { + mips_code_gen_t *cg = self; + ir_graph *irg = cg->irg; + FILE *out = cg->out; + + mips_register_emitters(); + + if (cg->emit_decls) { + mips_gen_decls(cg->out); + cg->emit_decls = 0; + } + + mips_finish_irg(irg, cg); + dump_ir_block_graph_sched(irg, "-mips-finished"); + mips_gen_routine(out, irg, cg); + + cur_reg_set = NULL; + + /* de-allocate code generator */ + del_set(cg->reg_set); + if (cg->bl_list) { + DEL_ARR_F(cg->bl_list); + free_survive_dce(cg->bl_list_sdce); + } + free(cg); +} + +static void *mips_cg_init(FILE *F, const be_irg_t *birg); + +static const arch_code_generator_if_t mips_code_gen_if = { + mips_cg_init, + NULL, /* before abi introduce */ + mips_prepare_graph, + mips_before_sched, /* before scheduling hook */ + mips_before_ra, /* before register allocation hook */ + mips_after_ra, + mips_emit_and_done +}; + +/** + * Initializes the code generator. + */ +static void *mips_cg_init(FILE *F, const be_irg_t *birg) { + mips_isa_t *isa = (mips_isa_t *)birg->main_env->arch_env->isa; + mips_code_gen_t *cg = xmalloc(sizeof(*cg)); + + cg->impl = &mips_code_gen_if; + cg->irg = birg->irg; + cg->reg_set = new_set(mips_cmp_irn_reg_assoc, 1024); + cg->mod = firm_dbg_register("firm.be.mips.cg"); + cg->out = F; + cg->arch_env = birg->main_env->arch_env; + cg->birg = birg; + cg->bl_list = NULL; + + isa->num_codegens++; + + if (isa->num_codegens > 1) + cg->emit_decls = 0; + else + cg->emit_decls = 1; + + cur_reg_set = cg->reg_set; + + mips_irn_ops.cg = cg; + + return (arch_code_generator_t *)cg; +} + + +/***************************************************************** + * ____ _ _ _____ _____ + * | _ \ | | | | |_ _|/ ____| /\ + * | |_) | __ _ ___| | _____ _ __ __| | | | | (___ / \ + * | _ < / _` |/ __| |/ / _ \ '_ \ / _` | | | \___ \ / /\ \ + * | |_) | (_| | (__| < __/ | | | (_| | _| |_ ____) / ____ \ + * |____/ \__,_|\___|_|\_\___|_| |_|\__,_| |_____|_____/_/ \_\ + * + *****************************************************************/ + +static mips_isa_t mips_isa_template = { + &mips_isa_if, + &mips_general_purpose_regs[REG_SP], + &mips_general_purpose_regs[REG_FP], + -1, // stack direction + 0 // num codegens?!? TODO what is this? +}; + +/** + * Initializes the backend ISA and opens the output file. + */ +static void *mips_init(void) { + static int inited = 0; + mips_isa_t *isa; + + if(inited) + return NULL; + + isa = xcalloc(1, sizeof(*isa)); + memcpy(isa, &mips_isa_template, sizeof(*isa)); + + mips_register_init(isa); + mips_create_opcodes(); + mips_init_opcode_transforms(); + + inited = 1; + + return isa; +} + +/** + * Closes the output file and frees the ISA structure. + */ +static void mips_done(void *self) { + free(self); +} + +static int mips_get_n_reg_class(const void *self) { + return N_CLASSES; +} + +static const arch_register_class_t *mips_get_reg_class(const void *self, int i) { + assert(i >= 0 && i < N_CLASSES && "Invalid mips register class requested."); + return &mips_reg_classes[i]; +} + + + +/** + * Get the register class which shall be used to store a value of a given mode. + * @param self The this pointer. + * @param mode The mode in question. + * @return A register class which can hold values of the given mode. + */ +const arch_register_class_t *mips_get_reg_class_for_mode(const void *self, const ir_mode *mode) { + ASSERT_NO_FLOAT(mode); + return &mips_reg_classes[CLASS_mips_general_purpose]; +} + +typedef struct { + be_abi_call_flags_bits_t flags; + const mips_isa_t *isa; + const arch_env_t *arch_env; + ir_graph *irg; + // do special handling to support debuggers + int debug; +} mips_abi_env_t; + +static void *mips_abi_init(const be_abi_call_t *call, const arch_env_t *arch_env, ir_graph *irg) +{ + mips_abi_env_t *env = xmalloc(sizeof(env[0])); + be_abi_call_flags_t fl = be_abi_call_get_flags(call); + env->flags = fl.bits; + env->irg = irg; + env->arch_env = arch_env; + env->isa = (const mips_isa_t*) arch_env->isa; + env->debug = 1; + return env; +} + +static void mips_abi_dont_save_regs(void *self, pset *s) +{ + mips_abi_env_t *env = self; + if(env->flags.try_omit_fp) + pset_insert_ptr(s, env->isa->fp); +} + +static const arch_register_t *mips_abi_prologue(void *self, ir_node** mem, pmap *reg_map) +{ + mips_abi_env_t *env = self; + ir_graph *irg = env->irg; + dbg_info *dbg = NULL; // TODO where can I get this from? + ir_node *block = get_irg_start_block(env->irg); + mips_attr_t *attr; + ir_node *sp = be_abi_reg_map_get(reg_map, &mips_general_purpose_regs[REG_SP]); + ir_node *fp = be_abi_reg_map_get(reg_map, &mips_general_purpose_regs[REG_FP]); + int initialstackframesize; + + if(env->debug) { + /* + * The calling conventions wants a stack frame of at least 24bytes size with + * a0-a3 saved in offset 0-12 + * fp saved in offset 16 + * ra saved in offset 20 + */ + ir_node *mm[6]; + ir_node *sync, *reg, *store; + initialstackframesize = 24; + + // - setup first part of stackframe + sp = new_rd_mips_addi(dbg, irg, block, sp, mode_Is); + attr = get_mips_attr(sp); + attr->tv = new_tarval_from_long(-initialstackframesize, mode_Is); + mips_set_irn_reg(NULL, sp, &mips_general_purpose_regs[REG_SP]); + //arch_set_irn_register(mips_get_arg_env(), sp, &mips_general_purpose_regs[REG_SP]); + + /* TODO: where to get an edge with a0-a3 + int i; + for(i = 0; i < 4; ++i) { + ir_node *reg = be_abi_reg_map_get(reg_map, &mips_general_purpose_regs[REG_A0 + i]); + ir_node *store = new_rd_mips_store_r(dbg, irg, block, *mem, sp, reg, mode_T); + attr = get_mips_attr(store); + attr->load_store_mode = mode_Iu; + attr->tv = new_tarval_from_long(i * 4, mode_Is); + + mm[i] = new_r_Proj(irg, block, store, mode_M, pn_Store_M); + } + */ + + reg = be_abi_reg_map_get(reg_map, &mips_general_purpose_regs[REG_FP]); + store = new_rd_mips_store_r(dbg, irg, block, *mem, sp, reg, mode_T); + attr = get_mips_attr(store); + attr->modes.load_store_mode = mode_Iu; + attr->tv = new_tarval_from_long(16, mode_Is); + + mm[4] = new_r_Proj(irg, block, store, mode_M, pn_Store_M); + + reg = be_abi_reg_map_get(reg_map, &mips_general_purpose_regs[REG_RA]); + store = new_rd_mips_store_r(dbg, irg, block, *mem, sp, reg, mode_T); + attr = get_mips_attr(store); + attr->modes.load_store_mode = mode_Iu; + attr->tv = new_tarval_from_long(20, mode_Is); + + mm[5] = new_r_Proj(irg, block, store, mode_M, pn_Store_M); + + // TODO ideally we would route these mem edges directly towards the epilogue + sync = new_r_Sync(irg, block, 2, mm+4); + *mem = sync; + } else { + ir_node *reg, *store; + initialstackframesize = 4; + + // save old framepointer + sp = new_rd_mips_addi(dbg, irg, block, sp, mode_Is); + attr = get_mips_attr(sp); + attr->tv = new_tarval_from_long(-initialstackframesize, mode_Is); + mips_set_irn_reg(NULL, sp, &mips_general_purpose_regs[REG_SP]); + //arch_set_irn_register(mips_get_arg_env(), sp, &mips_general_purpose_regs[REG_SP]); + + reg = be_abi_reg_map_get(reg_map, &mips_general_purpose_regs[REG_FP]); + store = new_rd_mips_store_r(dbg, irg, block, *mem, sp, reg, mode_T); + attr = get_mips_attr(store); + attr->modes.load_store_mode = mode_Iu; + attr->tv = new_tarval_from_long(0, mode_Is); + + *mem = new_r_Proj(irg, block, store, mode_M, pn_Store_M); + } + + // setup framepointer + fp = new_rd_mips_addi(dbg, irg, block, sp, mode_Is); + attr = get_mips_attr(fp); + attr->tv = new_tarval_from_long(initialstackframesize, mode_Is); + mips_set_irn_reg(NULL, fp, &mips_general_purpose_regs[REG_FP]); + //arch_set_irn_register(mips_get_arg_env(), fp, &mips_general_purpose_regs[REG_FP]); + + be_abi_reg_map_set(reg_map, &mips_general_purpose_regs[REG_FP], fp); + be_abi_reg_map_set(reg_map, &mips_general_purpose_regs[REG_SP], sp); + + return &mips_general_purpose_regs[REG_SP]; +} + +static void mips_abi_epilogue(void *self, ir_node *block, ir_node **mem, pmap *reg_map) +{ + mips_abi_env_t *env = self; + ir_graph *irg = env->irg; + dbg_info *dbg = NULL; // TODO where can I get this from? + mips_attr_t *attr; + ir_node *sp = be_abi_reg_map_get(reg_map, &mips_general_purpose_regs[REG_SP]); + ir_node *fp = be_abi_reg_map_get(reg_map, &mips_general_purpose_regs[REG_FP]); + ir_node *load; + int initial_frame_size = env->debug ? 24 : 4; + int fp_save_offset = env->debug ? 16 : 0; + + // restore sp + //sp = be_new_IncSP(&mips_general_purpose_regs[REG_SP], irg, block, sp, *mem, BE_STACK_FRAME_SIZE, be_stack_dir_against); + + // copy fp to sp + sp = new_rd_mips_move(dbg, irg, block, fp, mode_Iu); + mips_set_irn_reg(NULL, sp, &mips_general_purpose_regs[REG_SP]); + //arch_set_irn_register(mips_get_arg_env(), fp, &mips_general_purpose_regs[REG_SP]); + + // 1. restore fp + load = new_rd_mips_load_r(dbg, irg, block, *mem, sp, mode_T); + attr = get_mips_attr(load); + attr->modes.load_store_mode = mode_Iu; + // sp is at the fp address already, so we have to do fp_save_offset - initial_frame_size + attr->tv = new_tarval_from_long(fp_save_offset - initial_frame_size, mode_Is); + + fp = new_r_Proj(irg, block, load, mode_Iu, pn_Load_res); + mips_set_irn_reg(NULL, fp, &mips_general_purpose_regs[REG_FP]); + //arch_set_irn_register(mips_get_arg_env(), fp, &mips_general_purpose_regs[REG_FP]); + + be_abi_reg_map_set(reg_map, &mips_general_purpose_regs[REG_FP], fp); + be_abi_reg_map_set(reg_map, &mips_general_purpose_regs[REG_SP], sp); +} + +/** + * Produces the type which sits between the stack args and the locals on the stack. + * it will contain the return address and space to store the old frame pointer. + * @return The Firm type modelling the ABI between type. + */ +static ir_type *mips_abi_get_between_type(void *self) { + mips_abi_env_t *env = self; + + static ir_type *debug_between_type = NULL; + static ir_type *opt_between_type = NULL; + static entity *old_fp_ent = NULL; + + if(env->debug && debug_between_type == NULL) { + entity *a0_ent, *a1_ent, *a2_ent, *a3_ent; + entity *ret_addr_ent; + ir_type *ret_addr_type = new_type_primitive(new_id_from_str("return_addr"), mode_P); + ir_type *old_fp_type = new_type_primitive(new_id_from_str("fp"), mode_P); + ir_type *old_param_type = new_type_primitive(new_id_from_str("param"), mode_Iu); + + debug_between_type = new_type_class(new_id_from_str("mips_between_type")); + a0_ent = new_entity(debug_between_type, new_id_from_str("a0_ent"), old_param_type); + a1_ent = new_entity(debug_between_type, new_id_from_str("a1_ent"), old_param_type); + a2_ent = new_entity(debug_between_type, new_id_from_str("a2_ent"), old_param_type); + a3_ent = new_entity(debug_between_type, new_id_from_str("a3_ent"), old_param_type); + old_fp_ent = new_entity(debug_between_type, new_id_from_str("old_fp"), old_fp_type); + ret_addr_ent = new_entity(debug_between_type, new_id_from_str("ret_addr"), ret_addr_type); + + set_entity_offset_bytes(a0_ent, 0); + set_entity_offset_bytes(a1_ent, 4); + set_entity_offset_bytes(a2_ent, 8); + set_entity_offset_bytes(a3_ent, 12); + set_entity_offset_bytes(old_fp_ent, 16); + set_entity_offset_bytes(ret_addr_ent, 20); + + set_type_size_bytes(debug_between_type, 24); + } else if(!env->debug && opt_between_type == NULL) { + ir_type *old_fp_type = new_type_primitive(new_id_from_str("fp"), mode_P); + entity *old_fp_ent; + + opt_between_type = new_type_class(new_id_from_str("mips_between_type")); + old_fp_ent = new_entity(opt_between_type, new_id_from_str("old_fp"), old_fp_type); + set_entity_offset_bytes(old_fp_ent, 0); + set_type_size_bytes(opt_between_type, 4); + } + + return env->debug ? debug_between_type : opt_between_type; +} + +static const be_abi_callbacks_t mips_abi_callbacks = { + mips_abi_init, + free, + mips_abi_get_between_type, + mips_abi_dont_save_regs, + mips_abi_prologue, + mips_abi_epilogue, +}; + +/** + * Get the ABI restrictions for procedure calls. + * @param self The this pointer. + * @param method_type The type of the method (procedure) in question. + * @param abi The abi object to be modified + */ +static void mips_get_call_abi(const void *self, ir_type *method_type, be_abi_call_t *abi) { + ir_type *tp; + ir_mode *mode; + int n = get_method_n_params(method_type); + int result_count; + int i; + ir_mode **modes; + const arch_register_t *reg; + be_abi_call_flags_t call_flags; + + memset(&call_flags, 0, sizeof(call_flags)); + call_flags.bits.left_to_right = 0; + call_flags.bits.store_args_sequential = 0; + call_flags.bits.try_omit_fp = 1; + call_flags.bits.fp_free = 0; + call_flags.bits.call_has_imm = 1; + + /* set stack parameter passing style */ + be_abi_call_set_flags(abi, call_flags, &mips_abi_callbacks); + + /* collect the mode for each type */ + modes = alloca(n * sizeof(modes[0])); + for (i = 0; i < n; i++) { + tp = get_method_param_type(method_type, i); + modes[i] = get_type_mode(tp); + } + + // assigns parameters to registers or stack + for (i = 0; i < n; i++) { + // first 4 params in $a0-$a3, the others on the stack + if(i < 4) { + reg = &mips_general_purpose_regs[REG_A0 + i]; + be_abi_call_param_reg(abi, i, reg); + } else { + /* default: all parameters on stack */ + be_abi_call_param_stack(abi, i, 4, 0, 0); + } + } + + /* set return register */ + /* default: return value is in R0 (and maybe R1) */ + result_count = get_method_n_ress(method_type); + assert(result_count <= 2 && "More than 2 result values not supported"); + for(i = 0; i < result_count; ++i) { + const arch_register_t* reg; + tp = get_method_res_type(method_type, i); + mode = get_type_mode(tp); + ASSERT_NO_FLOAT(mode); + + reg = &mips_general_purpose_regs[REG_V0 + i]; + be_abi_call_res_reg(abi, i, reg); + } +} + +static const void *mips_get_irn_ops(const arch_irn_handler_t *self, const ir_node *irn) { + return &mips_irn_ops; +} + +const arch_irn_handler_t mips_irn_handler = { + mips_get_irn_ops +}; + +const arch_irn_handler_t *mips_get_irn_handler(const void *self) { + return &mips_irn_handler; +} + +/** + * Initializes the code generator interface. + */ +static const arch_code_generator_if_t *mips_get_code_generator_if(void *self) { + return &mips_code_gen_if; +} + +#ifdef WITH_LIBCORE +static void mips_register_options(lc_opt_entry_t *ent) +{ +} +#endif /* WITH_LIBCORE */ + +const arch_isa_if_t mips_isa_if = { +#ifdef WITH_LIBCORE + mips_register_options, +#endif + mips_init, + mips_done, + mips_get_n_reg_class, + mips_get_reg_class, + mips_get_reg_class_for_mode, + mips_get_call_abi, + mips_get_irn_handler, + mips_get_code_generator_if, + mips_get_list_sched_selector, +}; diff --git a/ir/be/mips/bearch_mips.h b/ir/be/mips/bearch_mips.h new file mode 100644 index 000000000..bc0fdc118 --- /dev/null +++ b/ir/be/mips/bearch_mips.h @@ -0,0 +1,22 @@ +#ifndef _BEARCH_MIPS_H_ +#define _BEARCH_MIPS_H_ + +#include "../bearch.h" + +typedef struct _mips_code_gen_t mips_code_gen_t; + +extern const arch_isa_if_t mips_isa_if; + +/** return the scheduled block at position pos */ +ir_node *mips_get_sched_block(const mips_code_gen_t *cg, int pos); + +/** return the number of scheduled blocks */ +int mips_get_sched_n_blocks(const mips_code_gen_t *cg); + +/** set a block schedule number */ +void mips_set_block_sched_nr(ir_node *block, int nr); + +/** get a block schedule number */ +int mips_get_block_sched_nr(ir_node *block); + +#endif /* _BEARCH_MIPS_H_ */ diff --git a/ir/be/mips/bearch_mips_t.h b/ir/be/mips/bearch_mips_t.h new file mode 100644 index 000000000..1e4e30388 --- /dev/null +++ b/ir/be/mips/bearch_mips_t.h @@ -0,0 +1,54 @@ +#ifndef _BEARCH_mips_T_H_ +#define _BEARCH_mips_T_H_ + +#include "debug.h" +#include "irgopt.h" +#include "bearch_mips.h" +#include "mips_nodes_attr.h" +#include "../be.h" +#include "set.h" + +struct _mips_code_gen_t { + const arch_code_generator_if_t *impl; /**< implementation */ + ir_graph *irg; /**< current irg */ + FILE *out; /**< output file */ + const arch_env_t *arch_env; /**< the arch env */ + set *reg_set; /**< set to memorize registers for FIRM nodes (e.g. phi) */ + firm_dbg_module_t *mod; /**< debugging module */ + int emit_decls; /**< flag indicating if decls were already emitted */ + const be_irg_t *birg; /**< The be-irg (contains additional information about the irg) */ + ir_node **bl_list; /**< The block schedule list. */ + survive_dce_t *bl_list_sdce; /**< survive dce environment for the block schedule list */ +}; + + +typedef struct _mips_isa_t { + const arch_isa_if_t *impl; + const arch_register_t *sp; /**< The stack pointer register. */ + const arch_register_t *fp; /**< The base pointer register. */ + const int stack_dir; /**< -1 for decreasing, 1 for increasing. */ + int num_codegens; +} mips_isa_t; + + +typedef struct _mips_irn_ops_t { + const arch_irn_ops_if_t *impl; + mips_code_gen_t *cg; +} mips_irn_ops_t; + + +/* this is a struct to minimize the number of parameters + for transformation walker */ +typedef struct _mips_transform_env_t { + firm_dbg_module_t *mod; /**< The firm debugger */ + dbg_info *dbg; /**< The node debug info */ + ir_graph *irg; /**< The irg, the node should be created in */ + ir_node *block; /**< The block, the node should belong to */ + ir_node *irn; /**< The irn, to be transformed */ + ir_mode *mode; /**< The mode of the irn */ + mips_code_gen_t *cg; /**< The code generator */ +} mips_transform_env_t; + +ir_node *mips_new_NoReg(mips_code_gen_t *cg); + +#endif /* _BEARCH_mips_T_H_ */ diff --git a/ir/be/mips/mips_emitter.c b/ir/be/mips/mips_emitter.c new file mode 100644 index 000000000..bcd7a20d6 --- /dev/null +++ b/ir/be/mips/mips_emitter.c @@ -0,0 +1,773 @@ +/* mips emitter */ +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "xmalloc.h" +#include "tv.h" +#include "iredges.h" +#include "debug.h" +#include "irgwalk.h" +#include "irprintf.h" +#include "irop_t.h" +#include "irargs_t.h" +#include "irprog_t.h" +#include "irouts.h" + +#include "../besched.h" +#include "../benode_t.h" +#include "../beutil.h" + +#include "mips_emitter.h" +#include "gen_mips_emitter.h" +#include "mips_nodes_attr.h" +#include "mips_new_nodes.h" +#include "mips_map_regs.h" + +#define SNPRINTF_BUF_LEN 128 + +static const arch_env_t *arch_env = NULL; + + +/************************************************************* + * _ _ __ _ _ + * (_) | | / _| | | | | + * _ __ _ __ _ _ __ | |_| |_ | |__ ___| |_ __ ___ _ __ + * | '_ \| '__| | '_ \| __| _| | '_ \ / _ \ | '_ \ / _ \ '__| + * | |_) | | | | | | | |_| | | | | | __/ | |_) | __/ | + * | .__/|_| |_|_| |_|\__|_| |_| |_|\___|_| .__/ \___|_| + * | | | | + * |_| |_| + *************************************************************/ + +static const char *get_symconst_str(ir_node *node) +{ + ident *id; + + switch(get_SymConst_kind(node)) { + case symconst_addr_name: + id = get_SymConst_name(node); + return get_id_str(id); + case symconst_addr_ent: + id = get_entity_ident(get_SymConst_entity(node)); + return get_id_str(id); + default: + assert(0); + } + + return NULL; +} + +/** + * Return a const or symconst as string. + */ +static const char *node_const_to_str(ir_node *n) +{ + static char buf[64]; + const mips_attr_t *attr = get_mips_attr(n); + long val; + + if(is_mips_load_r(n) || is_mips_store_r(n)) { + mips_attr_t *attr = get_mips_attr(n); + ir_node *symconst; + + if(attr->tv != NULL) { + val = get_tarval_long(attr->tv); + snprintf(buf, sizeof(buf), "%ld", val); + + return buf; + } + if(attr->stack_entity != NULL) { + snprintf(buf, sizeof(buf), "%d", attr->stack_entity_offset); + return buf; + } + + symconst = get_irn_n(n, 1); + assert(get_irn_opcode(symconst) == iro_SymConst); + + return get_symconst_str(symconst); + } else if(is_mips_la(n)) { + snprintf(buf, sizeof(buf), "%s", get_id_str(attr->symconst_id)); + return buf; + } else if(is_mips_lli(n)) { + assert(attr->tv != NULL); + if(get_mode_sign(get_tarval_mode(attr->tv))) { + long val = get_tarval_long(attr->tv); + snprintf(buf, sizeof(buf), "0x%04X", val & 0xffff); + } else { + unsigned long val = get_tarval_long(attr->tv); + snprintf(buf, sizeof(buf), "0x%04X", val & 0xffff); + } + + return buf; + } else if(is_mips_lui(n)) { + assert(attr->tv != NULL); + if(get_mode_sign(get_tarval_mode(attr->tv))) { + long val = get_tarval_long(attr->tv); + val = (val & 0xffff0000) >> 16; + snprintf(buf, sizeof(buf), "0x%04X", val & 0xffff); + } else { + unsigned long val = get_tarval_long(attr->tv); + val = (val & 0xffff0000) >> 16; + snprintf(buf, sizeof(buf), "0x%04X", val & 0xffff); + } + + return buf; + } + + assert(attr->tv != NULL); + val = get_tarval_long(attr->tv); + snprintf(buf, sizeof(buf), "%ld", val); + + return buf; +} + +/** + * Returns node's offset as string. + */ +static const char *node_offset_to_str(ir_node *n) +{ + return ""; +} + +/* We always pass the ir_node which is a pointer. */ +static int mips_get_arg_type(const lc_arg_occ_t *occ) { + return lc_arg_type_ptr; +} + + +/** + * Returns the register at in position pos. + */ +static const arch_register_t *get_in_reg(ir_node *irn, int pos) +{ + ir_node *op; + const arch_register_t *reg = NULL; + + assert(get_irn_arity(irn) > pos && "Invalid IN position"); + + /* The out register of the operator at position pos is the + in register we need. */ + op = get_irn_n(irn, pos); + + reg = arch_get_irn_register(arch_env, op); + + assert(reg && "no in register found"); + return reg; +} + +/** + * Returns the register at out position pos. + */ +static const arch_register_t *get_out_reg(ir_node *irn, int pos) +{ + ir_node *proj; + const arch_register_t *reg = NULL; + + /* 1st case: irn is not of mode_T, so it has only */ + /* one OUT register -> good */ + /* 2nd case: irn is of mode_T -> collect all Projs and ask the */ + /* Proj with the corresponding projnum for the register */ + + if (get_irn_mode(irn) != mode_T) { + reg = arch_get_irn_register(arch_env, irn); + } + else if (is_mips_irn(irn)) { + reg = get_mips_out_reg(irn, pos); + } + else { + const ir_edge_t *edge; + + foreach_out_edge(irn, edge) { + proj = get_edge_src_irn(edge); + assert(is_Proj(proj) && "non-Proj from mode_T node"); + if (get_Proj_proj(proj) == pos) { + reg = arch_get_irn_register(arch_env, proj); + break; + } + } + } + + assert(reg && "no out register found"); + return reg; +} + +/** + * Returns the number of the in register at position pos. + */ +int get_mips_reg_nr(ir_node *irn, int pos, int in_out) +{ + const arch_register_t *reg; + + if (in_out == 1) { + reg = get_in_reg(irn, pos); + } + else { + reg = get_out_reg(irn, pos); + } + + return arch_register_get_index(reg); +} + +/** + * Returns the name of the in register at position pos. + */ +const char *get_mips_reg_name(ir_node *irn, int pos, int in_out) +{ + const arch_register_t *reg; + + if (in_out == 1) { + reg = get_in_reg(irn, pos); + } + else { + reg = get_out_reg(irn, pos); + } + + return arch_register_get_name(reg); +} + +/** + * Get the register name for a node. + */ +static int mips_get_reg_name(lc_appendable_t *app, + const lc_arg_occ_t *occ, const lc_arg_value_t *arg) +{ + const char *buf; + int res; + ir_node *X = arg->v_ptr; + int nr = occ->width - 1; + + if (!X) + return lc_arg_append(app, occ, "(null)", 6); + + if (occ->conversion == 'S') { + buf = get_mips_reg_name(X, nr, 1); + } + else { /* 'D' */ + buf = get_mips_reg_name(X, nr, 0); + } + + res = lc_appendable_chadd(app, '$'); + res += lc_appendable_snadd(app, buf, strlen(buf)); + return res; +} + +/** + * Returns the tarval or offset of an mips node as a string. + */ +static int mips_const_to_str(lc_appendable_t *app, + const lc_arg_occ_t *occ, const lc_arg_value_t *arg) +{ + const char *buf; + ir_node *X = arg->v_ptr; + + if (!X) + return lc_arg_append(app, occ, "(null)", 6); + + if (occ->conversion == 'C') { + buf = node_const_to_str(X); + } + else { /* 'O' */ + buf = node_offset_to_str(X); + } + + return lc_arg_append(app, occ, buf, strlen(buf)); +} + +/** + * Determines the SSE suffix depending on the mode. + */ +static int mips_get_mode_suffix(lc_appendable_t *app, + const lc_arg_occ_t *occ, const lc_arg_value_t *arg) +{ + ir_node *X = arg->v_ptr; + + if (!X) + return lc_arg_append(app, occ, "(null)", 6); + + if (get_mode_size_bits(get_irn_mode(X)) == 32) + return lc_appendable_chadd(app, 's'); + else + return lc_appendable_chadd(app, 'd'); +} + +/** + * Return the mips printf arg environment. + * We use the firm environment with some additional handlers. + */ +const lc_arg_env_t *mips_get_arg_env(void) +{ + static lc_arg_env_t *env = NULL; + + static const lc_arg_handler_t mips_reg_handler = { mips_get_arg_type, mips_get_reg_name }; + static const lc_arg_handler_t mips_const_handler = { mips_get_arg_type, mips_const_to_str }; + static const lc_arg_handler_t mips_mode_handler = { mips_get_arg_type, mips_get_mode_suffix }; + + if(env == NULL) { + /* extend the firm printer */ + env = firm_get_arg_env(); + //lc_arg_new_env(); + + lc_arg_register(env, "mips:sreg", 'S', &mips_reg_handler); + lc_arg_register(env, "mips:dreg", 'D', &mips_reg_handler); + lc_arg_register(env, "mips:cnst", 'C', &mips_const_handler); + lc_arg_register(env, "mips:offs", 'O', &mips_const_handler); + lc_arg_register(env, "mips:mode", 'M', &mips_mode_handler); + } + + return env; +} + +/* + * Add a number to a prefix. This number will not be used a second time. + */ +static char *get_unique_label(char *buf, size_t buflen, const char *prefix) +{ + static unsigned long id = 0; + snprintf(buf, buflen, "%s%lu", prefix, ++id); + return buf; +} + + +/** + * Returns the target label for a control flow node. + */ +static char *get_cfop_target(const ir_node *irn, char *buf) +{ + ir_node *bl = get_irn_link(irn); + + snprintf(buf, SNPRINTF_BUF_LEN, "BLOCK_%ld", get_irn_node_nr(bl)); + return buf; +} + +/************************************************************************/ +/* ABI Handling +/************************************************************************/ + +static void mips_emit_IncSP(const ir_node *node, mips_emit_env_t *env) +{ + FILE *F = env->out; + int offset = be_get_IncSP_offset(node); + if(offset == 0) + return; + + if(be_get_IncSP_direction(node) != be_stack_dir_expand) + offset = -offset; + + fprintf(F, "\taddi $sp, $sp, %d\n", -offset); +} + +static void mips_emit_Copy(const ir_node *node, mips_emit_env_t *env) +{ + FILE *F = env->out; + + lc_efprintf(mips_get_arg_env(), F, "\tor %1D, $zero, %1S\t\t\t# copy\n", node, node); +} + +static void mips_emit_Return(const ir_node* node, mips_emit_env_t *env) +{ + FILE *F = env->out; + fprintf(F, "\tj $ra\t\t\t\t# return\n"); +} + +static void mips_emit_nops(FILE* F, int n) +{ + int i; + + for(i = 0; i < n; ++i) { + fprintf(F, "\tnop\n"); + } +} + +static void mips_emit_Perm(const ir_node *node, mips_emit_env_t *env) +{ + FILE* F = env->out; + + assert(/*get_irn_n_outs(node) == 2 &&*/ get_irn_arity(node) == 2); + + lc_efprintf(mips_get_arg_env(), F, "\txor %1S, %1S, %2S\t\t\t# perm\n", node, node, node); + mips_emit_nops(F, 3); + lc_efprintf(mips_get_arg_env(), F, "\txor %2S, %2S, %1S\n", node, node, node); + mips_emit_nops(F, 3); + lc_efprintf(mips_get_arg_env(), F, "\txor %1S, %1S, %2S\n", node, node, node); + mips_emit_nops(F, 3); +} + +static void mips_emit_Spill(const ir_node* node, mips_emit_env_t *env) +{ + FILE* F = env->out; + + ir_node* context = be_get_Spill_context(node); + entity* ent = be_get_spill_entity(node); + lc_efprintf(mips_get_arg_env(), F, "\tsw %1S, %d($fp)\n", node, get_entity_offset_bytes(ent)); +} + +static void mips_emit_Reload(const ir_node* node, mips_emit_env_t *env) +{ + FILE* F = env->out; + + entity* ent = be_get_spill_entity(node); + lc_efprintf(mips_get_arg_env(), F, "\tlw %1D, %d($fp)\n", node, get_entity_offset_bytes(ent)); +} + +/************************************************************************/ +/* Calls +/************************************************************************/ + +static void mips_emit_Call(ir_node *node, mips_emit_env_t *env) +{ + FILE *F = env->out; + const arch_register_t *callee_reg; + + // call to imediate value (label) + entity *callee = be_Call_get_entity(node); + if(callee != NULL) { + fprintf(F, "\tjal %s\n", get_entity_name(callee)); + return; + } + + // call to function pointer + callee_reg = get_in_reg(node, be_pos_Call_ptr); + assert(callee_reg != NULL); + + fprintf(F, "\tjal %s\n", arch_register_get_name(callee_reg)); +} + +/************************************************************************ + * _ + * | |_ _ _ __ ___ _ __ ___ + * _ | | | | | '_ ` _ \| '_ \/ __| + * | |_| | |_| | | | | | | |_) \__ \ + * \___/ \__,_|_| |_| |_| .__/|___/ + * |_| + ************************************************************************/ + +const char* mips_get_block_label(const ir_node* block) +{ + static char buf[64]; + snprintf(buf, sizeof(buf), "BLOCK_%ld", get_irn_node_nr(block)); + + return buf; +} + +static void mips_emit_Jump(ir_node *node, mips_emit_env_t *env) +{ + FILE *F = env->out; + const ir_node *block = get_irn_link(node); + + assert(is_Block(block)); + + fprintf(F, "\tb %s\n", mips_get_block_label(block)); +} + +ir_node *mips_get_jump_block(const ir_node* node, int projn) +{ + const ir_edge_t *oute; + for(oute = get_irn_out_edge_first(node); oute != NULL; + oute = get_irn_out_edge_next(node, oute)) { + ir_node *proj = get_edge_src_irn(oute); + long n; + assert(is_Proj(proj)); + + n = get_Proj_proj(proj); + if(n == projn) + return get_irn_link(proj); + } + + return NULL; +} + +/************************************************************************ + * ____ _ _ _ _ * + * / ___|_ _(_) |_ ___| |__ | |_ _ _ __ ___ _ __ * + * \___ \ \ /\ / / | __/ __| '_ \ _ | | | | | '_ ` _ \| '_ \ * + * ___) \ V V /| | || (__| | | | |_| | |_| | | | | | | |_) | * + * |____/ \_/\_/ |_|\__\___|_| |_|\___/ \__,_|_| |_| |_| .__/ * + * |_| * + * * + ************************************************************************/ + +/* jump table entry (target and corresponding number) */ +typedef struct _branch_t { + ir_node *target; + int value; +} branch_t; + +/* jump table for switch generation */ +typedef struct _jmp_tbl_t { + ir_node *defBlock; /**< default target */ + int min_value; /**< smallest switch case */ + int max_value; /**< largest switch case */ + int num_branches; /**< number of jumps */ + char *label; /**< label of the jump table */ + branch_t *branches; /**< jump array */ +} jmp_tbl_t; + +/** + * Compare two variables of type branch_t. Used to sort all switch cases + */ +static int mips_cmp_branch_t(const void *a, const void *b) { + branch_t *b1 = (branch_t *)a; + branch_t *b2 = (branch_t *)b; + + if (b1->value <= b2->value) + return -1; + else + return 1; +} + +const char* mips_get_jumptbl_label(const ir_node* switchjmp) +{ + static char buf[64]; + snprintf(buf, sizeof(buf), "__JUMPTBL%ld", get_irn_node_nr(switchjmp)); + + return buf; +} + +/** + * Emits code for a SwitchJmp (creates a jump table if + * possible otherwise a cmp-jmp cascade). Stolen from ia32 + */ +void emit_mips_jump_table(const ir_node *irn, FILE* F) { + int lastval, i, pn, do_jmp_tbl = 1; + jmp_tbl_t tbl; + ir_node *proj; + const ir_edge_t *edge; + const lc_arg_env_t *env = mips_get_arg_env(); + mips_attr_t *attr; + int i2; + + attr = get_mips_attr(irn); + + /* fill the table structure */ + tbl.label = xmalloc(SNPRINTF_BUF_LEN); + tbl.label = get_unique_label(tbl.label, SNPRINTF_BUF_LEN, "JMPTBL_"); + tbl.defBlock = NULL; + tbl.num_branches = get_irn_n_edges(irn); + tbl.branches = xcalloc(tbl.num_branches, sizeof(tbl.branches[0])); + tbl.min_value = INT_MAX; + tbl.max_value = INT_MIN; + + i = 0; + /* go over all proj's and collect them */ + foreach_out_edge(irn, edge) { + proj = get_edge_src_irn(edge); + assert(is_Proj(proj) && "Only proj allowed at SwitchJmp"); + + pn = get_Proj_proj(proj); + + /* create branch entry */ + tbl.branches[i].target = get_irn_link(proj); + tbl.branches[i].value = pn; + + tbl.min_value = pn < tbl.min_value ? pn : tbl.min_value; + tbl.max_value = pn > tbl.max_value ? pn : tbl.max_value; + + i++; + } + + /* sort the branches by their number */ + qsort(tbl.branches, tbl.num_branches, sizeof(tbl.branches[0]), mips_cmp_branch_t); + + fprintf(F, "%s:\n", mips_get_jumptbl_label(irn)); + lastval = tbl.min_value; + for(i = 0; i < tbl.num_branches; ++i) { + const branch_t *branch = &tbl.branches[i]; + int value = branch->value; + + for(i2 = lastval + 1; i2 < value; ++i2) { + fprintf(F, "\t.word %s\n", get_id_str(attr->symconst_id)); + } + + fprintf(F, "\t.word %s\n", mips_get_block_label(branch->target)); + lastval = branch->value; + } + + if (tbl.label) + free(tbl.label); + if (tbl.branches) + free(tbl.branches); +} + +static void dump_jump_tables(ir_node* node, void *env) +{ + FILE* F = (FILE*) env; + + // emit jump tables + if(is_mips_SwitchJump(node)) { + fprintf(F, ".data\n"); + emit_mips_jump_table(node, F); + fprintf(F, ".text\n"); + } +} + +/*********************************************************************************** + * _ __ _ + * (_) / _| | | + * _ __ ___ __ _ _ _ __ | |_ _ __ __ _ _ __ ___ _____ _____ _ __| | __ + * | '_ ` _ \ / _` | | '_ \ | _| '__/ _` | '_ ` _ \ / _ \ \ /\ / / _ \| '__| |/ / + * | | | | | | (_| | | | | | | | | | | (_| | | | | | | __/\ V V / (_) | | | < + * |_| |_| |_|\__,_|_|_| |_| |_| |_| \__,_|_| |_| |_|\___| \_/\_/ \___/|_| |_|\_\ + * + ***********************************************************************************/ + +static void mips_emit_nothing(ir_mode *mode, mips_emit_env_t *env) +{ +} + +static void mips_emit_this_shouldnt_happen(ir_mode *mode, mips_emit_env_t *env) +{ + assert(0); +} + +/** + * Register emitter functions for mips backend + */ +void mips_register_emitters(void) +{ + /* first clear the generic function pointer for all ops */ + clear_irp_opcodes_generic_func(); + + /* register all emitter functions defined in spec */ + mips_register_spec_emitters(); + + /* benode emitter */ + op_be_IncSP->ops.generic = (op_func) mips_emit_IncSP; + op_be_SetSP->ops.generic = (op_func) mips_emit_this_shouldnt_happen; + op_be_AddSP->ops.generic = (op_func) mips_emit_this_shouldnt_happen; + op_be_Call->ops.generic = (op_func) mips_emit_Call; + op_be_Keep->ops.generic = (op_func) mips_emit_nothing; + op_be_Copy->ops.generic = (op_func) mips_emit_Copy; + op_be_Return->ops.generic = (op_func) mips_emit_Return; + op_be_RegParams->ops.generic = (op_func) mips_emit_nothing; + op_be_Spill->ops.generic = (op_func) mips_emit_Spill; + op_be_Reload->ops.generic = (op_func) mips_emit_Reload; + + op_Start->ops.generic = (op_func) mips_emit_nothing; + op_Proj->ops.generic = (op_func) mips_emit_nothing; + op_SymConst->ops.generic = (op_func) mips_emit_nothing; + op_Jmp->ops.generic = (op_func) mips_emit_Jump; + op_Cmp->ops.generic = (op_func) mips_emit_this_shouldnt_happen; + op_Cond->ops.generic = (op_func) mips_emit_this_shouldnt_happen; +} + +typedef void (*emit_func) (const ir_node *, mips_emit_env_t *); + +/** + * Emits assembly for a single node + */ +static void mips_emit_node(ir_node *irn, mips_emit_env_t* env) +{ + mips_emit_env_t *emit_env = env; + firm_dbg_module_t *mod = emit_env->mod; + FILE *F = emit_env->out; + ir_op *op = get_irn_op(irn); + + DBG((mod, LEVEL_1, "emitting code for %+F\n", irn)); + + if (op->ops.generic) { + emit_func emit = (emit_func) op->ops.generic; + (*emit) (irn, env); + +#if 0 + if(emit != (emit_func) mips_emit_nothing) + mips_emit_nops(F, 5); +#endif + } else { + ir_fprintf(F, "\t\t\t\t\t# %+F\n", irn); + } +} + +/** + * Walks over the nodes in a block connected by scheduling edges + * and emits code for each node. + */ +void mips_gen_block(ir_node *block, void *env) +{ + FILE *F = ((mips_emit_env_t *)env)->out; + ir_node *irn; + + if (! is_Block(block)) + return; + + fprintf(F, "%s:\n", mips_get_block_label(block)); + sched_foreach(block, irn) { + mips_emit_node(irn, env); + } + fprintf(F, "\n"); +} + +/** + * Emits code for function start. + */ +void mips_emit_start(FILE *F, ir_graph *irg) +{ + const char *irg_name = get_entity_name(get_irg_entity(irg)); + + // dump jump tables + irg_walk_graph(irg, NULL, dump_jump_tables, F); + + fprintf(F, "\n\n"); + fprintf(F, "# Function Start of %s\n", irg_name); + fprintf(F, "%s:\n", irg_name); +} + +/** + * Emits code for function end + */ +void mips_emit_end(FILE *F, ir_graph *irg) +{ + const char *irg_name = get_entity_name(get_irg_entity(irg)); + + fprintf(F, "# End of function %s we should never get here...\n", irg_name); + fprintf(F, "\tjal exit\n"); +} + +/** + * Sets labels for control flow nodes (jump target) + * TODO: Jump optimization + */ +void mips_gen_labels(ir_node *block, void *env) +{ + ir_node *pred; + int n = get_Block_n_cfgpreds(block); + + for (n--; n >= 0; n--) { + pred = get_Block_cfgpred(block, n); + set_irn_link(pred, block); + } +} + +/** + * Main driver + */ +void mips_gen_routine(FILE *F, ir_graph *irg, const mips_code_gen_t *cg) +{ + mips_emit_env_t emit_env; + int i, n; + + emit_env.mod = firm_dbg_register("firm.be.mips.emit"); + emit_env.out = F; + emit_env.arch_env = cg->arch_env; + emit_env.cg = cg; + + /* set the global arch_env (needed by print hooks) */ + arch_env = cg->arch_env; + + irg_block_walk_graph(irg, mips_gen_labels, NULL, &emit_env); + mips_emit_start(F, irg); +// irg_walk_blkwise_graph(irg, NULL, mips_gen_block, &emit_env); + + dump_ir_block_graph_sched(irg, "-kaputtelist"); + + for (i = 0, n = mips_get_sched_n_blocks(cg); i < n; ++i) { + ir_node *block = mips_get_sched_block(cg, i); + mips_gen_block(block, &emit_env); + } + + mips_emit_end(F, irg); +} diff --git a/ir/be/mips/mips_emitter.h b/ir/be/mips/mips_emitter.h new file mode 100644 index 000000000..86c4133e6 --- /dev/null +++ b/ir/be/mips/mips_emitter.h @@ -0,0 +1,35 @@ +#ifndef _mips_EMITTER_H_ +#define _mips_EMITTER_H_ + +#include "irargs_t.h" // this also inlucdes +#include "irnode.h" +#include "debug.h" + +#include "../bearch.h" + +#include "bearch_mips_t.h" + +typedef struct _mips_emit_env_t { + firm_dbg_module_t *mod; + FILE *out; + const arch_env_t *arch_env; + const mips_code_gen_t *cg; +} mips_emit_env_t; + +const lc_arg_env_t *mips_get_arg_env(void); + +void equalize_dest_src(FILE *F, ir_node *n); + +int get_mips_reg_nr(ir_node *irn, int posi, int in_out); +const char *get_mips_in_reg_name(ir_node *irn, int pos); + +void mips_gen_routine(FILE *F, ir_graph *irg, const mips_code_gen_t *cg); +void mips_register_emitters(void); +ir_node *mips_get_jump_block(const ir_node* node, int projn); + +/** returns the label used for a block */ +const char* mips_get_block_label(const ir_node* block); +/** returns the label for the jumptable */ +const char* mips_get_jumptbl_label(const ir_node* switchjmp); + +#endif /* _mips_EMITTER_H_ */ diff --git a/ir/be/mips/mips_gen_decls.c b/ir/be/mips/mips_gen_decls.c new file mode 100644 index 000000000..ed1a854aa --- /dev/null +++ b/ir/be/mips/mips_gen_decls.c @@ -0,0 +1,609 @@ +/** + * Dumps global variables and constants as mips assembler. + * @date 14.02.2006 + * @version $Id$ + */ + +#include +#include +#include +#include + +#include "xmalloc.h" +#include + +#ifdef obstack_chunk_alloc +# undef obstack_chunk_alloc +# define obstack_chunk_alloc xmalloc +#else +# define obstack_chunk_alloc xmalloc +# define obstack_chunk_free free +#endif + +#include "tv.h" +#include "irnode.h" +#include "entity.h" +#include "irprog.h" + +#include "mips_gen_decls.h" + +extern int obstack_printf(struct obstack *obst, char *fmt, ...); + +/************************************************************************/ + +/* + * returns the highest bit value + */ +static unsigned highest_bit(unsigned v) +{ + int res = -1; + + if (v >= (1U << 16U)) { + res += 16; + v >>= 16; + } + if (v >= (1U << 8U)) { + res += 8; + v >>= 8; + } + if (v >= (1U << 4U)) { + res += 4; + v >>= 4; + } + if (v >= (1U << 2U)) { + res += 2; + v >>= 2; + } + if (v >= (1U << 1U)) { + res += 1; + v >>= 1; + } + if (v >= 1) + res += 1; + + return res; +} + +/* + * output the alignment + */ +static void mips_dump_align(struct obstack *obst, int align) +{ + int h = highest_bit(align); + + if ((1 << h) < align) + ++h; + align = (1 << h); + + if (align > 1) + obstack_printf(obst, "\t.align %d\n", align); +} + +static void dump_arith_tarval(struct obstack *obst, tarval *tv, int bytes) +{ + switch (bytes) { + + case 1: + obstack_printf(obst, "0x%02x", get_tarval_sub_bits(tv, 0)); + break; + + case 2: + obstack_printf(obst, "0x%02x%02x", get_tarval_sub_bits(tv, 1), get_tarval_sub_bits(tv, 0)); + break; + + case 4: + obstack_printf(obst, "0x%02x%02x%02x%02x", + get_tarval_sub_bits(tv, 3), get_tarval_sub_bits(tv, 2), get_tarval_sub_bits(tv, 1), get_tarval_sub_bits(tv, 0)); + break; + + case 8: + obstack_printf(obst, "0x%02x%02x%02x%02x%02x%02x%02x%02x", + get_tarval_sub_bits(tv, 7), get_tarval_sub_bits(tv, 6), get_tarval_sub_bits(tv, 5), get_tarval_sub_bits(tv, 4), + get_tarval_sub_bits(tv, 3), get_tarval_sub_bits(tv, 2), get_tarval_sub_bits(tv, 1), get_tarval_sub_bits(tv, 0)); + break; + + case 10: + case 12: + break; + + default: + fprintf(stderr, "Try to dump an tarval with %d bytes\n", bytes); + assert(0); + } +} + +/* + * dump an arithmetic tarval + */ +static void mips_dump_arith_tarval(struct obstack *obst, tarval *tv, int bytes) +{ + switch (bytes) { + + case 1: + obstack_printf(obst, "\t.byte\t"); + break; + + case 2: + obstack_printf(obst, "\t.half\t"); + break; + + case 4: + obstack_printf(obst, "\t.word\t"); + break; + + default: + fprintf(stderr, "Try to dump an tarval with %d bytes\n", bytes); + assert(0); + } + dump_arith_tarval(obst, tv, bytes); +} + + +/* + * dump an atomic value + */ +static void do_dump_atomic_init(struct obstack *obst, ir_node *init) +{ + ir_mode *mode = get_irn_mode(init); + int bytes = get_mode_size_bytes(mode); + tarval *tv; + + switch (get_irn_opcode(init)) { + + case iro_Cast: + do_dump_atomic_init(obst, get_Cast_op(init)); + return; + + case iro_Conv: + do_dump_atomic_init(obst, get_Conv_op(init)); + return; + + case iro_Const: + tv = get_Const_tarval(init); + + /* beware of old stuff */ + assert(! mode_is_reference(mode)); + + /* it's a arithmetic value */ + dump_arith_tarval(obst, tv, bytes); + return; + + case iro_SymConst: + switch (get_SymConst_kind(init)) { + case symconst_addr_name: + obstack_printf(obst, "%s", get_id_str(get_SymConst_name(init))); + break; + + case symconst_addr_ent: + obstack_printf(obst, "%s", get_entity_ld_name(get_SymConst_entity(init))); + break; + + case symconst_size: + obstack_printf(obst, "%d", get_type_size_bytes(get_SymConst_type(init))); + break; + + default: + assert(0 && "dump_atomic_init(): don't know how to init from this SymConst"); + } + return; + + case iro_Add: + do_dump_atomic_init(obst, get_Add_left(init)); + obstack_printf(obst, " + "); + do_dump_atomic_init(obst, get_Add_right(init)); + return; + + case iro_Sub: + do_dump_atomic_init(obst, get_Sub_left(init)); + obstack_printf(obst, " - "); + do_dump_atomic_init(obst, get_Sub_right(init)); + return; + + case iro_Mul: + do_dump_atomic_init(obst, get_Mul_left(init)); + obstack_printf(obst, " * "); + do_dump_atomic_init(obst, get_Mul_right(init)); + return; + + default: + assert(0 && "dump_atomic_init(): unknown IR-node"); + } +} + +/* + * dump an atomic value + */ +static void dump_atomic_init(struct obstack *obst, ir_node *init) +{ + ir_mode *mode = get_irn_mode(init); + int bytes = get_mode_size_bytes(mode); + + switch (bytes) { + + case 1: + obstack_printf(obst, "\t.byte\t"); + break; + + case 2: + obstack_printf(obst, "\t.half\t"); + break; + + case 4: + obstack_printf(obst, "\t.word\t"); + break; + + default: + fprintf(stderr, "Try to dump an tarval with %d bytes\n", bytes); + assert(0); + } + + do_dump_atomic_init(obst, init); + obstack_printf(obst, "\n"); +} + +/************************************************************************/ +/* Routines to dump global variables */ +/************************************************************************/ + +/** + * Determine if an entity is a string constant + * @param ent The entity + * @return 1 if it is a string constant, 0 otherwise + */ +static int ent_is_string_const(entity *ent) +{ + int res = 0; + ir_type *ty; + + ty = get_entity_type(ent); + + /* if it's an array */ + if (is_Array_type(ty)) { + ir_type *elm_ty = get_array_element_type(ty); + + /* and the array's element type is primitive */ + if (is_Primitive_type(elm_ty)) { + ir_mode *mode = get_type_mode(elm_ty); + + /* + * and the mode of the element type is an int of + * the same size as the byte mode + */ + if (mode_is_int(mode) + && get_mode_size_bits(mode) == get_mode_size_bits(mode_Bs)) + { + int i, c, n; + + n = get_compound_ent_n_values(ent); + for (i = 0; i < n; ++i) { + ir_node *irn = get_compound_ent_value(ent, i); + if(get_irn_opcode(irn) != iro_Const) + return 0; + + c = (int) get_tarval_long(get_Const_tarval(irn)); + + if((i < n - 1 && !(isgraph(c) || isspace(c))) + || (i == n - 1 && c != '\0')) + return 0; + } + + res = 1; + } + } + } + + return res; +} + +/** + * Dump a atring constant. + * No checks are made!! + * @param obst The obst to dump on. + * @param ent The entity to dump. + */ +static void dump_string_cst(struct obstack *obst, entity *ent) +{ + int i, n; + + obstack_printf(obst, "\t.asciiz \""); + n = get_compound_ent_n_values(ent); + + for (i = 0; i < n-1; ++i) { + ir_node *irn; + int c; + + irn = get_compound_ent_value(ent, i); + c = (int) get_tarval_long(get_Const_tarval(irn)); + + switch (c) { + case '"' : obstack_printf(obst, "\\\""); break; + case '\n': obstack_printf(obst, "\\n"); break; + case '\r': obstack_printf(obst, "\\r"); break; + case '\t': obstack_printf(obst, "\\t"); break; + default : + if (isprint(c)) + obstack_printf(obst, "%c", c); + else + obstack_printf(obst, "%O", c); + break; + } + } + obstack_printf(obst, "\"\n"); +} + +struct arr_info { + int n_elems; + int visit_cnt; + int size; +}; + +/* + * Dumps the initialization of global variables that are not + * "uninitialized". + */ +static void dump_global(struct obstack *rdata_obstack, struct obstack *data_obstack, struct obstack *comm_obstack, entity *ent) +{ + ir_type *ty = get_entity_type(ent); + const char *ld_name = get_entity_ld_name(ent); + int align, h; + struct obstack *obst = data_obstack; + + /* + * FIXME: did NOT work for partly constant values + */ + if (! is_Method_type(ty)) { + ent_variability variability = get_entity_variability(ent); + visibility visibility = get_entity_visibility(ent); + + if (variability == variability_constant) { + /* a constant entity, put it on the rdata */ + obst = rdata_obstack; + } + + /* check, wether it is initialized, if yes create data */ + if (variability != variability_uninitialized) { + if (visibility == visibility_external_visible) { + obstack_printf(obst, ".globl\t%s\n", ld_name); + } + + align = get_type_alignment_bytes(ty); + mips_dump_align(obst, align); + + obstack_printf(obst, "%s:\n", ld_name); + + if (is_atomic_type(ty)) { + if (get_entity_visibility(ent) != visibility_external_allocated) + dump_atomic_init(obst, get_atomic_ent_value(ent)); + } + else { + int i, size = 0; + + if (ent_is_string_const(ent)) { + dump_string_cst(obst, ent); + } + else if (is_Array_type(ty)) { + int filler; + + /* potential spare values should be already included! */ + for (i = 0; i < get_compound_ent_n_values(ent); ++i) { + entity *step = get_compound_ent_value_member(ent, i); + ir_type *stype = get_entity_type(step); + + if (get_type_mode(stype)) { + int align = (get_type_alignment_bits(stype) + 7) >> 3; + int n = size % align; + + if (n > 0) { + obstack_printf(obst, "\t.space\t%d\n", align - n); + size += align - n; + } + } + dump_atomic_init(obst, get_compound_ent_value(ent, i)); + size += get_type_size_bytes(stype); + } + filler = get_type_size_bytes(ty) - size; + + if (filler > 0) + obstack_printf(obst, "\t.space\t%d\n", filler); + } + else if (is_compound_type(ty)) { + ir_node **vals; + int type_size, j; + + /* Compound entities are NOT sorted. + * The sorting strategy used doesn't work for `value' compound fields nor + * for partially_constant entities. + */ + + /* + * in the worst case, every entity allocates one byte, so the type + * size should be equal or bigger the number of fields + */ + type_size = get_type_size_bytes(ty); + vals = xcalloc(type_size, sizeof(*vals)); + + /* collect the values and store them at the offsets */ + for(i = 0; i < get_compound_ent_n_values(ent); ++i) { + int graph_length, aipos, offset; + struct arr_info *ai; + int all_n = 1; + compound_graph_path *path = get_compound_ent_value_path(ent, i); + + /* get the access path to the costant value */ + graph_length = get_compound_graph_path_length(path); + ai = xcalloc(graph_length, sizeof(struct arr_info)); + + /* We wanna know how many arrays are on the path to the entity. We also have to know how + * many elements each array holds to calculate the offset for the entity. */ + for (j = 0; j < graph_length; j++) { + entity *step = get_compound_graph_path_node(path, j); + ir_type *step_type = get_entity_type(step); + int ty_size = (get_type_size_bits(step_type) + 7) >> 3; + int k, n = 0; + + if (is_Array_type(step_type)) + for (k = 0; k < get_array_n_dimensions(step_type); k++) + n += get_tarval_long(get_Const_tarval(get_array_upper_bound(step_type, k))); + if (n) all_n *= n; + ai[j].n_elems = n ? all_n + 1 : 0; + ai[j].visit_cnt = 0; + ai[j].size = ty_size; + } + + aipos = graph_length - 1; + if (aipos) aipos--; + + for (offset = j = 0; j < graph_length; j++) { + entity *step = get_compound_graph_path_node(path, j); + ir_type *step_type = get_entity_type(step); + int ent_ofs = get_entity_offset_bytes(step); + int stepsize = 0; + + /* add all positive offsets (= offsets in structs) */ + if (ent_ofs >= 0) offset += ent_ofs; + + if (j == graph_length - 1) { + stepsize = (get_type_size_bits(step_type) + 7) >> 3; + + /* Search the next free position in vals depending on the information from above (ai). */ + while (vals[offset]) { + if (ai[aipos].visit_cnt < ai[aipos].n_elems) { + offset += stepsize; + ai[aipos].visit_cnt++; + } + else + while (aipos >= 0 && ai[aipos].visit_cnt == ai[aipos].n_elems) { + stepsize = ai[aipos--].size; + offset += stepsize; + } + } + + assert(aipos >= 0 && "couldn't store entity"); + vals[offset] = get_compound_ent_value(ent, i); + } + } + + free(ai); + } + + /* now write them sorted */ + for(i = 0; i < type_size; ) { + if (vals[i]) { + dump_atomic_init(obst, vals[i]); + i += (get_mode_size_bytes(get_irn_mode(vals[i]))); + } + else { + /* a gap */ + obstack_printf(obst, "\t.byte\t0\n"); + ++i; + } + } + free(vals); + } + else { + assert(0 && "unsupported type"); + } + } + obstack_printf(obst, "\n"); + } + else if (visibility != visibility_external_allocated) { + if (visibility == visibility_local) { + obstack_printf(comm_obstack, "\t.local\t%s\n", ld_name); + } + + /* calculate the alignment */ + align = get_type_alignment_bytes(ty); + h = highest_bit(align); + + if ((1 << h) < align) + ++h; + align = (1 << h); + + if (align < 1) + align = 1; + + obstack_printf(comm_obstack, "\t.comm\t%s,%d,%d\n", ld_name, (get_type_size_bits(ty) + 7) >> 3, align); + } + } +} + +/* + * Dumps declarations of global variables and the initialization code. + */ +void mips_dump_globals(struct obstack *rdata_obstack, struct obstack *data_obstack, struct obstack *comm_obstack) +{ + ir_type *gt = get_glob_type(); + int i, n = get_class_n_members(gt); + + for (i = 0; i < n; i++) + dump_global(rdata_obstack, data_obstack, comm_obstack, get_class_member(gt, i)); +} + + +static void mips_emit_stdlib_call(FILE *F, const char* name, int num) { + fprintf(F, "%s:\n", name); + fprintf(F, "\tori $v0, $zero, %d\n", num); + fprintf(F, "\tsyscall\n"); + fprintf(F, "\tj $ra\n"); + fprintf(F, "\n"); +} + +/** + * Emits a default library for spim... Hack for now... + */ +static void mips_emit_standard_lib(FILE* F) { + static int output = 0; + if(output) + return; + output = 1; + + mips_emit_stdlib_call(F, "print_int", 1); + mips_emit_stdlib_call(F, "print_string", 4); + mips_emit_stdlib_call(F, "read_int", 5); + mips_emit_stdlib_call(F, "read_string", 8); + mips_emit_stdlib_call(F, "sbrk", 9); + mips_emit_stdlib_call(F, "exit", 10); +} + +/************************************************************************/ + +void mips_gen_decls(FILE *out) { + struct obstack rodata, data, comm; + int size; + char *cp; + + obstack_init(&rodata); + obstack_init(&data); + obstack_init(&comm); + + mips_dump_globals(&rodata, &data, &comm); + + size = obstack_object_size(&data); + cp = obstack_finish(&data); + if (size > 0) { + fprintf(out, "\t.data\n"); + fwrite(cp, 1, size, out); + } + + size = obstack_object_size(&rodata); + cp = obstack_finish(&rodata); + if (size > 0) { + fprintf(out, "\t.data\n"); + fwrite(cp, 1, size, out); + } + + size = obstack_object_size(&comm); + cp = obstack_finish(&comm); + if (size > 0) { + fprintf(out, "\t.data\n"); + fwrite(cp, 1, size, out); + } + + obstack_free(&rodata, NULL); + obstack_free(&data, NULL); + obstack_free(&comm, NULL); + + fprintf(out, "\t.text\n"); + + mips_emit_standard_lib(out); +} diff --git a/ir/be/mips/mips_gen_decls.h b/ir/be/mips/mips_gen_decls.h new file mode 100644 index 000000000..63356774f --- /dev/null +++ b/ir/be/mips/mips_gen_decls.h @@ -0,0 +1,9 @@ +#ifndef _MIPS_GEN_DECLS_H_ +#define _MIPS_GEN_DECLS_H_ + +/** + * Generate all entities. + */ +void mips_gen_decls(FILE *out); + +#endif /* _MIPS_GEN_DECLS_H_ */ diff --git a/ir/be/mips/mips_map_regs.c b/ir/be/mips/mips_map_regs.c new file mode 100644 index 000000000..5598385d1 --- /dev/null +++ b/ir/be/mips/mips_map_regs.c @@ -0,0 +1,58 @@ +/** + * Register mapping for firm nodes. Stolen from bearch_firm :) + * $Id$ + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "mips_map_regs.h" +#include "mips_new_nodes.h" + +/* Mapping to store registers in firm nodes */ + +struct mips_irn_reg_assoc { + const ir_node *irn; + const arch_register_t *reg; +}; + +int mips_cmp_irn_reg_assoc(const void *a, const void *b, size_t len) { + const struct mips_irn_reg_assoc *x = a; + const struct mips_irn_reg_assoc *y = b; + + return x->irn != y->irn; +} + +static struct mips_irn_reg_assoc *get_irn_reg_assoc(const ir_node *irn, set *reg_set) { + struct mips_irn_reg_assoc templ; + unsigned int hash; + + templ.irn = irn; + templ.reg = NULL; + hash = HASH_PTR(irn); + + return set_insert(reg_set, &templ, sizeof(templ), hash); +} + +void mips_set_firm_reg(ir_node *irn, const arch_register_t *reg, set *reg_set) { + struct mips_irn_reg_assoc *assoc = get_irn_reg_assoc(irn, reg_set); + assoc->reg = reg; +} + +const arch_register_t *mips_get_firm_reg(const ir_node *irn, set *reg_set) { + struct mips_irn_reg_assoc *assoc = get_irn_reg_assoc(irn, reg_set); + return assoc->reg; +} + +/** + * Translates the projnum into a "real" argument position for register + * requirements dependend on the predecessor. + */ +long mips_translate_proj_pos(const ir_node *proj) { + ir_node *pred = get_Proj_pred(proj); + long nr = get_Proj_proj(proj); + + return nr; +} diff --git a/ir/be/mips/mips_map_regs.h b/ir/be/mips/mips_map_regs.h new file mode 100644 index 000000000..40edbac2e --- /dev/null +++ b/ir/be/mips/mips_map_regs.h @@ -0,0 +1,16 @@ +#ifndef _mips_MAP_REGS_H_ +#define _mips_MAP_REGS_H_ + +#include "irnode.h" +#include "set.h" + +#include "../bearch.h" +#include "mips_nodes_attr.h" + +int mips_cmp_irn_reg_assoc(const void *a, const void *b, size_t len); +void mips_set_firm_reg(ir_node *irn, const arch_register_t *reg, set *reg_set); +const arch_register_t *mips_get_firm_reg(const ir_node *irn, set *reg_set); + +long mips_translate_proj_pos(const ir_node *proj); + +#endif /* _mips_MAP_REGS_H_ */ diff --git a/ir/be/mips/mips_new_nodes.c b/ir/be/mips/mips_new_nodes.c new file mode 100644 index 000000000..1fb006666 --- /dev/null +++ b/ir/be/mips/mips_new_nodes.c @@ -0,0 +1,500 @@ +/** + * This file implements the creation of the achitecture specific firm opcodes + * and the coresponding node constructors for the mips assembler irg. + * $Id$ + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _WIN32 +#include +#else +#include +#endif + +#include + +#include "irprog_t.h" +#include "irgraph_t.h" +#include "irnode_t.h" +#include "irmode_t.h" +#include "ircons_t.h" +#include "iropt_t.h" +#include "irop.h" +#include "firm_common_t.h" +#include "irvrfy_t.h" +#include "irprintf.h" + +#include "../bearch.h" + +#include "mips_nodes_attr.h" +#include "mips_new_nodes.h" +#include "gen_mips_regalloc_if.h" + + + +/*********************************************************************************** + * _ _ _ __ + * | | (_) | | / _| + * __| |_ _ _ __ ___ _ __ ___ _ __ _ _ __ | |_ ___ _ __| |_ __ _ ___ ___ + * / _` | | | | '_ ` _ \| '_ \ / _ \ '__| | | '_ \| __/ _ \ '__| _/ _` |/ __/ _ \ + * | (_| | |_| | | | | | | |_) | __/ | | | | | | || __/ | | || (_| | (_| __/ + * \__,_|\__,_|_| |_| |_| .__/ \___|_| |_|_| |_|\__\___|_| |_| \__,_|\___\___| + * | | + * |_| + ***********************************************************************************/ + +/** + * Returns a string containing the names of all registers within the limited bitset + */ +static char *get_limited_regs(const arch_register_req_t *req, char *buf, int max) { + bitset_t *bs = bitset_alloca(req->cls->n_regs); + char *p = buf; + int size = 0; + int i, cnt; + + req->limited(NULL, bs); + + for (i = 0; i < req->cls->n_regs; i++) { + if (bitset_is_set(bs, i)) { + cnt = snprintf(p, max - size, " %s", req->cls->regs[i].name); + if (cnt < 0) { + fprintf(stderr, "dumper problem, exiting\n"); + exit(1); + } + + p += cnt; + size += cnt; + + if (size >= max) + break; + } + } + + return buf; +} + +/** + * Dumps the register requirements for either in or out. + */ +static void dump_reg_req(FILE *F, ir_node *n, const mips_register_req_t **reqs, int inout) { + char *dir = inout ? "out" : "in"; + int max = inout ? get_mips_n_res(n) : get_irn_arity(n); + char *buf = alloca(1024); + int i; + + memset(buf, 0, 1024); + + if (reqs) { + for (i = 0; i < max; i++) { + fprintf(F, "%sreq #%d =", dir, i); + + if (reqs[i]->req.type == arch_register_req_type_none) { + fprintf(F, " n/a"); + } + + if (reqs[i]->req.type & arch_register_req_type_normal) { + fprintf(F, " %s", reqs[i]->req.cls->name); + } + + if (reqs[i]->req.type & arch_register_req_type_limited) { + fprintf(F, " %s", get_limited_regs(&reqs[i]->req, buf, 1024)); + } + + if (reqs[i]->req.type & arch_register_req_type_should_be_same) { + ir_fprintf(F, " same as %+F", get_irn_n(n, reqs[i]->same_pos)); + } + + if (reqs[i]->req.type & arch_register_req_type_should_be_different) { + ir_fprintf(F, " different from %+F", get_irn_n(n, reqs[i]->different_pos)); + } + + fprintf(F, "\n"); + } + + fprintf(F, "\n"); + } + else { + fprintf(F, "%sreq = N/A\n", dir); + } +} + + +/** + * Dumper interface for dumping mips nodes in vcg. + * @param n the node to dump + * @param F the output file + * @param reason indicates which kind of information should be dumped + * @return 0 on success or != 0 on failure + */ +static int dump_node_mips(ir_node *n, FILE *F, dump_reason_t reason) { + ir_mode *mode = NULL; + int bad = 0; + int i; + mips_attr_t *attr; + char buf[64]; + const mips_register_req_t **reqs; + const arch_register_t **slots; + + switch (reason) { + case dump_node_opcode_txt: + fprintf(F, "%s", get_irn_opname(n)); + break; + + case dump_node_mode_txt: + mode = get_irn_mode(n); + + if (mode) { + fprintf(F, "[%s]", get_mode_name(mode)); + } + else { + fprintf(F, "[?NOMODE?]"); + } + break; + + case dump_node_nodeattr_txt: + + /* TODO: dump some attributes which should show up */ + /* in node name in dump (e.g. consts or the like) */ + + break; + + case dump_node_info_txt: + attr = get_mips_attr(n); + fprintf(F, "=== mips attr begin ===\n"); + + /* dump IN requirements */ + if (get_irn_arity(n) > 0) { + reqs = get_mips_in_req_all(n); + dump_reg_req(F, n, reqs, 0); + } + + /* dump OUT requirements */ + if (attr->n_res > 0) { + reqs = get_mips_out_req_all(n); + dump_reg_req(F, n, reqs, 1); + } + + /* dump assigned registers */ + slots = get_mips_slots(n); + if (slots && attr->n_res > 0) { + for (i = 0; i < attr->n_res; i++) { + if (slots[i]) { + fprintf(F, "reg #%d = %s\n", i, slots[i]->name); + } + else { + fprintf(F, "reg #%d = n/a\n", i); + } + } + } + fprintf(F, "\n"); + + /* dump n_res */ + fprintf(F, "n_res = %d\n", get_mips_n_res(n)); + + /* dump flags */ + fprintf(F, "flags ="); + if (attr->flags == arch_irn_flags_none) { + fprintf(F, " none"); + } + else { + if (attr->flags & arch_irn_flags_dont_spill) { + fprintf(F, " unspillable"); + } + if (attr->flags & arch_irn_flags_rematerializable) { + fprintf(F, " remat"); + } + if (attr->flags & arch_irn_flags_ignore) { + fprintf(F, " ignore"); + } + } + fprintf(F, " (%d)\n", attr->flags); + + if(attr->modes.load_store_mode != NULL) { + fprintf(F, " load_store_mode %s\n", get_mode_name(attr->modes.load_store_mode)); + } + if(attr->stack_entity != NULL) { + fprintf(F, " stack entity %s\n", get_entity_name(attr->stack_entity)); + } + if(attr->tv != NULL) { + tarval_snprintf(buf, sizeof(buf), attr->tv); + fprintf(F, " tarval %s\n", buf); + } + if(attr->symconst_id != NULL) { + fprintf(F, " symconst '%s'\n", get_id_str(attr->symconst_id)); + } + + fprintf(F, "=== mips attr end ===\n"); + /* end of: case dump_node_info_txt */ + break; + } + + + return bad; +} + + + +/*************************************************************************************************** + * _ _ _ __ _ _ _ _ + * | | | | | | / / | | | | | | | | + * __ _| |_| |_ _ __ ___ ___| |_ / /_ _ ___| |_ _ __ ___ ___| |_| |__ ___ __| |___ + * / _` | __| __| '__| / __|/ _ \ __| / / _` |/ _ \ __| | '_ ` _ \ / _ \ __| '_ \ / _ \ / _` / __| + * | (_| | |_| |_| | \__ \ __/ |_ / / (_| | __/ |_ | | | | | | __/ |_| | | | (_) | (_| \__ \ + * \__,_|\__|\__|_| |___/\___|\__/_/ \__, |\___|\__| |_| |_| |_|\___|\__|_| |_|\___/ \__,_|___/ + * __/ | + * |___/ + ***************************************************************************************************/ + +/** + * Wraps get_irn_generic_attr() as it takes no const ir_node, so we need to do a cast. + * Firm was made by people hating const :-( + */ +mips_attr_t *get_mips_attr(const ir_node *node) { + assert(is_mips_irn(node) && "need mips node to get attributes"); + return (mips_attr_t *)get_irn_generic_attr((ir_node *)node); +} + +/** + * Returns the argument register requirements of a mips node. + */ +const mips_register_req_t **get_mips_in_req_all(const ir_node *node) { + mips_attr_t *attr = get_mips_attr(node); + return attr->in_req; +} + +/** + * Returns the result register requirements of an mips node. + */ +const mips_register_req_t **get_mips_out_req_all(const ir_node *node) { + mips_attr_t *attr = get_mips_attr(node); + return attr->out_req; +} + +/** + * Returns the argument register requirement at position pos of an mips node. + */ +const mips_register_req_t *get_mips_in_req(const ir_node *node, int pos) { + mips_attr_t *attr = get_mips_attr(node); + return attr->in_req[pos]; +} + +/** + * Returns the result register requirement at position pos of an mips node. + */ +const mips_register_req_t *get_mips_out_req(const ir_node *node, int pos) { + mips_attr_t *attr = get_mips_attr(node); + return attr->out_req[pos]; +} + +/** + * Sets the OUT register requirements at position pos. + */ +void set_mips_req_out(ir_node *node, const mips_register_req_t *req, int pos) { + mips_attr_t *attr = get_mips_attr(node); + attr->out_req[pos] = req; +} + +/** + * Sets the IN register requirements at position pos. + */ +void set_mips_req_in(ir_node *node, const mips_register_req_t *req, int pos) { + mips_attr_t *attr = get_mips_attr(node); + attr->in_req[pos] = req; +} + +/** + * Returns the register flag of an mips node. + */ +arch_irn_flags_t get_mips_flags(const ir_node *node) { + mips_attr_t *attr = get_mips_attr(node); + return attr->flags; +} + +/** + * Sets the register flag of an mips node. + */ +void set_mips_flags(const ir_node *node, arch_irn_flags_t flags) { + mips_attr_t *attr = get_mips_attr(node); + attr->flags = flags; +} + +/** + * Returns the result register slots of an mips node. + */ +const arch_register_t **get_mips_slots(const ir_node *node) { + mips_attr_t *attr = get_mips_attr(node); + return attr->slots; +} + +/** + * Returns the name of the OUT register at position pos. + */ +const char *get_mips_out_reg_name(const ir_node *node, int pos) { + mips_attr_t *attr = get_mips_attr(node); + + assert(is_mips_irn(node) && "Not an mips node."); + assert(pos < attr->n_res && "Invalid OUT position."); + assert(attr->slots[pos] && "No register assigned"); + + return arch_register_get_name(attr->slots[pos]); +} + +/** + * Returns the index of the OUT register at position pos within its register class. + */ +int get_mips_out_regnr(const ir_node *node, int pos) { + mips_attr_t *attr = get_mips_attr(node); + + assert(is_mips_irn(node) && "Not an mips node."); + assert(pos < attr->n_res && "Invalid OUT position."); + assert(attr->slots[pos] && "No register assigned"); + + return arch_register_get_index(attr->slots[pos]); +} + +/** + * Returns the OUT register at position pos. + */ +const arch_register_t *get_mips_out_reg(const ir_node *node, int pos) { + mips_attr_t *attr = get_mips_attr(node); + + assert(is_mips_irn(node) && "Not an mips node."); + assert(pos < attr->n_res && "Invalid OUT position."); + assert(attr->slots[pos] && "No register assigned"); + + return attr->slots[pos]; +} + +/** + * Sets the number of results. + */ +void set_mips_n_res(ir_node *node, int n_res) { + mips_attr_t *attr = get_mips_attr(node); + attr->n_res = n_res; +} + +/** + * Returns the number of results. + */ +int get_mips_n_res(const ir_node *node) { + mips_attr_t *attr = get_mips_attr(node); + return attr->n_res; +} + +/** + * Initializes the nodes attributes. + */ +void init_mips_attributes(ir_node *node, arch_irn_flags_t flags, const mips_register_req_t **in_reqs, + const mips_register_req_t **out_reqs, int n_res) +{ + mips_attr_t *attr = get_mips_attr(node); + + attr->flags = flags; + attr->out_req = out_reqs; + + attr->n_res = n_res; + if(n_res) { + attr->slots = xcalloc(n_res, sizeof(attr->slots[0])); + } else { + attr->slots = NULL; + } + attr->in_req = in_reqs; +} + +/************************************************************************ + * ___ _____ _ _ _ + * |_ _| ___|__ | | __| (_)_ __ __ _ + * | || |_ / _ \| |/ _` | | '_ \ / _` | + * | || _| (_) | | (_| | | | | | (_| | + * |___|_| \___/|_|\__,_|_|_| |_|\__, | + * |___/ + ************************************************************************/ + +// test if a tarval can be expressed in a 16bit immediate value +static int is_tarval_16(ir_node* node) +{ + mips_attr_t *attr = get_mips_attr(node); + tarval* tv = attr->tv; + long val = get_tarval_long(tv); + if(get_mode_sign(get_irn_mode(node))) { + if(val < -32768 || val > 32767) + return 0; + } else { + unsigned long uval = (unsigned long) val; + if(uval > 65536) + return 0; + } + + return 1; +} + +#define MIPS_MAKE_IFOLDING_TRANSFORM(srcnode, inode, commutative) \ +ir_node *mips_transform_##srcnode(ir_node* node) \ +{ \ + ir_node* op1 = get_irn_n(node, 0); \ + ir_node* op2 = get_irn_n(node, 1); \ + ir_node* result; \ + if(op1 == NULL || op2 == NULL) \ + return node; \ + \ + if((is_mips_lli(op2) || is_mips_lui(op2)) && is_tarval_16(op2)) { \ + mips_attr_t *attr, *op_attr = get_mips_attr(op2); \ + long val = get_tarval_long(op_attr->tv); \ + result = new_rd_mips_##inode(get_irn_dbg_info(node), get_irn_irg(node), get_nodes_block(node), \ + op1, get_irn_mode(node)); \ + attr = get_mips_attr(result); \ + attr->tv = new_tarval_from_long(val, get_mode_sign(get_irn_mode(node)) ? mode_Hs : mode_Hu); \ + return result; \ + } \ + \ + if(commutative && (is_mips_lli(op1) || is_mips_lui(op1)) && is_tarval_16(op1)) { \ + mips_attr_t *attr, *op_attr = get_mips_attr(op1); \ + long val = get_tarval_long(op_attr->tv); \ + result = new_rd_mips_##inode(get_irn_dbg_info(node), get_irn_irg(node), get_nodes_block(node), \ + op2, get_irn_mode(node)); \ + attr = get_mips_attr(result); \ + attr->tv = new_tarval_from_long(val, get_mode_sign(get_irn_mode(node)) ? mode_Hs : mode_Hu); \ + return result; \ + } \ + \ + return node; \ +} + +MIPS_MAKE_IFOLDING_TRANSFORM(add, addi, 1); +MIPS_MAKE_IFOLDING_TRANSFORM(and, andi, 1); +MIPS_MAKE_IFOLDING_TRANSFORM(or, ori, 1); +MIPS_MAKE_IFOLDING_TRANSFORM(sra, srai, 0); +MIPS_MAKE_IFOLDING_TRANSFORM(xor, xori, 1); +MIPS_MAKE_IFOLDING_TRANSFORM(sl, sli, 0); +MIPS_MAKE_IFOLDING_TRANSFORM(sr, sri, 0); +MIPS_MAKE_IFOLDING_TRANSFORM(slt, slti, 0); + +void mips_init_opcode_transforms() { + op_mips_add->ops.transform_node = mips_transform_add; + op_mips_and->ops.transform_node = mips_transform_and; + op_mips_or->ops.transform_node = mips_transform_or; + op_mips_sra->ops.transform_node = mips_transform_sra; + op_mips_xor->ops.transform_node = mips_transform_xor; + op_mips_sl->ops.transform_node = mips_transform_sl; + op_mips_sr->ops.transform_node = mips_transform_sr; + op_mips_slt->ops.transform_node = mips_transform_slt; +} + +/*************************************************************************************** + * _ _ _ + * | | | | | | + * _ __ ___ __| | ___ ___ ___ _ __ ___| |_ _ __ _ _ ___| |_ ___ _ __ ___ + * | '_ \ / _ \ / _` |/ _ \ / __/ _ \| '_ \/ __| __| '__| | | |/ __| __/ _ \| '__/ __| + * | | | | (_) | (_| | __/ | (_| (_) | | | \__ \ |_| | | |_| | (__| || (_) | | \__ \ + * |_| |_|\___/ \__,_|\___| \___\___/|_| |_|___/\__|_| \__,_|\___|\__\___/|_| |___/ + * + ***************************************************************************************/ + +static void mips_register_additional_opcodes(int n) { + /* we don't need any additional opcodes */ +} + +/* Include the generated constructor functions */ +#include "gen_mips_new_nodes.c.inl" diff --git a/ir/be/mips/mips_new_nodes.h b/ir/be/mips/mips_new_nodes.h new file mode 100644 index 000000000..d0219e802 --- /dev/null +++ b/ir/be/mips/mips_new_nodes.h @@ -0,0 +1,113 @@ +#ifndef _mips_NEW_NODES_H_ +#define _mips_NEW_NODES_H_ + +/** + * Function prototypes for the assembler ir node constructors. + * $Id$ + */ + +#include "mips_nodes_attr.h" + +/*************************************************************************************************** + * _ _ _ __ _ _ _ _ + * | | | | | | / / | | | | | | | | + * __ _| |_| |_ _ __ ___ ___| |_ / /_ _ ___| |_ _ __ ___ ___| |_| |__ ___ __| |___ + * / _` | __| __| '__| / __|/ _ \ __| / / _` |/ _ \ __| | '_ ` _ \ / _ \ __| '_ \ / _ \ / _` / __| + * | (_| | |_| |_| | \__ \ __/ |_ / / (_| | __/ |_ | | | | | | __/ |_| | | | (_) | (_| \__ \ + * \__,_|\__|\__|_| |___/\___|\__/_/ \__, |\___|\__| |_| |_| |_|\___|\__|_| |_|\___/ \__,_|___/ + * __/ | + * |___/ + ***************************************************************************************************/ + +/** + * Returns the attributes of an mips node. + */ +mips_attr_t *get_mips_attr(const ir_node *node); + +/** + * Returns the argument register requirements of an mips node. + */ +const mips_register_req_t **get_mips_in_req_all(const ir_node *node); + +/** + * Returns the result register requirements of an mips node. + */ +const mips_register_req_t **get_mips_out_req_all(const ir_node *node); + +/** + * Returns the argument register requirements of an mips node. + */ +const mips_register_req_t *get_mips_in_req(const ir_node *node, int pos); + +/** + * Returns the result register requirements of an mips node. + */ +const mips_register_req_t *get_mips_out_req(const ir_node *node, int pos); + +/** + * Sets the OUT register requirements at position pos. + */ +void set_mips_req_out(ir_node *node, const mips_register_req_t *req, int pos); + +/** + * Sets the IN register requirements at position pos. + */ +void set_mips_req_in(ir_node *node, const mips_register_req_t *req, int pos); + +/** + * Returns the register flag of an mips node. + */ +arch_irn_flags_t get_mips_flags(const ir_node *node); + +/** + * Sets the register flag of an mips node. + */ +void set_mips_flags(const ir_node *node, arch_irn_flags_t flags); + +/** + * Returns the result register slots of an mips node. + */ +const arch_register_t **get_mips_slots(const ir_node *node); + +/** + * Returns the name of the OUT register at position pos. + */ +const char *get_mips_out_reg_name(const ir_node *node, int pos); + +/** + * Returns the index of the OUT register at position pos within its register class. + */ +int get_mips_out_regnr(const ir_node *node, int pos); + +/** + * Returns the OUT register at position pos. + */ +const arch_register_t *get_mips_out_reg(const ir_node *node, int pos); + +/** + * Sets the number of results. + */ +void set_mips_n_res(ir_node *node, int n_res); + +/** + * Returns the number of results. + */ +int get_mips_n_res(const ir_node *node); + + +/** + * Initializes the nodes attributes. + */ +void init_mips_attributes(ir_node *node, arch_irn_flags_t flags, const mips_register_req_t **in_reqs, + const mips_register_req_t **out_reqs, int n_res); + +/** + * Initilize transform ops for the mips opcodes + */ +void mips_init_opcode_transforms(); + + +/* Include the generated headers */ +#include "gen_mips_new_nodes.h" + +#endif /* _mips_NEW_NODES_H_ */ diff --git a/ir/be/mips/mips_nodes_attr.h b/ir/be/mips/mips_nodes_attr.h new file mode 100644 index 000000000..fc44e3da3 --- /dev/null +++ b/ir/be/mips/mips_nodes_attr.h @@ -0,0 +1,35 @@ +#ifndef _MIPS_NODES_ATTR_H_ +#define _MIPS_NODES_ATTR_H_ + +#include "../bearch.h" +#include "irmode_t.h" + +typedef struct _mips_register_req_t { + const arch_register_req_t req; + int same_pos; /**<< in case of "should be same" we need to remember the pos to get the irn */ + int different_pos; /**<< in case of "should be different" we need to remember the pos to get the irn */ +} mips_register_req_t; + + +typedef struct _mips_attr_t { + arch_irn_flags_t flags; /**<< indicating if spillable, rematerializeable ... etc. */ + int n_res; /**<< number of results for this node */ + + tarval *tv; /**<< contains the immediate value (if the node has any) */ + ident *symconst_id; /**<< contains the ident (for la operations) */ + + union { + ir_mode *load_store_mode; /**<< contains the mode of a load/store */ + ir_mode *original_mode; /**<< contains the original mode of the node */ + } modes; + entity *stack_entity; /**<< contains the entity on the stack for a load/store mode */ + int stack_entity_offset; /**<< contains the real stack offset for the entity */ + int switch_default_pn; /**< proj number of default case in switch */ + + const mips_register_req_t **in_req; /**<< register requirements for arguments */ + const mips_register_req_t **out_req; /**<< register requirements for results */ + + const arch_register_t **slots; /**<< register slots for assigned registers */ +} mips_attr_t; + +#endif /* _mips_NODES_ATTR_H_ */ diff --git a/ir/be/mips/mips_scheduler.c b/ir/be/mips/mips_scheduler.c new file mode 100644 index 000000000..b2fdda304 --- /dev/null +++ b/ir/be/mips/mips_scheduler.c @@ -0,0 +1,188 @@ +/* Mips implementation of list scheduler selector */ +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#include "mips_scheduler.h" + +#include "../besched_t.h" +#include "../be.h" +#include "../beabi.h" +#include "iredges.h" +#include "ircons.h" +#include "gen_mips_regalloc_if.h" + +#include "mips_new_nodes.h" + +list_sched_selector_t mips_sched_selector; + +typedef struct { + const arch_env_t* arch_env; + pset *div_set; + /** + * This array holds an entry for each register that specifies how much cycles + * have to pass before we can access that register again + * (because mips will write the register value back in the WB phase of the pipeline) + */ + int busy_registers[N_mips_general_purpose_REGS]; + /// current block + ir_node* block; + ir_node* last_nop; +} mips_sched_env_t; + +static void *mips_scheduler_init_graph(const list_sched_selector_t *vtab, const arch_env_t *arch_env, ir_graph *irg) +{ + mips_sched_env_t *sched_env = xmalloc(sizeof(sched_env[0])); + memset(sched_env, 0, sizeof(sched_env[0])); + + sched_env->arch_env = arch_env; + sched_env->div_set = new_pset(pset_default_ptr_cmp, 4); + + return sched_env; +} + +static void mips_scheduler_finish_graph(void* graph_env) +{ + mips_sched_env_t *sched_env = (mips_sched_env_t*) graph_env; + del_pset(sched_env->div_set); +} + +static void *mips_scheduler_init_block(void *graph_env, ir_node *block) +{ + mips_sched_env_t *sched_env = (mips_sched_env_t*) graph_env; + assert(pset_count(sched_env->div_set) == 0); + srand(12234); + // TODO later we might have blocks that don't end in a jump + memset(&sched_env->busy_registers, 0, sizeof(sched_env->busy_registers)); + sched_env->block = block; + sched_env->last_nop = NULL; + return sched_env; +} + +static void mips_scheduler_finish_block(void* graph_env) +{ + mips_sched_env_t *sched_env = (mips_sched_env_t*) graph_env; + // attach last nop to end node (so that firm doesn't discard it) + if(sched_env->last_nop != NULL) { + ir_node* end = get_irg_end(get_irn_irg(sched_env->block)); + } + sched_env->block = NULL; +} + +static int mips_scheduler_to_appear_in_schedule(void *block_env, const ir_node *irn) +{ + return is_mips_irn(irn) && !is_mips_zero(irn) && !is_mips_reinterpret_conv(irn) && !is_mips_fallthrough(irn); +} + +static void mips_collect_mflohis(pset* set, ir_node* node) { + // construct a list of nodes that need to be scheduled before + // we are allowed to schedule another div or mul instruction + const ir_edge_t *edge, *edge2; + + if(is_mips_div(node)) { + foreach_out_edge(node, edge) { + const ir_node* node2 = get_edge_src_irn(edge); + + assert(is_Proj(node2)); + foreach_out_edge(node2, edge2) { + const ir_node* node3 = get_edge_src_irn(edge2); + if(is_mips_mfhi(node3) || is_mips_mflo(node3)) + pset_insert_ptr(set, node3); + } + } + } else if(is_mips_mult(node)) { + foreach_out_edge(node, edge) { + const ir_node* node2 = get_edge_src_irn(edge); + + if(is_mips_mfhi(node2) || is_mips_mflo(node2)) + pset_insert_ptr(set, node2); + } + } +} + +static int mips_scheduler_node_allowed(mips_sched_env_t *sched_env, ir_node* node) +{ + if(pset_count(sched_env->div_set) != 0 && (is_mips_div(node) || is_mips_mult(node))) { + return 0; + } + + return 1; +} + +static ir_node *mips_scheduler_select(void *block_env, pset *ready_set) +{ + mips_sched_env_t *sched_env = (mips_sched_env_t*) block_env; + const arch_env_t *arch_env = (const arch_env_t*) sched_env->arch_env; + ir_node *node = NULL; + ir_node *block = sched_env->block; + ir_node *condjmp = NULL; + ir_graph *irg = get_irn_irg(block); + int have_non_branch_nodes = 0; + + // test all nodes in the ready set and take the first non-branch that + // is allowed + for(node = pset_first(ready_set); node != NULL; node = pset_next(ready_set)) { + if(arch_irn_classify(arch_env, node) == arch_irn_class_branch) { + if(is_irn_forking(node)) + condjmp = node; + continue; + } + + have_non_branch_nodes = 1; + + if(mips_scheduler_node_allowed(sched_env, node)) + { + pset_break(ready_set); + + // TODO update busy_registers + + if(is_mips_div(node) || is_mips_mult(node)) { + mips_collect_mflohis(sched_env->div_set, node); + } else if(is_mips_mflo(node) || is_mips_mfhi(node)) { + pset_remove_ptr(sched_env->div_set, node); + } + + return node; + } + } + + // if we arrive here no non-branch node was found that we can emit + + // return a branch if there are just branches left + if(!have_non_branch_nodes) { + // schedule conditional branches before non-conditional ones + if(condjmp != NULL) { + return condjmp; + } + node = pset_first(ready_set); + assert(arch_irn_classify(arch_env, node) == arch_irn_class_branch); + pset_break(ready_set); + return node; + } + + // emit a nop + node = new_rd_mips_nop(NULL, irg, block, mode_M); + keep_alive(node); + return node; +} + +/** + * Returns the reg_pressure scheduler with to_appear_in_schedule() overloaded + */ +const list_sched_selector_t *mips_get_list_sched_selector(const void *self) +{ + memset(&mips_sched_selector, 0, sizeof(mips_sched_selector)); + mips_sched_selector.init_graph = mips_scheduler_init_graph; + mips_sched_selector.init_block = mips_scheduler_init_block; + mips_sched_selector.select = mips_scheduler_select; + mips_sched_selector.to_appear_in_schedule = mips_scheduler_to_appear_in_schedule; + mips_sched_selector.finish_block = mips_scheduler_finish_block; + mips_sched_selector.finish_graph = mips_scheduler_finish_graph; + return &mips_sched_selector; +} diff --git a/ir/be/mips/mips_scheduler.h b/ir/be/mips/mips_scheduler.h new file mode 100644 index 000000000..e16a83bbb --- /dev/null +++ b/ir/be/mips/mips_scheduler.h @@ -0,0 +1,8 @@ +#ifndef _MIPS_SCHEDULER_H_ +#define _MIPS_SCHEDULER_H_ + +#include "../besched_t.h" + +const list_sched_selector_t *mips_get_list_sched_selector(const void *self); + +#endif diff --git a/ir/be/mips/mips_spec.pl b/ir/be/mips/mips_spec.pl new file mode 100644 index 000000000..736f0188e --- /dev/null +++ b/ir/be/mips/mips_spec.pl @@ -0,0 +1,606 @@ +# Creation: 2006/02/13 +# $Id$ +# This is a template specification for the Firm-Backend + +# the cpu architecture (ia32, ia64, mips, sparc, ppc, ...) + +$arch = "mips"; + +$comment_string = "#"; + +# The node description is done as a perl hash initializer with the +# following structure: +# +# %nodes = ( +# +# => { +# op_flags => "N|L|C|X|I|F|Y|H|c|K", +# "arity" => "0|1|2|3 ... |variable|dynamic|any", +# "state" => "floats|pinned|mem_pinned|exc_pinned", +# "args" => [ +# { "type" => "type 1", "name" => "name 1" }, +# { "type" => "type 2", "name" => "name 2" }, +# ... +# ], +# "comment" => "any comment for constructor", +# reg_req => { in => [ "reg_class|register" ], out => [ "reg_class|register|in_rX" ] }, +# "cmp_attr" => "c source code for comparing node attributes", +# emit => "emit code with templates", +# "rd_constructor" => "c source code which constructs an ir_node" +# }, +# +# ... # (all nodes you need to describe) +# +# ); # close the %nodes initializer + +# op_flags: flags for the operation, OPTIONAL (default is "N") +# the op_flags correspond to the firm irop_flags: +# N irop_flag_none +# L irop_flag_labeled +# C irop_flag_commutative +# X irop_flag_cfopcode +# I irop_flag_ip_cfopcode +# F irop_flag_fragile +# Y irop_flag_forking +# H irop_flag_highlevel +# c irop_flag_constlike +# K irop_flag_keep +# +# R rematerializeable +# N not spillable +# I ignore for register allocation +# +# state: state of the operation, OPTIONAL (default is "floats") +# +# arity: arity of the operation, MUST NOT BE OMITTED +# +# args: the OPTIONAL arguments of the node constructor (debug, irg and block +# are always the first 3 arguments and are always autmatically +# created) +# If this key is missing the following arguments will be created: +# for i = 1 .. arity: ir_node *op_i +# ir_mode *mode +# +# comment: OPTIONAL comment for the node constructor +# +# rd_constructor: for every operation there will be a +# new_rd__ function with the arguments from above +# which creates the ir_node corresponding to the defined operation +# you can either put the complete source code of this function here +# +# This key is OPTIONAL. If omitted, the following constructor will +# be created: +# if (!op__) assert(0); +# for i = 1 to arity +# set in[i] = op_i +# done +# res = new_ir_node(db, irg, block, op__, mode, arity, in) +# return res +# +# NOTE: rd_constructor and args are only optional if and only if arity is 0,1,2 or 3 + +# register types: +# 0 - no special type +# 1 - caller save (register must be saved by the caller of a function) +# 2 - callee save (register must be saved by the called function) +# 4 - ignore (do not assign this register) +# NOTE: Last entry of each class is the largest Firm-Mode a register can hold\ +%reg_classes = ( + "general_purpose" => [ + { name => "zero", type => 4+2 }, # always zero + { name => "at", type => 4 }, # reserved for assembler + { name => "v0", type => 1 }, # first return value + { name => "v1", type => 1 }, # second return value + { name => "a0", type => 1 }, # first argument + { name => "a1", type => 1 }, # second argument + { name => "a2", type => 1 }, # third argument + { name => "a3", type => 1 }, # fourth argument + { name => "t0", type => 1 }, + { name => "t1", type => 1 }, + { name => "t2", type => 1 }, + { name => "t3", type => 1 }, + { name => "t4", type => 1 }, + { name => "t5", type => 1 }, + { name => "t6", type => 1 }, + { name => "t7", type => 1 }, + { name => "s0", type => 2 }, + { name => "s1", type => 2 }, + { name => "s2", type => 2 }, + { name => "s3", type => 2 }, + { name => "s4", type => 2 }, + { name => "s5", type => 2 }, + { name => "s6", type => 2 }, + { name => "s7", type => 2 }, + { name => "t8", type => 1 }, + { name => "t9", type => 1 }, + { name => "k0", type => 4 }, # reserved for OS + { name => "k1", type => 4 }, # reserved for OS + { name => "gp", type => 4 }, # general purpose + { name => "sp", type => 4+2 }, # stack pointer + { name => "fp", type => 4+2 }, # frame pointer + { name => "ra", type => 2+1 }, # return address. This is also caller save, because + # the jla instruction that is used for calls modifies + # the ra register. It is callee save too, because at the last + # command of a function (the ja $ra) it needs to have it's + # old value. + { mode => "mode_P" } + ], +); # %reg_classes + +#--------------------------------------------------# +# _ # +# (_) # +# _ __ _____ __ _ _ __ ___ _ __ ___ # +# | '_ \ / _ \ \ /\ / / | | '__| / _ \| '_ \/ __| # +# | | | | __/\ V V / | | | | (_) | |_) \__ \ # +# |_| |_|\___| \_/\_/ |_|_| \___/| .__/|___/ # +# | | # +# |_| # +#--------------------------------------------------# + +%nodes = ( + +#-----------------------------------------------------------------# +# _ _ _ # +# (_) | | | | # +# _ _ __ | |_ ___ __ _ ___ _ __ _ __ ___ __| | ___ ___ # +# | | '_ \| __/ _ \/ _` |/ _ \ '__| | '_ \ / _ \ / _` |/ _ \/ __| # +# | | | | | || __/ (_| | __/ | | | | | (_) | (_| | __/\__ \ # +# |_|_| |_|\__\___|\__, |\___|_| |_| |_|\___/ \__,_|\___||___/ # +# __/ | # +# |___/ # +#-----------------------------------------------------------------# + +# commutative operations + +add => { + op_flags => "C", + reg_req => { in => [ "general_purpose", "general_purpose" ], out => [ "general_purpose" ] }, + emit => ' + if (mode_is_signed(get_irn_mode(n))) +2. add %D1, %S1, %S2 + else +2. addu %D1, %S1, %S2 +' +}, + +addi => { + reg_req => { in => [ "general_purpose" ], out => [ "general_purpose" ] }, + emit => ' + if (mode_is_signed(get_irn_mode(n))) +2. addi %D1, %S1, %C + else +2. addiu %D1, %S1, %C +', + cmp_attr => 'return attr_a->tv != attr_b->tv;', +}, + +and => { + op_flags => "C", + reg_req => { in => [ "general_purpose", "general_purpose" ], out => [ "general_purpose" ] }, + emit => '. and %D1, %S1, %S2', +}, + +andi => { + reg_req => { in => [ "general_purpose" ], out => [ "general_purpose" ] }, + emit => '. andi %D1, %S1, %C', + cmp_attr => 'return attr_a->tv != attr_b->tv;', +}, + +div => { + reg_req => { in => [ "general_purpose", "general_purpose" ], out => [ "none", "none", "none", "none" ] }, + emit => ' + mips_attr_t *attr = get_mips_attr(n); + if(attr->modes.original_mode->sign) { +2. div %S1, %S2 + } else { +2. divu %S1, %S2 + } +', +}, + +mult => { + op_flags => "C", + reg_req => { in => [ "general_purpose", "general_purpose" ], out => [ "none" ] }, + emit => ' + if (mode_is_signed(get_irn_mode(n))) +2. mult %S1, %S2 + else +2. multu %S1, %S2 +' +}, + +nor => { + op_flags => "C", + reg_req => { in => [ "general_purpose", "general_purpose" ], out => [ "general_purpose" ] }, + emit => '. nor %D1, %S1, %S2' +}, + +not => { + reg_req => { in => [ "general_purpose" ], out => [ "general_purpose" ] }, + emit => ' + assert(get_mode_size_bits(get_irn_mode(n)) == 32); +. nor %D1, %S1, $zero +' +}, + +or => { + op_flags => "C", + reg_req => { in => [ "general_purpose", "general_purpose" ], out => [ "general_purpose" ] }, + emit => '. or %D1, %S1, %S2' +}, + +ori => { + reg_req => { in => [ "general_purpose" ], out => [ "general_purpose" ] }, + emit => '. ori %D1, %S1, %C', + cmp_attr => 'return attr_a->tv != attr_b->tv;', +}, + +sl => { + reg_req => { in => [ "general_purpose", "general_purpose" ], out => [ "general_purpose" ] }, + emit => ' + if (mode_is_signed(get_irn_mode(n))) +2. sal %D1, %S1, %S2 + else +2. sll %D1, %S1, %S2', +}, + +sli => { + reg_req => { in => [ "general_purpose" ], out => [ "general_purpose" ] }, + emit => ' + if (mode_is_signed(get_irn_mode(n))) +2. sal %D1, %S1, %C + else +2. sll %D1, %S1, %C', +}, + +sra => { + reg_req => { in => [ "general_purpose", "general_purpose" ], out => [ "general_purpose" ] }, + emit => '. sra %D1, %S1, %S2', +}, + +srai => { + reg_req => { in => [ "general_purpose" ], out => [ "general_purpose" ] }, + emit => '. sra %D1, %S1, %C', + cmp_attr => 'return attr_a->tv != attr_b->tv;', +}, + +sr => { + reg_req => { in => [ "general_purpose", "general_purpose" ], out => [ "general_purpose" ] }, + emit => ' + if (mode_is_signed(get_irn_mode(n))) +2. sra %D1, %S1, %S2 + else +2. srl %D1, %S1, %S2 +', +}, + +sri => { + reg_req => { in => [ "general_purpose" ], out => [ "general_purpose" ] }, + emit => ' + if (mode_is_signed(get_irn_mode(n))) +2. sra %D1, %S1, %C + else +2. srl %D1, %S1, %C +', +}, + +srlv => { + reg_req => { in => [ "general_purpose", "general_purpose" ], out => [ "general_purpose" ] }, + emit => '. srlv %D1, %S1, %S2', +}, + +sllv => { + reg_req => { in => [ "general_purpose", "general_purpose" ], out => [ "general_purpose" ] }, + emit => '. sllv %D1, %S1, %S2', +}, + +sub => { + reg_req => { in => [ "general_purpose", "general_purpose" ], out => [ "general_purpose" ] }, + emit => '. sub %D1, %S1, %S2' +}, + +subu => { + reg_req => { in => [ "general_purpose", "general_purpose" ], out => [ "general_purpose" ] }, + emit => '. subu %D1, %S1, %S2', +}, + +subuzero => { + reg_req => { in => [ "general_purpose" ], out => [ "general_purpose" ] }, + emit => '. subu %D1, $zero, %S1', +}, + +xor => { + reg_req => { in => [ "general_purpose", "general_purpose" ], out => [ "general_purpose" ] }, + emit => '. xor %D1, %S1, %S2' +}, + +xori => { + reg_req => { in => [ "general_purpose" ], out => [ "general_purpose" ] }, + emit => '. xori %D1, %S1, %C', + cmp_attr => 'return attr_a->tv != attr_b->tv;', +}, + +# ____ _ _ +# / ___|___ _ __ ___| |_ __ _ _ __ | |_ ___ +# | | / _ \| '_ \/ __| __/ _` | '_ \| __/ __| +# | |__| (_) | | | \__ \ || (_| | | | | |_\__ \ +# \____\___/|_| |_|___/\__\__,_|_| |_|\__|___/ +# + +# load upper imediate +lui => { + op_flags => "c", + reg_req => { out => [ "general_purpose" ] }, + emit => '. lui %D1, %C', + cmp_attr => 'return attr_a->tv != attr_b->tv;', +}, + +# load lower immediate +lli => { + op_flags => "c", + reg_req => { in => [ "general_purpose" ], out => [ "general_purpose" ] }, + emit => '. ori %D1, %S1, %C', + cmp_attr => 'return attr_a->tv != attr_b->tv;', +}, + +la => { + op_flags => "c", + reg_req => { out => [ "general_purpose" ] }, + emit => '. la %D1, %C', + cmp_attr => 'return attr_a->symconst_id != attr_b->symconst_id;', +}, + +mflo => { + reg_req => { in => [ "none" ], out => [ "general_purpose" ] }, + emit => '. mflo %D1' +}, + +mfhi => { + reg_req => { in => [ "none" ], out => [ "general_purpose" ] }, + emit => '. mfhi %D1' +}, + +zero => { + reg_req => { out => [ "zero" ] }, + emit => '', +}, + +# +# ____ _ __ _ +# | __ ) _ __ __ _ _ __ ___| |__ / / | |_ _ _ __ ___ _ __ +# | _ \| '__/ _` | '_ \ / __| '_ \ / / | | | | | '_ ` _ \| '_ \ +# | |_) | | | (_| | | | | (__| | | |/ / |_| | |_| | | | | | | |_) | +# |____/|_| \__,_|_| |_|\___|_| |_/_/ \___/ \__,_|_| |_| |_| .__/ +# |_| +# + +slt => { + reg_req => { in => [ "general_purpose", "general_purpose" ], out => [ "general_purpose" ] }, + emit => ' + if (mode_is_signed(get_irn_mode(n))) +2. slt %D1, %S1, %S2 + else +2. sltu %D1, %S1, %S2 +', +}, + +slti => { + reg_req => { in => [ "general_purpose" ], out => [ "general_purpose" ] }, + emit => ' + if (mode_is_signed(get_irn_mode(n))) +2. slti %D1, %S1, %C + else +2. sltiu %D1, %S1, %C +', + cmp_attr => 'return attr_a->tv != attr_b->tv;', +}, + +beq => { + op_flags => "X|Y", + # TxT -> TxX + reg_req => { in => [ "general_purpose", "general_purpose" ], out => [ "in_r0", "none" ] }, + emit => ' + ir_node *jumpblock = mips_get_jump_block(n, 1); + assert(jumpblock != NULL); + + lc_efprintf(arg_env, F, "\tbeq %1S, %2S, BLOCK_%d\n", n, n, get_irn_node_nr(jumpblock)); +' +}, + +bne => { + op_flags => "X|Y", + # TxT -> TxX + reg_req => { in => [ "general_purpose", "general_purpose" ], out => [ "in_r0", "none" ] }, + emit => ' + ir_node *jumpblock = mips_get_jump_block(n, 1); + assert(jumpblock != NULL); + + lc_efprintf(arg_env, F, "\tbne %1S, %2S, BLOCK_%d\n", n, n, get_irn_node_nr(jumpblock)); +' +}, + +bgtz => { + op_flags => "X|Y", + # TxT -> TxX + reg_req => { in => [ "general_purpose" ], out => [ "in_r0", "none" ] }, + emit => ' + ir_node *jumpblock = mips_get_jump_block(n, 1); + assert(jumpblock != NULL); + + lc_efprintf(arg_env, F, "\tbgtz %1S, BLOCK_%d\n", n, get_irn_node_nr(jumpblock)); +' +}, + +blez => { + op_flags => "X|Y", + # TxT -> TxX + reg_req => { in => [ "general_purpose" ], out => [ "in_r0", "none" ] }, + emit => ' + ir_node *jumpblock = mips_get_jump_block(n, 1); + assert(jumpblock != NULL); + + lc_efprintf(arg_env, F, "\tblez %1S, BLOCK_%d\n", n, get_irn_node_nr(jumpblock)); +' +}, + +j => { + op_flags => "X", + reg_req => { in => [ "general_purpose" ] }, + emit => '. j %S1', +}, + +b => { + op_flags => "X", + # -> X + reg_req => { in => [ ], out => [ "none" ] }, + emit => ' + ir_node *jumpblock = get_irn_link(n); + assert(jumpblock != NULL); + + lc_efprintf(arg_env, F, "\tb BLOCK_%d\t\t\t# mips_b\n", get_irn_node_nr(jumpblock)); +' +}, + +fallthrough => { + op_flags => "X", + # -> X + reg_req => { in => [ ], out => [ "none" ] }, + emit => '. # fallthrough' +}, + +SwitchJump => { + op_flags => "X", + # -> X,X,... + reg_req => { in => [ "general_purpose" ], out => [ "none" ] }, + emit => '. j %S1' +}, + +# _ _ +# | | ___ __ _ __| | +# | | / _ \ / _` |/ _` | +# | |__| (_) | (_| | (_| | +# |_____\___/ \__,_|\__,_| +# + +load_r => { + reg_req => { in => [ "none", "general_purpose" ], out => [ "none", "none", "general_purpose" ] }, + emit => ' + mips_attr_t* attr = get_mips_attr(n); + ir_mode *mode; + + mode = attr->modes.load_store_mode; + + switch (get_mode_size_bits(mode)) { + case 8: + if (mode_is_signed(mode)) +3. lb %D3, %C(%S2) + else +3. lbu %D3, %C(%S2) + break; + case 16: + if (mode_is_signed(mode)) +3. lh %D3, %C(%S2) + else +3. lhu %D3, %C(%S2) + break; + case 32: +2. lw %D3, %C(%S2) + break; + default: + assert(! "Only 8, 16 and 32 bit loads supported"); + break; + } +', + cmp_attr => 'return attr_a->tv != attr_b->tv || attr_a->stack_entity != attr_b->stack_entity;', +}, + + +# _ _ ______ _ +# | | ___ __ _ __| | / / ___|| |_ ___ _ __ ___ +# | | / _ \ / _` |/ _` | / /\___ \| __/ _ \| '__/ _ \ +# | |__| (_) | (_| | (_| |/ / ___) | || (_) | | | __/ +# |_____\___/ \__,_|\__,_/_/ |____/ \__\___/|_| \___| +# + +store_r => { + reg_req => { in => [ "none", "general_purpose", "general_purpose" ], out => [ "none", "none" ] }, + emit => ' + mips_attr_t* attr = get_mips_attr(n); + ir_mode* mode; + + mode = attr->modes.load_store_mode; + + switch (get_mode_size_bits(mode)) { + case 8: + if (mode_is_signed(mode)) +2. sb %S3, %C(%S2) + break; + case 16: + if (mode_is_signed(mode)) +2. sh %S3, %C(%S2) + break; + case 32: +2. sw %S3, %C(%S2) + break; + default: + assert(! "Only 8, 16 and 32 bit stores supported"); + break; + } +', + cmp_attr => 'return attr_a->tv != attr_b->tv;', +}, + +store_i => { + reg_req => { in => [ "none", "none", "general_purpose" ], out => [ "none", "none" ] }, + emit => ' + mips_attr_t* attr = get_mips_attr(n); + ir_mode *mode; + + mode = attr->modes.load_store_mode; + + switch (get_mode_size_bits(mode)) { + case 8: +2. sb %S3, %C + break; + case 16: +2. sh %S3, %C + break; + case 32: +2. sw %S3, %C + break; + default: + assert(! "Only 8, 16 and 32 bit stores supported"); + break; + } +', + cmp_attr => ' + return attr_a->stack_entity != attr_b->stack_entity; +', +}, + +move => { + reg_req => { in => [ "general_purpose" ], out => [ "general_purpose" ] }, + emit => '. or %D1, $zero, %S1' +}, + +# +# Conversion +# + +reinterpret_conv => { + reg_req => { in => [ "general_purpose" ], out => [ "in_r1" ] }, + emit => '. # reinterpret %S1 -> %D1', +}, + +# +# Miscelaneous +# + +nop => { + op_flags => "K", + reg_req => { in => [], out => [ "none" ] }, + emit => '. nop # nop', +}, + +); # end of %nodes diff --git a/ir/be/mips/mips_transform.c b/ir/be/mips/mips_transform.c new file mode 100644 index 000000000..d5dc9c629 --- /dev/null +++ b/ir/be/mips/mips_transform.c @@ -0,0 +1,1189 @@ +/* The codegenrator (transform FIRM into mips FIRM */ +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "irnode_t.h" +#include "irgraph_t.h" +#include "irmode_t.h" +#include "irgmod.h" +#include "iredges.h" +#include "irvrfy.h" +#include "ircons.h" +#include "dbginfo.h" +#include "iropt_t.h" +#include "debug.h" + +#include "../benode_t.h" +#include "../beabi.h" +#include "../besched.h" +#include "../besched_t.h" +#include "bearch_mips_t.h" + +#include "mips_nodes_attr.h" +#include "../arch/archop.h" /* we need this for Min and Max nodes */ +#include "mips_transform.h" +#include "mips_new_nodes.h" +#include "mips_map_regs.h" +#include "mips_util.h" +#include "mips_emitter.h" + +#include "gen_mips_regalloc_if.h" + +/**************************************************************************************************** + * _ _ __ _ _ + * | | | | / _| | | (_) + * _ __ ___ __| | ___ | |_ _ __ __ _ _ __ ___| |_ ___ _ __ _ __ ___ __ _| |_ _ ___ _ __ + * | '_ \ / _ \ / _` |/ _ \ | __| '__/ _` | '_ \/ __| _/ _ \| '__| '_ ` _ \ / _` | __| |/ _ \| '_ \ + * | | | | (_) | (_| | __/ | |_| | | (_| | | | \__ \ || (_) | | | | | | | | (_| | |_| | (_) | | | | + * |_| |_|\___/ \__,_|\___| \__|_| \__,_|_| |_|___/_| \___/|_| |_| |_| |_|\__,_|\__|_|\___/|_| |_| + * + ****************************************************************************************************/ + +#define MIPS_GENBINFUNC(mips_nodetype) \ + static ir_node* mips_gen_##mips_nodetype(mips_transform_env_t *env, ir_node *op1, ir_node *op2) {\ + ASSERT_NO_FLOAT(env->mode); \ + /*assert(get_irn_mode(op1) == get_irn_mode(op2));*/ \ + /*assert(get_irn_mode(op1) == env->mode);*/ \ + assert(get_mode_size_bits(env->mode) == 32); \ + return new_rd_mips_##mips_nodetype(env->dbg, env->irg, env->block, op1, op2, env->mode);\ + } + +MIPS_GENBINFUNC(add); +MIPS_GENBINFUNC(sub); +MIPS_GENBINFUNC(div); +MIPS_GENBINFUNC(and); +MIPS_GENBINFUNC(or); +MIPS_GENBINFUNC(xor); +MIPS_GENBINFUNC(sl); +MIPS_GENBINFUNC(sr); +MIPS_GENBINFUNC(sra); + +#define MIPS_GENUNFUNC(mips_nodetype) \ + static ir_node *mips_gen_##mips_nodetype(mips_transform_env_t *env, ir_node *op) { \ + ASSERT_NO_FLOAT(env->mode); \ + assert(get_irn_mode(op) == env->mode); \ + assert(get_mode_size_bits(env->mode) == 32); \ + return new_rd_mips_##mips_nodetype(env->dbg, env->irg, env->block, op, env->mode); \ + } + +MIPS_GENUNFUNC(not); + +static ir_node* mips_get_reg_node(mips_transform_env_t *env, const arch_register_t *reg) { + return be_abi_get_callee_save_irn(env->cg->birg->abi, reg); +} + +static ir_node* gen_zero_node(mips_transform_env_t *env, dbg_info *ebg, ir_graph *irg, ir_node *block) +{ + ir_node *zero = be_abi_get_callee_save_irn(env->cg->birg->abi, &mips_general_purpose_regs[REG_ZERO]); + // TODO make zero nodes work + //ir_node *unknown = new_rd_mips_zero(dbg, irg, block, mode); + + return zero; +} + +static ir_node* gen_node_for_Const(mips_transform_env_t *env, dbg_info *dbg, ir_graph *irg, ir_node *block, ir_node *constant) +{ + tarval* tv = get_Const_tarval(constant); + ir_node *lui; + ir_node *lli; + mips_attr_t *attr; + ir_mode* mode = get_irn_mode(constant); + unsigned long val, lower, upper; + + val = get_tarval_long(tv); + if(val == 0) + return gen_zero_node(env, dbg, irg, block); + + lower = val & 0xffff; + upper = (val >> 16) & 0xffff; + if(upper == 0) { + ir_node *zero = gen_zero_node(env, dbg, irg, block); + ir_node *lli = new_rd_mips_lli(dbg, irg, block, zero, mode); + attr = get_mips_attr(lli); + attr->tv = new_tarval_from_long(val, mode); + + return lli; + } + + lui = new_rd_mips_lui(dbg, irg, block, mode); + attr = get_mips_attr(lui); + attr->tv = new_tarval_from_long(val, mode); + + if(lower == 0) + return lui; + + lli = new_rd_mips_lli(dbg, irg, block, lui, mode); + attr = get_mips_attr(lli); + attr->tv = new_tarval_from_long(val, mode); + + return lli; +} + +static ir_node* exchange_node_for_Const(mips_transform_env_t *env, ir_node* pred, int n) { + ir_node *node = env->irn; + dbg_info *dbg = get_irn_dbg_info(pred); + ir_graph *irg = get_irn_irg(node); + ir_mode* mode = get_irn_mode(pred); + ir_node *block; + + if(get_irn_opcode(node) == iro_Phi) { + ir_node *phipred = get_nodes_block(node); + block = get_Block_cfgpred_block(phipred, n); + } else { + block = get_nodes_block(node); + } + + return gen_node_for_Const(env, dbg, irg, block, pred); +} + +static ir_node* gen_node_for_SymConst(mips_transform_env_t *env, ir_node* pred, int n) { + ir_node *result; + symconst_kind kind; + mips_attr_t *attr; + ir_node *node = env->irn; + dbg_info *dbg = get_irn_dbg_info(pred); + ir_graph *irg = get_irn_irg(node); + ir_mode* mode = get_irn_mode(pred); + ir_node *block; + + if (is_Phi(node)) { + ir_node *phipred = get_nodes_block(node); + block = get_Block_cfgpred_block(phipred, n); + } else { + block = get_nodes_block(node); + } + + kind = get_SymConst_kind(pred); + if(kind == symconst_addr_ent) { + result = new_rd_mips_la(dbg, irg, block, mode); + attr = get_mips_attr(result); + attr->symconst_id = get_entity_ld_ident(get_SymConst_entity(pred)); + return result; + } else if(kind == symconst_addr_name) { + result = new_rd_mips_la(dbg, irg, block, mode); + attr = get_mips_attr(result); + attr->symconst_id = get_SymConst_name(pred); + return result; + } + + // TODO + assert(0); + return NULL; +} + +/** + * Generates a mips node for a firm Load node + */ +static ir_node *gen_node_for_Load(mips_transform_env_t *env) { + ir_node *node = env->irn; + ir_node *result = NULL; + ir_mode *mode; + ir_node *load_ptr; + mips_attr_t *attr; + + ASSERT_NO_FLOAT(get_irn_mode(node)); + + mode = get_Load_mode(node); + assert(mode->vector_elem == 1); + assert(mode->sort == irms_int_number || mode->sort == irms_reference); + + load_ptr = get_Load_ptr(node); + assert(get_mode_sort(mode) == irms_reference || get_mode_sort(mode) == irms_int_number); + result = new_rd_mips_load_r(env->dbg, env->irg, env->block, + get_Load_mem(node), load_ptr, get_irn_mode(node)); + + attr = get_mips_attr(result); + attr->tv = new_tarval_from_long(0, mode_Iu); + attr->modes.load_store_mode = mode; + + return result; +} + +/** + * Generates a mips node for a firm Store node + */ +static ir_node *gen_node_for_Store(mips_transform_env_t *env) { + ir_node *node = env->irn; + ir_node *result = NULL; + ir_mode *mode; + mips_attr_t *attr; + ir_node *store_ptr; + + ASSERT_NO_FLOAT(env->mode); + + store_ptr = get_Store_ptr(node); + mode = get_irn_mode(store_ptr); + assert(mode->vector_elem == 1); + assert(mode->sort == irms_int_number || mode->sort == irms_reference); + + if(get_irn_opcode(store_ptr) == iro_SymConst) { + result = new_rd_mips_store_i(env->dbg, env->irg, env->block, get_Store_mem(node), + get_Store_ptr(node), get_Store_value(node), env->mode); + } else { + result = new_rd_mips_store_r(env->dbg, env->irg, env->block, get_Store_mem(node), + get_Store_ptr(node), get_Store_value(node), env->mode); + } + attr = get_mips_attr(result); + attr->tv = new_tarval_from_long(0, mode_Iu); + attr->modes.load_store_mode = mode; + + return result; +} + +static ir_node *gen_node_for_div_Proj(mips_transform_env_t *env) { + ir_node *proj = env->irn; + ir_node *new_proj; + ir_node *pred = get_irn_n(proj, 0); + mips_attr_t *attr; + long n; + + n = get_Proj_proj(proj); + + // set the div mode to the DivMod node + attr = get_mips_attr(pred); + assert(attr->modes.original_mode == NULL || attr->modes.original_mode == env->mode); + attr->modes.original_mode = env->mode; + + // we have to construct a new proj here, to avoid circular refs that + // happen when we reuse the old one + new_proj = new_ir_node(env->dbg, env->irg, env->block, op_Proj, mode_ANY, 1, &pred); + set_Proj_proj(new_proj, n); + + if(n == pn_DivMod_res_div) { + return new_rd_mips_mflo(env->dbg, env->irg, env->block, new_proj, env->mode); + } else if(n == pn_DivMod_res_mod) { + return new_rd_mips_mfhi(env->dbg, env->irg, env->block, new_proj, env->mode); + } + + return proj; +} + +static ir_node *make_jmp_or_fallthrough(mips_transform_env_t *env) +{ + const ir_edge_t *edge; + ir_node *node = env->irn; + ir_node *next_block; + int our_block_sched_nr = mips_get_block_sched_nr(get_nodes_block(node)); + + edge = get_irn_out_edge_first(node); + next_block = get_edge_src_irn(edge); + + if(mips_get_sched_block(env->cg, our_block_sched_nr + 1) == next_block) { + return new_rd_mips_fallthrough(env->dbg, env->irg, env->block, mode_X); + } + + return new_rd_mips_b(env->dbg, env->irg, env->block, mode_X); +} + +static ir_node *gen_node_for_Cond_Proj(mips_transform_env_t *env, ir_node* node, int true_false) +{ + // we can't use get_Cond_selector here because the selector is already + // replaced by a mips_ compare node + ir_node *proj = get_Cond_selector(node); + ir_node *original_cmp = get_irn_n(proj, 0); + ir_node *result = NULL; + ir_node *cmp; + ir_node *condjmp; + ir_node *op1, *op2; + dbg_info *dbg = env->dbg; + ir_graph *irg = env->irg; + ir_node *block = env->block; + long n; + + n = get_Proj_proj(proj); + assert(n < 8 && "Only ordered comps supported"); + + assert(get_irn_opcode(original_cmp) == iro_Cmp); + op1 = get_Cmp_left(original_cmp); + op2 = get_Cmp_right(original_cmp); + + switch(n) { + case pn_Cmp_False: + if(true_false) + return NULL; + + return make_jmp_or_fallthrough(env); + + case pn_Cmp_Eq: + if(!true_false) + return make_jmp_or_fallthrough(env); + + condjmp = new_rd_mips_beq(dbg, irg, block, op1, op2, mode_T); + return new_rd_Proj(dbg, irg, block, condjmp, mode_X, 1); + + case pn_Cmp_Lt: + if(!true_false) + return make_jmp_or_fallthrough(env); + + cmp = new_rd_mips_slt(dbg, irg, block, op1, op2, get_irn_mode(op1)); + condjmp = new_rd_mips_bgtz(dbg, irg, block, cmp, mode_T); + return new_rd_Proj(dbg, irg, block, condjmp, mode_X, 1); + + case pn_Cmp_Le: + if(!true_false) + return make_jmp_or_fallthrough(env); + + cmp = new_rd_mips_slt(dbg, irg, block, op2, op1, get_irn_mode(op1)); + condjmp = new_rd_mips_blez(dbg, irg, block, cmp, mode_T); + return new_rd_Proj(dbg, irg, block, condjmp, mode_X, 1); + + case pn_Cmp_Gt: + if(!true_false) + return make_jmp_or_fallthrough(env); + + cmp = new_rd_mips_slt(dbg, irg, block, op2, op1, get_irn_mode(op1)); + condjmp = new_rd_mips_bgtz(dbg, irg, block, cmp, mode_T); + return new_rd_Proj(dbg, irg, block, condjmp, mode_X, 1); + + case pn_Cmp_Ge: + if(!true_false) + return make_jmp_or_fallthrough(env); + + cmp = new_rd_mips_slt(dbg, irg, block, op1, op2, get_irn_mode(op1)); + condjmp = new_rd_mips_blez(dbg, irg, block, cmp, mode_T); + return new_rd_Proj(dbg, irg, block, condjmp, mode_X, 1); + + case pn_Cmp_Lg: + if(!true_false) + return make_jmp_or_fallthrough(env); + + condjmp = new_rd_mips_bne(dbg, irg, block, op1, op2, mode_T); + return new_rd_Proj(dbg, irg, block, condjmp, mode_X, 1); + + case pn_Cmp_Leg: + if(!true_false) + return NULL; + + return make_jmp_or_fallthrough(env); + + default: + assert(0); + } + + return NULL; +} + +static ir_node *gen_node_for_Proj(mips_transform_env_t *env) +{ + ir_node *proj = env->irn; + long n; + ir_node *predecessor = get_Proj_pred(proj); + + // all DivMods, Div, Mod should be replaced by now + assert(get_irn_opcode(predecessor) != iro_DivMod); + assert(get_irn_opcode(predecessor) != iro_Div); + assert(get_irn_opcode(predecessor) != iro_Mod); + + if(is_mips_div(predecessor)) + return gen_node_for_div_Proj(env); + + if(get_irn_opcode(predecessor) == iro_Cond) { + ir_node *selector = get_Cond_selector(predecessor); + ir_mode *mode = get_irn_mode(selector); + n = get_Proj_proj(proj); + + if(get_mode_sort(mode) == irms_internal_boolean) { + assert(n == pn_Cond_true || n == pn_Cond_false); + return gen_node_for_Cond_Proj(env, predecessor, n == pn_Cond_true); + } + } + + return proj; +} + +static ir_node *gen_node_for_Cond(mips_transform_env_t *env) +{ + ir_node *selector = get_Cond_selector(env->irn); + ir_mode *selector_mode = get_irn_mode(selector); + ir_node *node = env->irn; + dbg_info *dbg = env->dbg; + ir_graph *irg = env->irg; + ir_node *block = env->block; + ir_node *sub, *sltu, *minval_const, *max_const, *switchjmp; + ir_node *defaultproj, *defaultproj_succ; + ir_node *beq, *sl; + long pn, minval, maxval, defaultprojn; + const ir_edge_t *edge; + ir_node *zero, *two_const, *add, *la, *load, *proj; + ir_mode *unsigned_mode; + mips_attr_t *attr; + + // mode_b conds are handled by gen_node_for_Proj + if(get_mode_sort(selector_mode) != irms_int_number) + return env->irn; + + assert(get_mode_size_bits(selector_mode) == 32); + + defaultprojn = get_Cond_defaultProj(node); + + // go over all projs to find min-&maxval of the switch + minval = INT_MAX; + maxval = INT_MIN; + foreach_out_edge(node, edge) { + ir_node* proj = get_edge_src_irn(edge); + assert(is_Proj(proj) && "Only proj allowed at SwitchJmp"); + + pn = get_Proj_proj(proj); + if(pn == defaultprojn) { + defaultproj = proj; + continue; + } + + if(pn < minval) + minval = pn; + if(pn > maxval) + maxval = pn; + } + assert(defaultproj != NULL); + + // subtract minval from the switch value + + if(minval != 0) { + minval_const = new_rd_Const(dbg, irg, block, selector_mode, new_tarval_from_long(minval, selector_mode)); + minval_const = gen_node_for_Const(env, dbg, irg, block, minval_const); + sub = new_rd_mips_sub(dbg, irg, block, selector, minval_const, selector_mode); + } else { + sub = selector; + } + + // compare if we're above maxval-minval or below zero. + // we can do this with 1 compare because we use unsigned mode + unsigned_mode = new_ir_mode(get_mode_name(selector_mode), + get_mode_sort(selector_mode), get_mode_size_bits(selector_mode), + 0, get_mode_arithmetic(selector_mode), get_mode_modulo_shift(selector_mode)); + + max_const = new_rd_Const(dbg, irg, block, unsigned_mode, new_tarval_from_long(maxval - minval + 1, unsigned_mode)); + max_const = gen_node_for_Const(env, dbg, irg, block, max_const); + sltu = new_rd_mips_slt(dbg, irg, block, sub, max_const, unsigned_mode); + + zero = gen_zero_node(env, dbg, irg, block); + beq = new_rd_mips_beq(dbg, irg, block, sltu, zero, mode_T); + + // attach defaultproj to beq now + set_irn_n(defaultproj, 0, beq); + set_Proj_proj(defaultproj, 1); + + two_const = new_rd_Const(dbg, irg, block, unsigned_mode, new_tarval_from_long(2, unsigned_mode)); + two_const = gen_node_for_Const(env, dbg, irg, block, two_const); + sl = new_rd_mips_sl(dbg, irg, block, sub, two_const, unsigned_mode); + + la = new_rd_mips_la(dbg, irg, block, mode_Iu); + add = new_rd_mips_add(dbg, irg, block, sl, la, mode_Iu); + load = new_rd_mips_load_r(dbg, irg, block, new_rd_NoMem(irg), add, mode_T); + attr = get_mips_attr(load); + attr->modes.load_store_mode = mode_Iu; + attr->tv = new_tarval_from_long(0, mode_Iu); + + proj = new_rd_Proj(dbg, irg, block, load, mode_Iu, pn_Load_res); + + switchjmp = new_rd_mips_SwitchJump(dbg, irg, block, proj, mode_T); + attr = get_mips_attr(switchjmp); + attr->switch_default_pn = defaultprojn; + + edge = get_irn_out_edge_first(defaultproj); + defaultproj_succ = get_edge_src_irn(edge); + attr->symconst_id = new_id_from_str(mips_get_block_label(defaultproj_succ)); + + attr = get_mips_attr(la); + attr->symconst_id = new_id_from_str(mips_get_jumptbl_label(switchjmp)); + + return switchjmp; +} + +static ir_node *create_conv_store_load(mips_transform_env_t *env, ir_mode* srcmode, ir_mode* dstmode) { + ir_node *nomem, *store, *mem_proj, *value_proj, *load; + entity *mem_entity; + ir_node *node = env->irn; + ir_node *pred = get_Conv_op(node); + ir_node *sp; + // TODO HACK make this global... + ident* id; + ir_type *i32type; + ir_type *ptr_i32type; + mips_attr_t* attr; + + id = new_id_from_str("__conv0"); + i32type = new_type_primitive(new_id_from_str("ptr32"), mode_Iu); + ptr_i32type = new_d_type_pointer(id, i32type, mode_P, env->dbg); + mem_entity = new_d_entity(get_irg_frame_type(env->irg), id, ptr_i32type, env->dbg); + + sp = mips_get_reg_node(env, &mips_general_purpose_regs[REG_SP]); + nomem = new_ir_node(env->dbg, env->irg, env->block, op_NoMem, mode_M, 0, NULL); + + store = new_rd_mips_store_r(env->dbg, env->irg, env->block, nomem, sp, pred, mode_T); + attr = get_mips_attr(store); + attr->tv = new_tarval_from_long(0, mode_Iu); + attr->modes.load_store_mode = srcmode; + attr->stack_entity = mem_entity; + + mem_proj = new_ir_node(env->dbg, env->irg, env->block, op_Proj, mode_M, 1, &store); + set_Proj_proj(mem_proj, pn_Store_M); + + load = new_rd_mips_load_r(env->dbg, env->irg, env->block, mem_proj, sp, mode_T); + attr = get_mips_attr(load); + attr->tv = new_tarval_from_long(0, mode_Iu); + attr->modes.load_store_mode = dstmode; + attr->stack_entity = mem_entity; + + value_proj = new_ir_node(env->dbg, env->irg, env->block, op_Proj, env->mode, 1, &load); + set_Proj_proj(value_proj, pn_Load_res); + + return value_proj; +} + +static ir_node *create_conv_and(mips_transform_env_t *env, long immediate) { + ir_node *node = env->irn; + ir_node *pred; + ir_node *result; + mips_attr_t *attr; + + pred = get_Conv_op(node); + result = new_rd_mips_andi(env->dbg, env->irg, env->block, pred, node->mode); + attr = get_mips_attr(result); + attr->tv = new_tarval_from_long(immediate, mode_Iu); + + return result; +} + +static ir_node *gen_node_for_Conv(mips_transform_env_t *env) { + ir_node *node = env->irn; + ir_node *pred; + ir_mode *srcmode; + ir_mode *destmode; + int dst_size, src_size; + + pred = get_Conv_op(node); + srcmode = get_irn_mode(pred); + destmode = get_irn_mode(node); + + dst_size = get_mode_size_bits(destmode); + src_size = get_mode_size_bits(srcmode); + + if(srcmode->size >= destmode->size) { + assert(srcmode->size > destmode->size || srcmode->sign != destmode->sign); + return new_rd_mips_reinterpret_conv(env->dbg, env->irg, env->block, pred, node->mode); + } + if(srcmode->sign) { + if(srcmode->size == 8) { + return create_conv_store_load(env, mode_Bs, mode_Bs); + } else if(srcmode->size == 16) { + return create_conv_store_load(env, mode_Hs, mode_Hs); + } + } else { + if(src_size == 8) { + return create_conv_and(env, 0xff); + } else if(src_size == 16) { + return create_conv_and(env, 0xffff); + } + } + + assert(0); + return NULL; +} + +static ir_node *gen_node_mips_div(mips_transform_env_t *env, ir_node* op1, ir_node* op2, long p_div, long p_mod, + long p_m, long p_x) +{ + ir_node *node = env->irn; + ir_node *div; + const ir_edge_t *edge; + + div = new_rd_mips_div(env->dbg, env->irg, env->block, op1, op2, mode_T); + + // Adjust div projs + foreach_out_edge(node, edge) { + ir_node *proj = get_edge_src_irn(edge); + long n = get_Proj_proj(proj); + assert(is_Proj(proj) && "non-Proj from Mod node"); + if (n == p_div) { + set_Proj_proj(proj, pn_DivMod_res_div); + } else if (n == p_mod) { + set_Proj_proj(proj, pn_DivMod_res_mod); + } else if(n == p_m) { + set_Proj_proj(proj, pn_DivMod_M); + } else if(n == p_x) { + set_Proj_proj(proj, pn_DivMod_X_except); + } else { + assert(!"invalid proj"); + } + } + + return div; +} + +static ir_node *gen_node_for_DivMod(mips_transform_env_t *env) { + ir_node *node = env->irn; + + return gen_node_mips_div(env, get_DivMod_left(node), get_DivMod_right(node), pn_DivMod_res_div, + pn_DivMod_res_mod, pn_DivMod_M, pn_DivMod_X_except); +} + +static ir_node *gen_node_for_Div(mips_transform_env_t *env) { + ir_node *node = env->irn; + + return gen_node_mips_div(env, get_Div_left(node), get_Div_right(node), pn_Div_res, -1, + pn_Div_M, pn_Div_X_except); +} + +static ir_node *gen_node_for_Mod(mips_transform_env_t *env) { + ir_node *node = env->irn; + + return gen_node_mips_div(env, get_Mod_left(node), get_Mod_right(node), -1, pn_Mod_res, + pn_Mod_M, pn_Mod_X_except); +} + +static ir_node *gen_node_for_Mul(mips_transform_env_t *env) { + ir_node *node = env->irn; + ir_node *mul; + ir_node *mflo; + ir_node *op1, *op2; + + op1 = get_Mul_left(node); + op2 = get_Mul_right(node); + + assert(get_mode_size_bits(env->mode) == 32); + assert(get_mode_size_bits(get_irn_mode(op1)) == get_mode_size_bits(env->mode)); + assert(get_mode_size_bits(get_irn_mode(op2)) == get_mode_size_bits(env->mode)); + + mul = new_rd_mips_mult(env->dbg, env->irg, env->block, get_Mul_left(node), get_Mul_right(node), env->mode); + mflo = new_rd_mips_mflo(env->dbg, env->irg, env->block, mul, env->mode); + + return mflo; +} + +static ir_node *gen_node_for_IJmp(mips_transform_env_t *env) { + ir_node *node = env->irn; + + return new_rd_mips_j(env->dbg, env->irg, env->block, get_IJmp_target(node), node->mode); +} + +static ir_node *gen_node_for_Jmp(mips_transform_env_t *env) { + return make_jmp_or_fallthrough(env); +} + +static ir_node *gen_node_for_Abs(mips_transform_env_t *env) { + ir_node *node = env->irn; + ir_node *sra, *add, *xor; + mips_attr_t *attr; + + // TODO for other bit sizes... + assert(get_mode_size_bits(env->mode) == 32); + sra = new_rd_mips_srai(env->dbg, env->irg, env->block, get_Abs_op(node), node->mode); + attr = get_mips_attr(sra); + attr->tv = new_tarval_from_long(31, mode_Iu); + add = new_rd_mips_add(env->dbg, env->irg, env->block, get_Abs_op(node), sra, node->mode); + xor = new_rd_mips_xor(env->dbg, env->irg, env->block, sra, add, node->mode); + + return xor; +} + +static ir_node *gen_node_for_Rot(mips_transform_env_t *env) { + ir_node *node = env->irn; + ir_node *subu, *srlv, *sllv, *or; + + subu = new_rd_mips_subuzero(env->dbg, env->irg, env->block, get_Rot_right(node), env->mode); + srlv = new_rd_mips_srlv(env->dbg, env->irg, env->block, get_Rot_left(node), subu, env->mode); + sllv = new_rd_mips_sllv(env->dbg, env->irg, env->block, get_Rot_left(node), get_Rot_right(node), env->mode); + or = new_rd_mips_or(env->dbg, env->irg, env->block, sllv, srlv, env->mode); + + return or; +} + +static ir_node *gen_node_for_Unknown(mips_transform_env_t *env) +{ + return gen_zero_node(env, env->dbg, env->irg, env->block); +} + +/* + * lower a copyB into standard Firm assembler :-) + */ +ir_node *gen_code_for_CopyB(ir_node *block, ir_node *node) { + ir_node *cnt, *sub; + ir_node *dst = get_CopyB_dst(node); + ir_node *src = get_CopyB_src(node); + ir_type *type = get_CopyB_type(node); + ir_node *mem = get_CopyB_mem(node); + ir_node *mm[4]; + ir_node *result = NULL; + int size = get_type_size_bytes(type); + dbg_info *dbg = get_irn_dbg_info(node); + ir_graph *irg = get_irn_irg(block); + mips_attr_t *attr; + int i, n; + + if (size > 16) { + ir_node *phi, *projT, *projF, *cmp, *proj, *cond, *jmp, *in[2]; + ir_node *new_bl, *src_phi, *dst_phi, *mem_phi, *add; + ir_mode *p_mode = get_irn_mode(src); + ir_node *ld[4]; + + /* build the control loop */ + in[0] = in[1] = new_r_Unknown(irg, mode_X); + + new_bl = new_r_Block(irg, 2, in); + + in[0] = cnt = new_Const_long(mode_Is, (size >> 4)); + in[1] = new_r_Unknown(irg, mode_Is); + phi = new_r_Phi(irg, new_bl, 2, in, mode_Is); + + sub = new_rd_Sub(dbg, irg, new_bl, phi, new_Const_long(mode_Is, -1), mode_Is); + set_Phi_pred(phi, 1, sub); + + cmp = new_rd_Cmp(dbg, irg, new_bl, sub, new_Const_long(mode_Is, 0)); + proj = new_r_Proj(irg, new_bl, cmp, mode_b, pn_Cmp_Lg); + cond = new_rd_Cond(dbg, irg, new_bl, proj); + + projT = new_r_Proj(irg, new_bl, cond, mode_X, pn_Cond_true); + projF = new_r_Proj(irg, new_bl, cond, mode_X, pn_Cond_false); + + jmp = get_Block_cfgpred(block, 0); + set_Block_cfgpred(block, 0, projF); + + set_Block_cfgpred(new_bl, 0, jmp); + set_Block_cfgpred(new_bl, 1, projT); + + size &= 0xF; + + /* build the copy */ + in[0] = src; + in[1] = new_r_Unknown(irg, p_mode); + src_phi = new_r_Phi(irg, new_bl, 2, in, p_mode); + + in[0] = dst; + dst_phi = new_r_Phi(irg, new_bl, 2, in, p_mode); + + add = new_rd_Add(dbg, irg, new_bl, src_phi, new_Const_long(mode_Is, 16), p_mode); + set_Phi_pred(src_phi, 1, add); + add = new_rd_Add(dbg, irg, new_bl, dst_phi, new_Const_long(mode_Is, 16), p_mode); + set_Phi_pred(dst_phi, 1, add); + + in[0] = mem; + in[1] = new_r_Unknown(irg, mode_M); + mem_phi = new_r_Phi(irg, new_bl, 2, in, mode_M); + + src = src_phi; + dst = dst_phi; + + /* create 4 parallel loads */ + for (i = 0; i < 4; ++i) { + ir_node *load; + + load = new_rd_mips_load_r(dbg, irg, new_bl, mem_phi, src, mode_T); + attr = get_mips_attr(load); + attr->modes.load_store_mode = mode_Iu; + attr->tv = new_tarval_from_long(i * 4, mode_Iu); + + ld[i] = new_rd_Proj(dbg, irg, new_bl, load, mode_Iu, pn_Load_res); + } + + /* create 4 parallel stores */ + for (i = 0; i < 4; ++i) { + ir_node *store; + + store = new_rd_mips_store_r(dbg, irg, new_bl, mem_phi, dst, ld[i], mode_T); + attr = get_mips_attr(store); + attr->modes.load_store_mode = mode_Iu; + attr->tv = new_tarval_from_long(i * 4, mode_Iu); + + mm[i] = new_rd_Proj(dbg, irg, new_bl, store, mode_M, pn_Store_M); + } + mem = new_r_Sync(irg, new_bl, 4, mm); + result = mem; + set_Phi_pred(mem_phi, 1, mem); + } + + // output store/loads manually + n = 0; + for(i = size; i > 0; ) { + ir_mode *mode; + ir_node *load, *store, *projv; + int offset = size - i; + if(i >= 4) { + mode = mode_Iu; + i -= 4; + } else if(i >= 2) { + mode = mode_Hu; + i -= 2; + } else { + mode = mode_Bu; + i -= 1; + } + + load = new_rd_mips_load_r(dbg, irg, block, mem, src, mode_T); + attr = get_mips_attr(load); + attr->modes.load_store_mode = mode; + attr->tv = new_tarval_from_long(offset, mode_Iu); + + projv = new_rd_Proj(dbg, irg, block, load, mode, pn_Load_res); + + store = new_rd_mips_store_r(dbg, irg, block, mem, dst, projv, mode_T); + attr = get_mips_attr(store); + attr->modes.load_store_mode = mode; + attr->tv = new_tarval_from_long(offset, mode_Iu); + + mm[n] = new_rd_Proj(dbg, irg, block, store, mode_M, pn_Store_M); + n++; + } + + if(n > 0) { + result = new_r_Sync(irg, block, n, mm); + } else if(n == 1) { + result = mm[0]; + } + + return result; +} + +static void mips_fix_CopyB_Proj(mips_transform_env_t* env) { + ir_node *node = env->irn; + long n = get_Proj_proj(node); + + if(n == pn_CopyB_M_except) { + assert(0); + } else if(n == pn_CopyB_M_regular) { + set_Proj_proj(node, pn_Store_M); + } else if(n == pn_CopyB_M_except) { + set_Proj_proj(node, pn_Store_X_except); + } +} + +static void mips_transform_Spill(mips_transform_env_t* env) { + ir_node *node = env->irn; + ir_node *sched_point = NULL; + ir_node *store, *proj; + ir_node *nomem = new_rd_NoMem(env->irg); + ir_node *ptr = get_irn_n(node, 0); + ir_node *val = get_irn_n(node, 1); + entity *ent = be_get_frame_entity(node); + mips_attr_t *attr; + + if(sched_is_scheduled(node)) { + sched_point = sched_prev(node); + } + + store = new_rd_mips_store_r(env->dbg, env->irg, env->block, nomem, ptr, val, mode_T); + attr = get_mips_attr(store); + attr->stack_entity = ent; + attr->modes.load_store_mode = get_irn_mode(val); + + proj = new_rd_Proj(env->dbg, env->irg, env->block, store, mode_M, pn_Store_M); + + if (sched_point) { + sched_add_after(sched_point, store); + sched_add_after(store, proj); + + sched_remove(node); + } + + exchange(node, proj); +} + +static void mips_transform_Reload(mips_transform_env_t* env) { + ir_node *node = env->irn; + ir_node *sched_point = NULL; + ir_node *load, *proj; + ir_node *nomem = new_rd_NoMem(env->irg); + ir_node *ptr = get_irn_n(node, 0); + ir_node *mem = get_irn_n(node, 1); + ir_mode *mode = get_irn_mode(node); + entity *ent = be_get_frame_entity(node); + const arch_register_t* reg; + mips_attr_t *attr; + + if(sched_is_scheduled(node)) { + sched_point = sched_prev(node); + } + + load = new_rd_mips_load_r(env->dbg, env->irg, env->block, mem, ptr, mode_T); + attr = get_mips_attr(load); + attr->stack_entity = ent; + attr->modes.load_store_mode = mode; + + proj = new_rd_Proj(env->dbg, env->irg, env->block, load, mode, pn_Load_res); + + if (sched_point) { + sched_add_after(sched_point, load); + sched_add_after(load, proj); + + sched_remove(node); + } + + /* copy the register from the old node to the new Load */ + reg = arch_get_irn_register(env->cg->arch_env, node); + arch_set_irn_register(env->cg->arch_env, proj, reg); + + exchange(node, proj); +} + +static ir_node *gen_node_for_StackParam(mips_transform_env_t *env) +{ + ir_node *node = env->irn; + ir_node *sp = get_irn_n(node, 0); + ir_node *load; + ir_node *nomem = new_rd_NoMem(env->irg); + ir_node *proj; + mips_attr_t *attr; + + load = new_rd_mips_load_r(env->dbg, env->irg, env->block, nomem, sp, mode_T); + attr = get_mips_attr(load); + attr->stack_entity = be_get_frame_entity(node); + attr->modes.load_store_mode = env->mode; + + proj = new_rd_Proj(env->dbg, env->irg, env->block, load, env->mode, pn_Load_res); + + return proj; +} + +static ir_node *gen_node_for_AddSP(mips_transform_env_t *env) +{ + ir_node *node = env->irn; + ir_node *op1, *op2; + ir_node *add; + const arch_register_t *reg; + + op1 = get_irn_n(node, 0); + op2 = get_irn_n(node, 1); + + add = new_rd_mips_add(env->dbg, env->irg, env->block, op1, op2, mode_Iu); + + /* copy the register requirements from the old node to the new node */ + reg = arch_get_irn_register(env->cg->arch_env, node); + arch_set_irn_register(env->cg->arch_env, add, reg); + + return add; +} + +/********************************************************* + * _ _ _ + * (_) | | (_) + * _ __ ___ __ _ _ _ __ __| |_ __ ___ _____ _ __ + * | '_ ` _ \ / _` | | '_ \ / _` | '__| \ \ / / _ \ '__| + * | | | | | | (_| | | | | | | (_| | | | |\ V / __/ | + * |_| |_| |_|\__,_|_|_| |_| \__,_|_| |_| \_/ \___|_| + * + *********************************************************/ + + +/** + * Transforms the given firm node (and maybe some other related nodes) + * into one or more assembler nodes. + * + * @param node the firm node + * @param env the debug module + */ +void mips_transform_node(ir_node *node, void *env) { + mips_code_gen_t *cgenv = (mips_code_gen_t *)env; + opcode code = get_irn_opcode(node); + ir_node *asm_node = node; + mips_transform_env_t tenv; + + if (is_Block(node)) + return; + + tenv.block = get_nodes_block(node); + tenv.dbg = get_irn_dbg_info(node); + tenv.irg = current_ir_graph; + tenv.irn = node; + tenv.mod = cgenv->mod; + tenv.mode = get_irn_mode(node); + tenv.cg = cgenv; + +#define UNOP(firm_opcode, mips_nodetype) case iro_##firm_opcode: asm_node = mips_gen_##mips_nodetype(&tenv, get_##firm_opcode##_op(node)); break +#define BINOP(firm_opcode, mips_nodetype) case iro_##firm_opcode: asm_node = mips_gen_##mips_nodetype(&tenv, get_##firm_opcode##_left(node), get_##firm_opcode##_right(node)); break +#define IGN(a) case iro_##a: break +#define BAD(a) case iro_##a: goto bad + + DBG((tenv.mod, LEVEL_1, "check %+F ... ", node)); + + switch (code) { + BINOP(Add, add); + BINOP(Sub, sub); + BINOP(And, and); + BINOP(Or, or); + BINOP(Eor, xor); + UNOP(Not, not); + BINOP(Shl, sl); + BINOP(Shr, sr); + BINOP(Shrs, sra); + + case iro_Abs: + asm_node = gen_node_for_Abs(&tenv); + break; + + case iro_Rot: + asm_node = gen_node_for_Rot(&tenv); + break; + + case iro_Div: + asm_node = gen_node_for_Div(&tenv); + break; + + case iro_Mod: + asm_node = gen_node_for_Mod(&tenv); + break; + + case iro_Load: + asm_node = gen_node_for_Load(&tenv); + break; + + case iro_Store: + asm_node = gen_node_for_Store(&tenv); + break; + + case iro_Proj: + asm_node = gen_node_for_Proj(&tenv); + break; + + case iro_Conv: + asm_node = gen_node_for_Conv(&tenv); + break; + + case iro_DivMod: + asm_node = gen_node_for_DivMod(&tenv); + break; + + case iro_Mul: + asm_node = gen_node_for_Mul(&tenv); + break; + + case iro_Jmp: + asm_node = gen_node_for_Jmp(&tenv); + break; + + case iro_IJmp: + asm_node = gen_node_for_IJmp(&tenv); + break; + + case iro_Unknown: + asm_node = gen_node_for_Unknown(&tenv); + break; + + case iro_Cond: + asm_node = gen_node_for_Cond(&tenv); + break; + + /* TODO: implement these nodes */ + BAD(Mux); + + /* You probably don't need to handle the following nodes */ + + // call is handled in the emit phase + IGN(Call); + // Cmp is handled together with Cond + IGN(Cmp); + IGN(Alloc); + + IGN(Block); + IGN(Start); + IGN(End); + IGN(NoMem); + IGN(Phi); + IGN(Break); + IGN(Sync); + + IGN(Const); + IGN(SymConst); + + BAD(Raise); + BAD(Sel); + BAD(InstOf); + BAD(Cast); + BAD(Free); + BAD(Tuple); + BAD(Id); + BAD(Bad); + BAD(Confirm); + BAD(Filter); + BAD(CallBegin); + BAD(EndReg); + BAD(EndExcept); + + default: + if(be_is_StackParam(node)) { + asm_node = gen_node_for_StackParam(&tenv); + } else if(be_is_AddSP(node)) { + asm_node = gen_node_for_AddSP(&tenv); + } + break; + +bad: + fprintf(stderr, "Not implemented: %s\n", get_irn_opname(node)); + assert(0); + } + + if (asm_node != node) { + exchange(node, asm_node); + DB((tenv.mod, LEVEL_1, "created node %+F[%p]\n", asm_node, asm_node)); + } else { + DB((tenv.mod, LEVEL_1, "ignored\n")); + } +} + +void mips_pre_transform_node(ir_node *node, void *env) { + mips_code_gen_t *cgenv = (mips_code_gen_t *)env; + int i; + + mips_transform_env_t tenv; + + if (is_Block(node)) + return; + + tenv.block = get_nodes_block(node); + tenv.dbg = get_irn_dbg_info(node); + tenv.irg = current_ir_graph; + tenv.irn = node; + tenv.mod = cgenv->mod; + tenv.mode = get_irn_mode(node); + tenv.cg = cgenv; + + if(is_Proj(node)) { + ir_node* pred = get_Proj_pred(node); + + if(get_irn_opcode(pred) == iro_CopyB) { + mips_fix_CopyB_Proj(&tenv); + } + } + + for(i = 0; i < get_irn_arity(node); ++i) { + ir_node* pred = get_irn_n(node, i); + + if (is_Const(pred)) { + ir_node* constnode = exchange_node_for_Const(&tenv, pred, i); + set_irn_n(node, i, constnode); + } else if (get_irn_op(pred) == op_SymConst) { + ir_node* constnode = gen_node_for_SymConst(&tenv, pred, i); + set_irn_n(node, i, constnode); + } + } +} + +/** + * Calls the transform functions for Spill and Reload. + */ +void mips_after_ra_walker(ir_node *node, void *env) { + mips_code_gen_t *cg = env; + mips_transform_env_t tenv; + + if (is_Block(node)) + return; + + tenv.block = get_nodes_block(node); + tenv.dbg = get_irn_dbg_info(node); + tenv.irg = current_ir_graph; + tenv.irn = node; + tenv.mod = cg->mod; + tenv.mode = get_irn_mode(node); + tenv.cg = cg; + + /* be_is_StackParam(node) || */ + if (be_is_Reload(node)) { + mips_transform_Reload(&tenv); + } else if (be_is_Spill(node)) { + mips_transform_Spill(&tenv); + } +} diff --git a/ir/be/mips/mips_transform.h b/ir/be/mips/mips_transform.h new file mode 100644 index 000000000..2fef39120 --- /dev/null +++ b/ir/be/mips/mips_transform.h @@ -0,0 +1,18 @@ +#ifndef _MIPS_TRANSFORM_H_ +#define _MIPS_TRANSFORM_H_ + +/** + * Create Firm assembler for a copyB node. + * + * @param blk the block where to place the code + * @param node the copyB node to lower + * + * @return the memory from the lowered CopyB + */ +ir_node *gen_code_for_CopyB(ir_node *blk, ir_node *node); + +void mips_pre_transform_node(ir_node *node, void *env); +void mips_transform_node(ir_node *node, void *env); +void mips_after_ra_walker(ir_node *node, void *env); + +#endif /* _MIPS_TRANSFORM_H_ */ diff --git a/ir/be/mips/mips_util.h b/ir/be/mips/mips_util.h new file mode 100644 index 000000000..6c076af27 --- /dev/null +++ b/ir/be/mips/mips_util.h @@ -0,0 +1,6 @@ +#ifndef _MIPS_UTIL_H_ +#define _MIPS_UTIL_H_ + +#define ASSERT_NO_FLOAT(mode) { assert( (!mode_is_float(mode)) && "floating point not supported (yet)"); } + +#endif