X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=ir%2Fbe%2Fmips%2Fmips_emitter.c;h=a04d4d5feca65c2b2d1308b766125e9d05e9d8fd;hb=b599aa34a918a033aa4e50e63b4c445b07d2e0ec;hp=bcd7a20d63e75751a23104d70106bde6ca27cca7;hpb=18dfdacb6faf2f93d4f6f9a6362584f8c7eb30a1;p=libfirm diff --git a/ir/be/mips/mips_emitter.c b/ir/be/mips/mips_emitter.c index bcd7a20d6..a04d4d5fe 100644 --- a/ir/be/mips/mips_emitter.c +++ b/ir/be/mips/mips_emitter.c @@ -1,6 +1,28 @@ -/* mips emitter */ -/* $Id$ */ +/* + * Copyright (C) 1995-2007 University of Karlsruhe. All right reserved. + * + * This file is part of libFirm. + * + * This file may be distributed and/or modified under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation and appearing in the file LICENSE.GPL included in the + * packaging of this file. + * + * Licensees holding valid libFirm Professional Edition licenses may use + * this file in accordance with the libFirm Commercial License. + * Agreement provided with the Software. + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE. + */ +/** + * @file + * @brief implementation of mips assembly emitter + * @author Matthias Braun, Mehdi + * @version $Id$ + */ #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -8,7 +30,6 @@ #include #include "xmalloc.h" -#include "tv.h" #include "iredges.h" #include "debug.h" #include "irgwalk.h" @@ -17,10 +38,13 @@ #include "irargs_t.h" #include "irprog_t.h" #include "irouts.h" +#include "tv.h" +#include "error.h" #include "../besched.h" #include "../benode_t.h" #include "../beutil.h" +#include "../begnuas.h" #include "mips_emitter.h" #include "gen_mips_emitter.h" @@ -28,10 +52,67 @@ #include "mips_new_nodes.h" #include "mips_map_regs.h" +DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;) + +#define BLOCK_PREFIX ".L" + #define SNPRINTF_BUF_LEN 128 -static const arch_env_t *arch_env = NULL; +/** + * Returns the register at in position pos. + */ +static const arch_register_t *get_in_reg(const arch_env_t *arch_env, + const ir_node *node, int pos) +{ + ir_node *op; + const arch_register_t *reg = NULL; + + assert(get_irn_arity(node) > pos && "Invalid IN position"); + + /* The out register of the operator at position pos is the + in register we need. */ + op = get_irn_n(node, 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(const arch_env_t *arch_env, + const ir_node *node, 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(node) != mode_T) { + reg = arch_get_irn_register(arch_env, node); + } else if (is_mips_irn(node)) { + reg = get_mips_out_reg(node, pos); + } else { + const ir_edge_t *edge; + + foreach_out_edge(node, 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; +} /************************************************************* * _ _ __ _ _ @@ -44,6 +125,29 @@ static const arch_env_t *arch_env = NULL; * |_| |_| *************************************************************/ +/** + * Emit the name of the source register at given input position. + */ +void mips_emit_source_register(mips_emit_env_t *env, const ir_node *node, + int pos) +{ + const arch_register_t *reg = get_in_reg(env->arch_env, node, pos); + be_emit_char(env->emit, '$'); + be_emit_string(env->emit, arch_register_get_name(reg)); +} + +/** + * Emit the name of the destination register at given output position. + */ +void mips_emit_dest_register(mips_emit_env_t *env, const ir_node *node, + int pos) +{ + const arch_register_t *reg = get_out_reg(env->arch_env, node, pos); + be_emit_char(env->emit, '$'); + be_emit_string(env->emit, arch_register_get_name(reg)); +} + +#if 0 static const char *get_symconst_str(ir_node *node) { ident *id; @@ -97,10 +201,10 @@ static const char *node_const_to_str(ir_node *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); + snprintf(buf, sizeof(buf), "0x%04lX", val & 0xffff); } else { unsigned long val = get_tarval_long(attr->tv); - snprintf(buf, sizeof(buf), "0x%04X", val & 0xffff); + snprintf(buf, sizeof(buf), "0x%04lX", val & 0xffff); } return buf; @@ -109,11 +213,11 @@ static const char *node_const_to_str(ir_node *n) 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); + snprintf(buf, sizeof(buf), "0x%04lX", val & 0xffff); } else { unsigned long val = get_tarval_long(attr->tv); val = (val & 0xffff0000) >> 16; - snprintf(buf, sizeof(buf), "0x%04X", val & 0xffff); + snprintf(buf, sizeof(buf), "0x%04lX", val & 0xffff); } return buf; @@ -125,203 +229,70 @@ static const char *node_const_to_str(ir_node *n) return buf; } +#endif -/** - * Returns node's offset as string. - */ -static const char *node_offset_to_str(ir_node *n) +void mips_emit_load_store_address(mips_emit_env_t *env, const ir_node *node, + int pos) { - return ""; -} + const mips_load_store_attr_t *attr = get_mips_load_store_attr_const(node); -/* 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; + be_emit_irprintf(env->emit, "%d(", attr->offset); + mips_emit_source_register(env, node, pos); + be_emit_char(env->emit, ')'); } - -/** - * Returns the register at in position pos. - */ -static const arch_register_t *get_in_reg(ir_node *irn, int pos) +void mips_emit_immediate_suffix(mips_emit_env_t *env, const ir_node *node, + 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; + ir_node *op = get_irn_n(node, pos); + if(is_mips_Immediate(op)) + be_emit_char(env->emit, 'i'); } -/** - * Returns the register at out position pos. - */ -static const arch_register_t *get_out_reg(ir_node *irn, int pos) +void mips_emit_immediate(mips_emit_env_t *env, const ir_node *node) { - ir_node *proj; - const arch_register_t *reg = NULL; + const mips_immediate_attr_t *attr = get_mips_immediate_attr_const(node); - /* 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; - } + switch(attr->imm_type) { + case MIPS_IMM_CONST: + be_emit_irprintf(env->emit, "%d", attr->val); + break; + case MIPS_IMM_SYMCONST_LO: + be_emit_cstring(env->emit, "%lo($"); + be_emit_ident(env->emit, get_entity_ld_ident(attr->entity)); + if(attr->val != 0) { + be_emit_irprintf(env->emit, "%+d", attr->val); } + be_emit_char(env->emit, ')'); + break; + case MIPS_IMM_SYMCONST_HI: + be_emit_cstring(env->emit, "%hi($"); + be_emit_ident(env->emit, get_entity_ld_ident(attr->entity)); + if(attr->val != 0) { + be_emit_irprintf(env->emit, "%+d", attr->val); + } + be_emit_char(env->emit, ')'); + break; + default: + panic("invalid immediate type found"); } - - 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. + * Emit the name of the destination register at given output position. */ -const lc_arg_env_t *mips_get_arg_env(void) +void mips_emit_source_register_or_immediate(mips_emit_env_t *env, + const ir_node *node, int pos) { - 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); + const ir_node *op = get_irn_n(node, pos); + if(is_mips_Immediate(op)) { + mips_emit_immediate(env, op); + } else { + mips_emit_source_register(env, node, pos); } - - return env; } +#if 0 /* * Add a number to a prefix. This number will not be used a second time. */ @@ -331,110 +302,114 @@ static char *get_unique_label(char *buf, size_t buflen, const char *prefix) 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; -} +#endif /************************************************************************/ -/* ABI Handling +/* ABI Handling */ /************************************************************************/ -static void mips_emit_IncSP(const ir_node *node, mips_emit_env_t *env) +static +void mips_emit_IncSP(mips_emit_env_t *env, const ir_node *node) { - FILE *F = env->out; - int offset = be_get_IncSP_offset(node); - if(offset == 0) + int offset = be_get_IncSP_offset(node); + + if(offset == 0) { + be_emit_cstring(env->emit, "\t/* omitted IncSP with 0 */"); + be_emit_finish_line_gas(env->emit, node); return; + } - if(be_get_IncSP_direction(node) != be_stack_dir_expand) - offset = -offset; + if(offset > 0xffff || offset < -0xffff) { + panic("stackframe > 2^16 bytes not supported yet\n"); + } - fprintf(F, "\taddi $sp, $sp, %d\n", -offset); + if(offset > 0) { + be_emit_irprintf(env->emit, "\tsubu $sp, $sp, %d", offset); + } else { + be_emit_irprintf(env->emit, "\taddu $sp, $sp, %d", -offset); + } + be_emit_finish_line_gas(env->emit, node); } -static void mips_emit_Copy(const ir_node *node, mips_emit_env_t *env) +static void mips_emit_Copy(mips_emit_env_t *env, const ir_node *node) { - FILE *F = env->out; - - lc_efprintf(mips_get_arg_env(), F, "\tor %1D, $zero, %1S\t\t\t# copy\n", node, node); + be_emit_cstring(env->emit, "\tmove "); + mips_emit_dest_register(env, node, 0); + be_emit_cstring(env->emit, ", "); + mips_emit_source_register(env, node, 0); + be_emit_finish_line_gas(env->emit, node); } -static void mips_emit_Return(const ir_node* node, mips_emit_env_t *env) +static void mips_emit_Return(mips_emit_env_t *env, const ir_node* node) { - FILE *F = env->out; - fprintf(F, "\tj $ra\t\t\t\t# return\n"); + be_emit_cstring(env->emit, "\tj $ra"); + be_emit_finish_line_gas(env->emit, node); } -static void mips_emit_nops(FILE* F, int n) +static __attribute__((unused)) +void mips_emit_nops(mips_emit_env_t *env, int n) { int i; for(i = 0; i < n; ++i) { - fprintf(F, "\tnop\n"); + be_emit_cstring(env->emit, "\tnop\n"); + be_emit_write_line(env->emit); } } -static void mips_emit_Perm(const ir_node *node, mips_emit_env_t *env) +static void mips_emit_Perm(mips_emit_env_t *env, const ir_node *node) { - FILE* F = env->out; + assert(get_irn_arity(node) == 2); - assert(/*get_irn_n_outs(node) == 2 &&*/ get_irn_arity(node) == 2); + be_emit_cstring(env->emit, "\txor "); + mips_emit_source_register(env, node, 0); + be_emit_cstring(env->emit, ", "); + mips_emit_source_register(env, node, 0); + be_emit_cstring(env->emit, ", "); + mips_emit_source_register(env, node, 1); + be_emit_finish_line_gas(env->emit, node); - 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); -} + /* mips_emit_nops(env, 3); */ -static void mips_emit_Spill(const ir_node* node, mips_emit_env_t *env) -{ - FILE* F = env->out; + be_emit_cstring(env->emit, "\txor "); + mips_emit_source_register(env, node, 1); + be_emit_cstring(env->emit, ", "); + mips_emit_source_register(env, node, 1); + be_emit_cstring(env->emit, ", "); + mips_emit_source_register(env, node, 0); + be_emit_finish_line_gas(env->emit, node); - 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)); -} + /* mips_emit_nops(env, 3); */ -static void mips_emit_Reload(const ir_node* node, mips_emit_env_t *env) -{ - FILE* F = env->out; + be_emit_cstring(env->emit, "\txor "); + mips_emit_source_register(env, node, 0); + be_emit_cstring(env->emit, ", "); + mips_emit_source_register(env, node, 0); + be_emit_cstring(env->emit, ", "); + mips_emit_source_register(env, node, 1); + be_emit_finish_line_gas(env->emit, node); - 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)); + /* mips_emit_nops(env, 3); */ } /************************************************************************/ -/* Calls +/* Calls */ /************************************************************************/ -static void mips_emit_Call(ir_node *node, mips_emit_env_t *env) +static void mips_emit_Call(mips_emit_env_t *env, const ir_node *node) { - FILE *F = env->out; - const arch_register_t *callee_reg; + ir_entity *callee; + + be_emit_cstring(env->emit, "\tjal "); - // call to imediate value (label) - entity *callee = be_Call_get_entity(node); + /* call of immediate value (label) */ + callee = be_Call_get_entity(node); if(callee != NULL) { - fprintf(F, "\tjal %s\n", get_entity_name(callee)); - return; + be_emit_ident(env->emit, get_entity_ld_ident(callee)); + } else { + mips_emit_source_register(env, node, be_pos_Call_ptr); } - - // 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)); + be_emit_finish_line_gas(env->emit, node); } /************************************************************************ @@ -454,17 +429,33 @@ const char* mips_get_block_label(const ir_node* block) return buf; } -static void mips_emit_Jump(ir_node *node, mips_emit_env_t *env) +/** + * Emits a block label from the given block. + */ +static +void mips_emit_block_label(mips_emit_env_t *env, const ir_node *block) { - FILE *F = env->out; - const ir_node *block = get_irn_link(node); + if (has_Block_label(block)) { + be_emit_string(env, be_gas_label_prefix()); + be_emit_irprintf(env->emit, "%lu", get_Block_label(block)); + } else { + be_emit_cstring(env, BLOCK_PREFIX); + be_emit_irprintf(env->emit, "%d", get_irn_node_nr(block)); + } +} + +static void mips_emit_Jump(mips_emit_env_t *env, const ir_node *node) +{ + const ir_node *block = get_irn_link(node); assert(is_Block(block)); - fprintf(F, "\tb %s\n", mips_get_block_label(block)); + be_emit_cstring(env->emit, "\tb "); + mips_emit_block_label(env, block); + be_emit_finish_line_gas(env->emit, node); } -ir_node *mips_get_jump_block(const ir_node* node, int projn) +ir_node *mips_get_jump_block(const ir_node* node, long projn) { const ir_edge_t *oute; for(oute = get_irn_out_edge_first(node); oute != NULL; @@ -481,16 +472,45 @@ ir_node *mips_get_jump_block(const ir_node* node, int projn) return NULL; } +void mips_emit_jump_target_proj(mips_emit_env_t *env, const ir_node *node, + long projn) +{ + ir_node *jumpblock = mips_get_jump_block(node, projn); + assert(jumpblock != NULL); + + mips_emit_block_label(env, jumpblock); +} + +void mips_emit_jump_target(mips_emit_env_t *env, const ir_node *node) +{ + ir_node *jumpblock = get_irn_link(node); + assert(jumpblock != NULL); + + mips_emit_block_label(env, jumpblock); +} + +void mips_emit_jump_or_fallthrough(mips_emit_env_t *env, const ir_node *node, + long pn) +{ + ir_node *jumpblock = mips_get_jump_block(node, pn); + assert(jumpblock != NULL); + + /* TODO: use fallthrough when possible */ + be_emit_cstring(env->emit, "b "); + mips_emit_block_label(env, jumpblock); +} + /************************************************************************ - * ____ _ _ _ _ * - * / ___|_ _(_) |_ ___| |__ | |_ _ _ __ ___ _ __ * - * \___ \ \ /\ / / | __/ __| '_ \ _ | | | | | '_ ` _ \| '_ \ * - * ___) \ V V /| | || (__| | | | |_| | |_| | | | | | | |_) | * - * |____/ \_/\_/ |_|\__\___|_| |_|\___/ \__,_|_| |_| |_| .__/ * - * |_| * + * ____ _ _ _ _ * + * / ___|_ _(_) |_ ___| |__ | |_ _ _ __ ___ _ __ * + * \___ \ \ /\ / / | __/ __| '_ \ _ | | | | | '_ ` _ \| '_ \ * + * ___) \ V V /| | || (__| | | | |_| | |_| | | | | | | |_) | * + * |____/ \_/\_/ |_|\__\___|_| |_|\___/ \__,_|_| |_| |_| .__/ * + * |_| * * * ************************************************************************/ +#if 0 /* jump table entry (target and corresponding number) */ typedef struct _branch_t { ir_node *target; @@ -510,7 +530,8 @@ typedef struct _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) { +static int mips_cmp_branch_t(const void *a, const void *b) +{ branch_t *b1 = (branch_t *)a; branch_t *b2 = (branch_t *)b; @@ -532,16 +553,12 @@ const char* mips_get_jumptbl_label(const ir_node* switchjmp) * 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); +void emit_mips_jump_table(mips_emit_env_t *env, const ir_node *irn) { + int lastval, i, i2, pn; + jmp_tbl_t tbl; + ir_node *proj; + const ir_edge_t *edge; + const mips_attr_t *attr = get_mips_attr_const(irn); /* fill the table structure */ tbl.label = xmalloc(SNPRINTF_BUF_LEN); @@ -573,17 +590,26 @@ void emit_mips_jump_table(const ir_node *irn, FILE* F) { /* 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)); + be_emit_string(env->emit, mips_get_jumptbl_label(irn)); + be_emit_cstring(env->emit, ":\n"); + be_emit_write_line(env->emit); 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)); + be_emit_cstring(env->emit, "\t.word "); + be_emit_ident(env->emit, get_entity_ld_ident(attr->symconst)); + be_emit_char(env->emit, '\n'); + be_emit_write_line(env->emit); } - fprintf(F, "\t.word %s\n", mips_get_block_label(branch->target)); + be_emit_cstring(env->emit, "\t.word "); + mips_emit_block_label(env, branch->target); + be_emit_char(env->emit, '\n'); + be_emit_write_line(env->emit); + lastval = branch->value; } @@ -593,17 +619,23 @@ void emit_mips_jump_table(const ir_node *irn, FILE* F) { free(tbl.branches); } -static void dump_jump_tables(ir_node* node, void *env) +static +void dump_jump_tables(ir_node* node, void *data) { - FILE* F = (FILE*) env; + mips_emit_env_t *env = data; // emit jump tables if(is_mips_SwitchJump(node)) { - fprintf(F, ".data\n"); - emit_mips_jump_table(node, F); - fprintf(F, ".text\n"); + be_emit_cstring(env->emit, ".data\n"); + be_emit_write_line(env->emit); + + emit_mips_jump_table(env, node); + + be_emit_cstring(env->emit, ".text\n"); + be_emit_write_line(env->emit); } } +#endif /*********************************************************************************** * _ __ _ @@ -615,20 +647,34 @@ static void dump_jump_tables(ir_node* node, void *env) * ***********************************************************************************/ -static void mips_emit_nothing(ir_mode *mode, mips_emit_env_t *env) +static void mips_emit_nothing(mips_emit_env_t *env, const ir_node *node) { + (void) env; + (void) node; } -static void mips_emit_this_shouldnt_happen(ir_mode *mode, mips_emit_env_t *env) +static void mips_emit_this_shouldnt_happen(mips_emit_env_t *env, const ir_node *node) { - assert(0); + (void) env; + panic("Found non-lowered node %+F while emitting", node); +} + +/** + * The type of a emitter function. + */ +typedef void (*emit_func) (mips_emit_env_t *, const ir_node *); + +/** + * Set a node emitter. Make it a bit more type safe. + */ +static void register_emitter(ir_op *op, emit_func func) { + op->ops.generic = (op_func) func; } /** * Register emitter functions for mips backend */ -void mips_register_emitters(void) -{ +void mips_register_emitters(void) { /* first clear the generic function pointer for all ops */ clear_irp_opcodes_generic_func(); @@ -636,49 +682,41 @@ void mips_register_emitters(void) 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 *); + register_emitter(op_be_IncSP, mips_emit_IncSP); + register_emitter(op_be_SetSP, mips_emit_this_shouldnt_happen); + register_emitter(op_be_AddSP, mips_emit_this_shouldnt_happen); + register_emitter(op_be_Call, mips_emit_Call); + register_emitter(op_be_Copy, mips_emit_Copy); + register_emitter(op_be_Keep, mips_emit_nothing); + register_emitter(op_be_Barrier, mips_emit_nothing); + register_emitter(op_be_Return, mips_emit_Return); + register_emitter(op_be_RegParams, mips_emit_nothing); + register_emitter(op_be_Spill, mips_emit_this_shouldnt_happen); + register_emitter(op_be_Reload, mips_emit_this_shouldnt_happen); + register_emitter(op_be_Perm, mips_emit_Perm); + + register_emitter(op_Start, mips_emit_nothing); + register_emitter(op_Proj, mips_emit_nothing); + register_emitter(op_SymConst, mips_emit_this_shouldnt_happen); + register_emitter(op_Const, mips_emit_this_shouldnt_happen); + register_emitter(op_Jmp, mips_emit_Jump); + register_emitter(op_Cmp, mips_emit_this_shouldnt_happen); + register_emitter(op_Cond, mips_emit_this_shouldnt_happen); + register_emitter(op_Phi, mips_emit_nothing); +} /** * Emits assembly for a single node */ -static void mips_emit_node(ir_node *irn, mips_emit_env_t* env) +static void mips_emit_node(mips_emit_env_t *env, const ir_node *node) { - 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)); + ir_op *op = get_irn_op(node); 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 + (*emit) (env, node); } else { - ir_fprintf(F, "\t\t\t\t\t# %+F\n", irn); + panic("No emitter defined for node %+F", node); } } @@ -686,55 +724,82 @@ static void mips_emit_node(ir_node *irn, mips_emit_env_t* env) * 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) +void mips_gen_block(mips_emit_env_t *env, ir_node *block) { - FILE *F = ((mips_emit_env_t *)env)->out; - ir_node *irn; + ir_node *node; if (! is_Block(block)) return; - fprintf(F, "%s:\n", mips_get_block_label(block)); - sched_foreach(block, irn) { - mips_emit_node(irn, env); + mips_emit_block_label(env, block); + be_emit_cstring(env->emit, ":\n"); + be_emit_write_line(env->emit); + + sched_foreach(block, node) { + mips_emit_node(env, node); } - fprintf(F, "\n"); + + be_emit_char(env->emit, '\n'); + be_emit_write_line(env->emit); } /** * Emits code for function start. */ -void mips_emit_start(FILE *F, ir_graph *irg) +void mips_emit_func_prolog(mips_emit_env_t *env, ir_graph *irg) { - const char *irg_name = get_entity_name(get_irg_entity(irg)); + ident *irg_ident = get_entity_ld_ident(get_irg_entity(irg)); + be_emit_env_t *eenv = env->emit; // dump jump tables - irg_walk_graph(irg, NULL, dump_jump_tables, F); + //irg_walk_graph(irg, NULL, dump_jump_tables, env); + + be_emit_write_line(eenv); + be_gas_emit_switch_section(eenv, GAS_SECTION_TEXT); + + be_emit_cstring(eenv, "\t.balign\t4\n"); + + be_emit_cstring(eenv, "\t.global\t"); + be_emit_ident(eenv, irg_ident); + be_emit_char(eenv, '\n'); + + be_emit_cstring(eenv, "\t.set\tnomips16\n"); - fprintf(F, "\n\n"); - fprintf(F, "# Function Start of %s\n", irg_name); - fprintf(F, "%s:\n", irg_name); + be_emit_cstring(eenv, "\t.ent\t"); + be_emit_ident(eenv, irg_ident); + be_emit_char(eenv, '\n'); + + be_emit_ident(eenv, irg_ident); + be_emit_cstring(eenv, ":\n"); + + be_emit_cstring(eenv, "\t.frame\t$fp, 24, $ra\n"); + be_emit_cstring(eenv, "\t.mask\t0xc0000000, -4\n"); + be_emit_cstring(eenv, "\t.fmask\t0x00000000, 0\n"); + + be_emit_write_line(eenv); } /** * Emits code for function end */ -void mips_emit_end(FILE *F, ir_graph *irg) +void mips_emit_func_epilog(mips_emit_env_t *env, ir_graph *irg) { - const char *irg_name = get_entity_name(get_irg_entity(irg)); + ident *irg_ident = get_entity_ident(get_irg_entity(irg)); - fprintf(F, "# End of function %s we should never get here...\n", irg_name); - fprintf(F, "\tjal exit\n"); + be_emit_cstring(env->emit, "\t.end\t"); + be_emit_ident(env->emit, irg_ident); + be_emit_char(env->emit, '\n'); + be_emit_write_line(env->emit); } /** * 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); + (void) env; for (n--; n >= 0; n--) { pred = get_Block_cfgpred(block, n); @@ -745,29 +810,33 @@ void mips_gen_labels(ir_node *block, void *env) /** * Main driver */ -void mips_gen_routine(FILE *F, ir_graph *irg, const mips_code_gen_t *cg) +void mips_gen_routine(mips_code_gen_t *cg, ir_graph *irg) { - mips_emit_env_t emit_env; + mips_emit_env_t 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; + env.isa = (mips_isa_t*) cg->arch_env->isa; + env.emit = &env.isa->emit; + env.arch_env = cg->arch_env; + env.cg = cg; + + mips_register_emitters(); - /* set the global arch_env (needed by print hooks) */ - arch_env = cg->arch_env; + irg_block_walk_graph(irg, mips_gen_labels, NULL, &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); + mips_emit_func_prolog(&env, irg); 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_gen_block(&env, block); } - mips_emit_end(F, irg); + mips_emit_func_epilog(&env, irg); +} + +void mips_init_emitter(void) +{ + FIRM_DBG_REGISTER(dbg, "firm.be.mips.emitter"); }