X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=ir%2Fbe%2Farm%2Farm_emitter.c;h=5294070466e8aa920866465e9ac04f9cb69aa5a3;hb=75ee2fcd7c5bfb5ff2c680b7086cf796fa9ff66a;hp=b206413e7c7bd06a774ac235e171d8825a6a2b63;hpb=5991fbaabe35ebb8e3550cf1634bf997bbe8d69b;p=libfirm diff --git a/ir/be/arm/arm_emitter.c b/ir/be/arm/arm_emitter.c index b206413e7..529407046 100644 --- a/ir/be/arm/arm_emitter.c +++ b/ir/be/arm/arm_emitter.c @@ -1,6 +1,29 @@ +/* + * Copyright (C) 1995-2008 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 arm emitter + * @author Oliver Richter, Tobias Gneist, Michael Beck + * @version $Id$ + */ #define SILENCER -/* arm emitter */ -/* $Id$ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -13,12 +36,19 @@ #include "iredges.h" #include "debug.h" #include "irgwalk.h" +#include "irtools.h" #include "irprintf.h" #include "irop_t.h" #include "irprog_t.h" #include "irargs_t.h" +#include "error.h" +#include "raw_bitset.h" +#include "dbginfo.h" #include "../besched.h" +#include "../beblocksched.h" +#include "../beirg_t.h" +#include "../begnuas.h" #include "arm_emitter.h" #include "gen_arm_emitter.h" @@ -29,33 +59,86 @@ #include "../benode_t.h" +#define BLOCK_PREFIX ".L" + #define SNPRINTF_BUF_LEN 128 -static const arch_env_t *arch_env = NULL; +DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;) + +static const arch_env_t *arch_env = NULL; +static const arm_code_gen_t *cg; +static const arm_isa_t *isa; +static set *sym_or_tv; /** - * Switch to a new section + * Returns the register at in position pos. */ -void arm_switch_section(FILE *f, sections sec) { - static sections curr_sec = NO_SECTION; +static const arch_register_t *get_in_reg(const ir_node *irn, int pos) { + ir_node *op; + const arch_register_t *reg = NULL; - if (curr_sec == sec) - return; + assert(get_irn_arity(irn) > pos && "Invalid IN position"); - curr_sec = sec; - switch (sec) { + /* The out register of the operator at position pos is the + in register we need. */ + op = get_irn_n(irn, pos); - case NO_SECTION: - break; + reg = arch_get_irn_register(arch_env, op); - case SECTION_TEXT: - fprintf(f, "\t.text\n"); - break; + assert(reg && "no in register found"); - case SECTION_DATA: - fprintf(f, "\t.data\n"); - break; + /* in case of a joker register: just return a valid register */ + if (arch_register_type_is(reg, joker)) { + const arch_register_req_t *req; + + /* ask for the requirements */ + req = arch_get_register_req(arch_env, irn, pos); + + if (arch_register_req_is(req, limited)) { + /* in case of limited requirements: get the first allowed register */ + unsigned idx = rbitset_next(req->limited, 0, 1); + reg = arch_register_for_index(req->cls, idx); + } else { + /* otherwise get first register in class */ + reg = arch_register_for_index(req->cls, 0); + } } + return reg; +} + + +/** + * Returns the register at out position pos. + */ +static const arch_register_t *get_out_reg(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_arm_irn(node)) { + reg = get_arm_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; } /************************************************************* @@ -70,376 +153,271 @@ void arm_switch_section(FILE *f, sections sec) { *************************************************************/ /** - * Returns non-zero if a mode has a Immediate attribute. + * Emit the name of the source register at given input position. */ -int is_immediate_node(ir_node *irn) { - arm_attr_t *attr = get_arm_attr(irn); - return ARM_GET_SHF_MOD(attr) == ARM_SHF_IMM; +void arm_emit_source_register(const ir_node *node, int pos) { + const arch_register_t *reg = get_in_reg(node, pos); + be_emit_string(arch_register_get_name(reg)); } /** - * Return a const or SymConst as string. + * Emit the name of the destination register at given output position. */ -static const char *node_const_to_str(ir_node *n, char *buf, int buflen) { - if (is_immediate_node(n)) { - snprintf(buf, buflen, "#0x%X", arm_decode_imm_w_shift(get_arm_value(n))); - return buf; - } - else if (is_arm_SymConst(n)) - return get_arm_symconst_label(n); - - assert( 0 && "das ist gar keine Konstante"); - return NULL; +void arm_emit_dest_register(const ir_node *node, int pos) { + const arch_register_t *reg = get_out_reg(node, pos); + be_emit_string(arch_register_get_name(reg)); } /** - * Returns node's offset as string. + * Emit a node's offset. */ -static const char *node_offset_to_str(ir_node *n, char *buf, int buflen) { +void arm_emit_offset(const ir_node *node) { int offset = 0; - ir_op *irn_op = get_irn_op(n); - - if (irn_op == op_be_StackParam) { - entity *ent = be_get_frame_entity(n); - offset = get_entity_offset_bytes(ent); - } else if (irn_op == op_be_Reload || irn_op == op_be_Spill) { - entity * ent = be_get_spill_entity(n); - offset = get_entity_offset_bytes(ent); + ir_op *irn_op = get_irn_op(node); + + if (irn_op == op_be_Reload || irn_op == op_be_Spill) { + ir_entity *ent = be_get_frame_entity(node); + offset = get_entity_offset(ent); } else if (irn_op == op_be_IncSP) { - int offs = be_get_IncSP_offset(n); - be_stack_dir_t dir = be_get_IncSP_direction(n); - offset = (dir == be_stack_dir_expand) ? -offs : offs; + offset = - be_get_IncSP_offset(node); } else { - return "node_offset_to_str will fuer diesen Knotentyp noch implementiert werden"; + assert(!"unimplemented arm_emit_offset for this node type"); + panic("unimplemented arm_emit_offset for this node type"); } - snprintf(buf, buflen, "%d", offset); - return buf; + be_emit_irprintf("%d", offset); } -/* We always pass the ir_node which is a pointer. */ -static int arm_get_arg_type(const lc_arg_occ_t *occ) { - return lc_arg_type_ptr; -} - - /** - * Returns the register at in position pos. + * Emit the arm fpa instruction suffix depending on the mode. */ -static const arch_register_t *get_in_reg(const 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(const ir_node *irn, int pos) { - ir_node *proj; - const arch_register_t *reg = NULL; - - assert(get_irn_n_edges(irn) > pos && "Invalid OUT position"); - - /* 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_arm_irn(irn)) { - reg = get_arm_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; +static void arm_emit_fpa_postfix(const ir_mode *mode) { + int bits = get_mode_size_bits(mode); + if (bits == 32) + be_emit_char('s'); + else if (bits == 64) + be_emit_char('d'); + else + be_emit_char('e'); } /** - * Returns the number of the in register at position pos. + * Emit the instruction suffix depending on the mode. */ -int get_arm_reg_nr(ir_node *irn, int pos, int in_out) { - const arch_register_t *reg; +void arm_emit_mode(const ir_node *node) { + ir_mode *mode; - if (in_out == 1) { - reg = get_in_reg(irn, pos); - } - else { - reg = get_out_reg(irn, pos); + if (is_arm_irn(node)) { + const arm_attr_t *attr = get_arm_attr_const(node); + mode = attr->op_mode ? attr->op_mode : get_irn_mode(node); + } else { + mode = get_irn_mode(node); } - - return arch_register_get_index(reg); + arm_emit_fpa_postfix(mode); } /** - * Returns the name of the in register at position pos. + * Emit a const or SymConst value. */ -const char *get_arm_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); - } +void arm_emit_immediate(const ir_node *node) { + const arm_attr_t *attr = get_arm_attr_const(node); + + if (ARM_GET_SHF_MOD(attr) == ARM_SHF_IMM) { + be_emit_irprintf("#0x%X", arm_decode_imm_w_shift(get_arm_imm_value(node))); + } else if (ARM_GET_FPA_IMM(attr)) { + be_emit_irprintf("#%s", arm_get_fpa_imm_name(get_arm_imm_value(node))); + } else if (is_arm_SymConst(node)) + be_emit_ident(get_arm_symconst_id(node)); else { - reg = get_out_reg(irn, pos); - } - - return arch_register_get_name(reg); -} - -/** - * Get the register name for a node. - */ -static int arm_get_reg_name(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; - int nr = occ->width - 1; - - if (!X) - return lc_appendable_snadd(app, "(null)", 6); - - if (occ->conversion == 'S') { - buf = get_arm_reg_name(X, nr, 1); - } - else { /* 'D' */ - buf = get_arm_reg_name(X, nr, 0); + assert(!"not a Constant"); } - - lc_appendable_chadd(app, '%'); - return lc_appendable_snadd(app, buf, strlen(buf)); } /** * Returns the tarval or offset of an arm node as a string. */ -static int arm_const_to_str(lc_appendable_t *app, - const lc_arg_occ_t *occ, const lc_arg_value_t *arg) -{ - char buffer[SNPRINTF_BUF_LEN]; - const char *buf; - ir_node *X = arg->v_ptr; +void arm_emit_shift(const ir_node *node) { + arm_shift_modifier mod; - if (!X) - return lc_appendable_snadd(app, "(null)", 6); + mod = get_arm_shift_modifier(node); + if (ARM_HAS_SHIFT(mod)) { + long v = get_arm_imm_value(node); - if (occ->conversion == 'C') { - buf = node_const_to_str(X, buffer, sizeof(buffer)); - } - else { /* 'O' */ - buf = node_offset_to_str(X, buffer, sizeof(buffer)); + be_emit_irprintf(", %s #%l", arm_shf_mod_name(mod), v); } - - return lc_appendable_snadd(app, buf, strlen(buf)); } +/** An entry in the sym_or_tv set. */ +typedef struct sym_or_tv_t { + union { + ident *id; /**< An ident. */ + tarval *tv; /**< A tarval. */ + const void *generic; /**< For generic compare. */ + } u; + unsigned label; /**< the associated label. */ + char is_ident; /**< Non-zero if an ident is stored. */ +} sym_or_tv_t; + /** - * Returns the tarval or offset of an arm node as a string. + * Returns a unique label. This number will not be used a second time. */ -static int arm_shift_str(lc_appendable_t *app, - const lc_arg_occ_t *occ, const lc_arg_value_t *arg) -{ - char buffer[SNPRINTF_BUF_LEN]; - ir_node *irn = arg->v_ptr; - arm_shift_modifier mod; - - if (!irn) - return lc_appendable_snadd(app, "(null)", 6); - - mod = get_arm_shift_modifier(irn); - if (ARM_HAS_SHIFT(mod)) { - long v = get_tarval_long(get_arm_value(irn)); - - snprintf(buffer, sizeof(buffer), ", %s #%ld", arm_shf_mod_name(mod), v); - return lc_appendable_snadd(app, buffer, strlen(buffer)); - } - return 0; +static unsigned get_unique_label(void) { + static unsigned id = 0; + return ++id; } /** - * Determines the instruction suffix depending on the mode. + * Emit a SymConst. */ -static int arm_get_mode_suffix(lc_appendable_t *app, - const lc_arg_occ_t *occ, const lc_arg_value_t *arg) -{ - ir_node *irn = arg->v_ptr; - arm_attr_t *attr; - ir_mode *mode; - int bits; - - if (! irn) - return lc_appendable_snadd(app, "(null)", 6); - - attr = get_arm_attr(irn); - mode = attr->op_mode ? attr->op_mode : get_irn_mode(irn); - bits = get_mode_size_bits(mode); +static void emit_arm_SymConst(const ir_node *irn) { + sym_or_tv_t key, *entry; + unsigned label; + + key.u.id = get_arm_symconst_id(irn); + key.is_ident = 1; + key.label = 0; + entry = (sym_or_tv_t *)set_insert(sym_or_tv, &key, sizeof(key), HASH_PTR(key.u.generic)); + if (entry->label == 0) { + /* allocate a label */ + entry->label = get_unique_label(); + } + label = entry->label; - if (bits == 32) - return lc_appendable_chadd(app, 's'); - else if (bits == 64) - return lc_appendable_chadd(app, 'd'); - else - return lc_appendable_chadd(app, 'e'); + /* load the symbol indirect */ + be_emit_cstring("\tldr "); + arm_emit_dest_register(irn, 0); + be_emit_irprintf(", .L%u", label); + be_emit_finish_line_gas(irn); } /** - * Return the arm printf arg environment. - * We use the firm environment with some additional handlers. + * Emit a floating point fpa constant. */ -const lc_arg_env_t *arm_get_arg_env(void) { - static lc_arg_env_t *env = NULL; - - static const lc_arg_handler_t arm_reg_handler = { arm_get_arg_type, arm_get_reg_name }; - static const lc_arg_handler_t arm_const_handler = { arm_get_arg_type, arm_const_to_str }; - static const lc_arg_handler_t arm_mode_handler = { arm_get_arg_type, arm_get_mode_suffix }; - static const lc_arg_handler_t arm_shf_handler = { arm_get_arg_type, arm_shift_str }; - - if (env == NULL) { - /* extend the firm printer */ - env = firm_get_arg_env(); - //lc_arg_new_env(); +static void emit_arm_fpaConst(const ir_node *irn) { + sym_or_tv_t key, *entry; + unsigned label; + ir_mode *mode; - lc_arg_register(env, "arm:sreg", 'S', &arm_reg_handler); - lc_arg_register(env, "arm:dreg", 'D', &arm_reg_handler); - lc_arg_register(env, "arm:cnst", 'C', &arm_const_handler); - lc_arg_register(env, "arm:offs", 'O', &arm_const_handler); - lc_arg_register(env, "arm:mode", 'M', &arm_mode_handler); - lc_arg_register(env, "arm:shf", 'X', &arm_shf_handler); + key.u.tv = get_fpaConst_value(irn); + key.is_ident = 0; + key.label = 0; + entry = (sym_or_tv_t *)set_insert(sym_or_tv, &key, sizeof(key), HASH_PTR(key.u.generic)); + if (entry->label == 0) { + /* allocate a label */ + entry->label = get_unique_label(); } + label = entry->label; - return env; -} + /* load the tarval indirect */ + mode = get_irn_mode(irn); + be_emit_cstring("\tldf"); + arm_emit_fpa_postfix(mode); + be_emit_char(' '); -/** - * Formated print of commands and comments. - */ -static void arm_fprintf_format(FILE *F, const char *cmd_buf, const char *cmnt_buf, const ir_node *irn) { - lc_efprintf(arm_get_arg_env(), F, "\t%-35s %-60s /* %+F (%G) */\n", cmd_buf, cmnt_buf, irn, irn); + arm_emit_dest_register(irn, 0); + be_emit_irprintf(", .L%u", label); + be_emit_finish_line_gas(irn); } /** - * Returns a unique label. This number will not be used a second time. + * Returns the next block in a block schedule. */ -static unsigned get_unique_label(void) { - static unsigned id = 0; - return ++id; +static ir_node *sched_next_block(const ir_node *block) { + return get_irn_link(block); } - /** - * Returns the target label for a control flow node. + * Returns the target block 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_%d", get_irn_node_nr(bl)); - return buf; +static ir_node *get_cfop_target_block(const ir_node *irn) { + return get_irn_link(irn); } /** - * Emit a SymConst + * Emits a block label for the given block. */ -static void emit_arm_SymConst(ir_node *irn, void *env) { - arm_emit_env_t *emit_env = env; - FILE *F = emit_env->out; - SymConstEntry *entry = obstack_alloc(&emit_env->obst, sizeof(*entry)); - const lc_arg_env_t *arg_env = arm_get_arg_env(); - char cmd_buf[256]; - - entry->label = get_unique_label(); - entry->symconst = irn; - entry->next = emit_env->symbols; - emit_env->symbols = entry; - - lc_esnprintf(arg_env, cmd_buf, 256, "ldr %1D, .L%u", irn, entry->label); - arm_fprintf_format(F, cmd_buf, "/* indirect SymConst */", irn); +static void arm_emit_block_name(const ir_node *block) { + if (has_Block_label(block)) { + be_emit_string(be_gas_label_prefix()); + be_emit_irprintf("%lu", get_Block_label(block)); + } else { + be_emit_cstring(BLOCK_PREFIX); + be_emit_irprintf("%d", get_irn_node_nr(block)); + } } /** - * Returns the next block in a block schedule. + * Emit the target label for a control flow node. */ -static ir_node *sched_next_block(ir_node *block) { - return get_irn_link(block); +static void arm_emit_cfop_target(const ir_node *irn) { + ir_node *block = get_cfop_target_block(irn); + + arm_emit_block_name(block); } /** - * Emit a conditional jump. + * Emit a Compare with conditional branch. */ -static void emit_arm_CondJmp(ir_node *irn, void *env) { - arm_emit_env_t *emit_env = env; - FILE *out = emit_env->out; +static void emit_arm_CmpBra(const ir_node *irn) { const ir_edge_t *edge; - ir_node *true_block = NULL; - ir_node *false_block = NULL; + const ir_node *proj_true = NULL; + const ir_node *proj_false = NULL; + const ir_node *block; + const ir_node *next_block; ir_node *op1 = get_irn_n(irn, 0); ir_mode *opmode = get_irn_mode(op1); - char *suffix; - int proj_num = get_arm_proj_num(irn); - char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; - + const char *suffix; + int proj_num = get_arm_CondJmp_proj_num(irn); foreach_out_edge(irn, edge) { ir_node *proj = get_edge_src_irn(edge); long nr = get_Proj_proj(proj); - ir_node *block = get_irn_link(proj); - if ( nr == pn_Cond_true) { - true_block = block; - } else if (nr == pn_Cond_false) { - false_block = block; + if (nr == pn_Cond_true) { + proj_true = proj; } else { - assert(0 && "tertium non datur! (CondJmp)"); + proj_false = proj; } } + /* for now, the code works for scheduled and non-schedules blocks */ + block = get_nodes_block(irn); + + /* we have a block schedule */ + next_block = sched_next_block(block); + if (proj_num == pn_Cmp_False) { /* always false: should not happen */ - fprintf(out, "\tb BLOCK_%d\t\t\t/* false case */\n", get_irn_node_nr(false_block)); + be_emit_cstring("\tb "); + arm_emit_cfop_target(proj_false); + be_emit_finish_line_gas(proj_false); } else if (proj_num == pn_Cmp_True) { /* always true: should not happen */ - fprintf(out, "\tb BLOCK_%d\t\t\t/* true case */\n", get_irn_node_nr(true_block)); + be_emit_cstring("\tb "); + arm_emit_cfop_target(proj_true); + be_emit_finish_line_gas(proj_true); } else { - ir_node *block = get_nodes_block(irn); - if (mode_is_float(opmode)) { suffix = "ICHWILLIMPLEMENTIERTWERDEN"; - lc_esnprintf(arm_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "fcmp %1S, %2S", irn, irn); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* Compare(%1S, %2S) -> FCPSR */", irn, irn ); - arm_fprintf_format(out, cmd_buf, cmnt_buf, irn); + be_emit_cstring("\tfcmp "); + arm_emit_source_register(irn, 0); + be_emit_cstring(", "); + arm_emit_source_register(irn, 1); + be_emit_finish_line_gas(irn); - lc_esnprintf(arm_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "fmstat", irn, irn); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* FCSPR -> CPSR */"); - arm_fprintf_format(out, cmd_buf, cmnt_buf, irn); + be_emit_cstring("\tfmstat"); + be_emit_pad_comment(); + be_emit_cstring("/* FCSPR -> CPSR */"); + be_emit_finish_line_gas(NULL); } else { - if (true_block == sched_next_block(block)) { - /* negate it */ - proj_num = get_negated_pnc(proj_num, opmode); + if (get_cfop_target_block(proj_true) == next_block) { + /* exchange both proj's so the second one can be omitted */ + const ir_node *t = proj_true; + + proj_true = proj_false; + proj_false = t; + proj_num = get_negated_pnc(proj_num, mode_Iu); } - switch(proj_num) { + switch (proj_num) { case pn_Cmp_Eq: suffix = "eq"; break; case pn_Cmp_Lt: suffix = "lt"; break; case pn_Cmp_Le: suffix = "le"; break; @@ -447,99 +425,208 @@ static void emit_arm_CondJmp(ir_node *irn, void *env) { case pn_Cmp_Ge: suffix = "ge"; break; case pn_Cmp_Lg: suffix = "ne"; break; case pn_Cmp_Leg: suffix = "al"; break; - default: assert(0 && "komische Dinge geschehen"); suffix = "al"; + default: assert(!"Cmp unsupported"); suffix = "al"; } - - lc_esnprintf(arm_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "cmp %1S, %2S", irn, irn); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* Compare(%1S, %2S) -> CPSR */", irn, irn ); - arm_fprintf_format(out, cmd_buf, cmnt_buf, irn); + be_emit_cstring("\tcmp "); + arm_emit_source_register(irn, 0); + be_emit_cstring(", "); + arm_emit_source_register(irn, 1); + be_emit_finish_line_gas(irn); } - if (true_block == sched_next_block(block)) { - lc_esnprintf(arm_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "b%s BLOCK_%d", suffix, get_irn_node_nr(true_block)); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* false case */"); - arm_fprintf_format(out, cmd_buf, cmnt_buf, irn); + /* emit the true proj */ + be_emit_irprintf("\tb%s ", suffix); + arm_emit_cfop_target(proj_true); + be_emit_finish_line_gas(proj_true); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* fallthrough BLOCK_%d */", get_irn_node_nr(false_block)); - arm_fprintf_format(out, "", cmnt_buf, irn); + if (get_cfop_target_block(proj_false) == next_block) { + be_emit_cstring("\t/* fallthrough to "); + arm_emit_cfop_target(proj_false); + be_emit_cstring(" */"); + be_emit_finish_line_gas(proj_false); + } else { + be_emit_cstring("b "); + arm_emit_cfop_target(proj_false); + be_emit_finish_line_gas(proj_false); } - else { - lc_esnprintf(arm_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "b%s BLOCK_%d", suffix, get_irn_node_nr(true_block)); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* true case */"); - arm_fprintf_format(out, cmd_buf, cmnt_buf, irn); - - if (false_block == sched_next_block(block)) { - lc_esnprintf(arm_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* fallthrough BLOCK_%d */", get_irn_node_nr(false_block)); - arm_fprintf_format(out, "", cmnt_buf, irn); - } - else { - lc_esnprintf(arm_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "b BLOCK_%d", get_irn_node_nr(false_block)); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* false case */"); - arm_fprintf_format(out, cmd_buf, cmnt_buf, irn); - } - } } } -static void emit_arm_CopyB(ir_node *irn, void *env) { - arm_emit_env_t *emit_env = env; - FILE *out = emit_env->out; - char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; - unsigned int size = get_tarval_long(get_arm_value(irn)); - const lc_arg_env_t *arg_env = arm_get_arg_env(); +/** + * Emit a Compare with conditional branch. + */ +static void emit_arm_fpaCmfBra(const ir_node *irn) { + (void) irn; +} + +/** + * Emit a Compare with conditional branch. + */ +static void emit_arm_fpaCmfeBra(const ir_node *irn) { + (void) irn; +} + +/** Sort register in ascending order. */ +static int reg_cmp(const void *a, const void *b) { + const arch_register_t * const *ra = a; + const arch_register_t * const *rb = b; - lc_esnprintf(arg_env, cmnt_buf, SNPRINTF_BUF_LEN, "/* MemCopy (%2S)->(%1S) [%d bytes], Use %3S, %4S, %5S and %%r12 */", irn, irn, size, irn, irn, irn); + return *ra < *rb ? -1 : (*ra != *rb); +} - assert ( size > 0 && "CopyB needs size > 0" ); - if (size & 3) +/** + * Create the CopyB instruction sequence. + */ +static void emit_arm_CopyB(const ir_node *irn) { + unsigned size = (unsigned)get_arm_imm_value(irn); + + const char *tgt = arch_register_get_name(get_in_reg(irn, 0)); + const char *src = arch_register_get_name(get_in_reg(irn, 1)); + const char *t0, *t1, *t2, *t3; + + const arch_register_t *tmpregs[4]; + + /* collect the temporary registers and sort them, we need ascending order */ + tmpregs[0] = get_in_reg(irn, 2); + tmpregs[1] = get_in_reg(irn, 3); + tmpregs[2] = get_in_reg(irn, 4); + tmpregs[3] = &arm_gp_regs[REG_R12]; + + /* Note: R12 is always the last register because the RA did not assign higher ones */ + qsort((void *)tmpregs, 3, sizeof(tmpregs[0]), reg_cmp); + + /* need ascending order */ + t0 = arch_register_get_name(tmpregs[0]); + t1 = arch_register_get_name(tmpregs[1]); + t2 = arch_register_get_name(tmpregs[2]); + t3 = arch_register_get_name(tmpregs[3]); + + be_emit_cstring("/* MemCopy ("); + be_emit_string(src); + be_emit_cstring(")->("); + arm_emit_source_register(irn, 0); + be_emit_irprintf(" [%u bytes], Uses ", size); + be_emit_string(t0); + be_emit_cstring(", "); + be_emit_string(t1); + be_emit_cstring(", "); + be_emit_string(t2); + be_emit_cstring(", and "); + be_emit_string(t3); + be_emit_cstring("*/"); + be_emit_finish_line_gas(NULL); + + assert(size > 0 && "CopyB needs size > 0" ); + + if (size & 3) { + assert(!"strange hack enabled: copy more bytes than needed!"); size += 4; + } + size >>= 2; - switch(size & 3) { + switch (size & 3) { case 0: break; case 1: - lc_esnprintf(arg_env, cmd_buf, SNPRINTF_BUF_LEN, "ldr %%r12, [%2S, #0]!", irn); - arm_fprintf_format(out, cmd_buf, cmnt_buf, irn); - lc_esnprintf(arg_env, cmd_buf, SNPRINTF_BUF_LEN, "str %%r12, [%1S, #0]!", irn); - arm_fprintf_format(out, cmd_buf, cmnt_buf, irn); + be_emit_cstring("\tldr "); + be_emit_string(t3); + be_emit_cstring(", ["); + be_emit_string(src); + be_emit_cstring(", #0]"); + be_emit_finish_line_gas(NULL); + + be_emit_cstring("\tstr "); + be_emit_string(t3); + be_emit_cstring(", ["); + be_emit_string(tgt); + be_emit_cstring(", #0]"); + be_emit_finish_line_gas(irn); break; case 2: - lc_esnprintf(arg_env, cmd_buf, SNPRINTF_BUF_LEN, "ldmia %2S!, {%%r12, %3S}", irn, irn); - arm_fprintf_format(out, cmd_buf, cmnt_buf, irn); - lc_esnprintf(arg_env, cmd_buf, SNPRINTF_BUF_LEN, "stmia %1S!, {%%r12, %3S}", irn, irn); - arm_fprintf_format(out, cmd_buf, cmnt_buf, irn); + be_emit_cstring("\tldmia "); + be_emit_string(src); + be_emit_cstring("!, {"); + be_emit_string(t0); + be_emit_cstring(", "); + be_emit_string(t1); + be_emit_char('}'); + be_emit_finish_line_gas(NULL); + + be_emit_cstring("\tstmia "); + be_emit_string(tgt); + be_emit_cstring("!, {"); + be_emit_string(t0); + be_emit_cstring(", "); + be_emit_string(t1); + be_emit_char('}'); + be_emit_finish_line_gas(irn); break; case 3: - lc_esnprintf(arg_env, cmd_buf, SNPRINTF_BUF_LEN, "ldmia %2S!, {%%r12, %3S, %4S}", irn, irn, irn); - arm_fprintf_format(out, cmd_buf, cmnt_buf, irn); - lc_esnprintf(arg_env, cmd_buf, SNPRINTF_BUF_LEN, "stmia %1S!, {%%r12, %3S, %4S}", irn, irn, irn); - arm_fprintf_format(out, cmd_buf, cmnt_buf, irn); + be_emit_cstring("\tldmia "); + be_emit_string(src); + be_emit_cstring("!, {"); + be_emit_string(t0); + be_emit_cstring(", "); + be_emit_string(t1); + be_emit_cstring(", "); + be_emit_string(t2); + be_emit_char('}'); + be_emit_finish_line_gas(NULL); + + be_emit_cstring("\tstmia "); + be_emit_string(tgt); + be_emit_cstring("!, {"); + be_emit_string(t0); + be_emit_cstring(", "); + be_emit_string(t1); + be_emit_cstring(", "); + be_emit_string(t2); + be_emit_char('}'); + be_emit_finish_line_gas(irn); break; } size >>= 2; while (size) { - lc_esnprintf(arg_env, cmd_buf, SNPRINTF_BUF_LEN, "ldmia %2S!, {%%r12, %3S, %4S, %5S}", irn, irn, irn, irn); - arm_fprintf_format(out, cmd_buf, cmnt_buf, irn); - lc_esnprintf(arg_env, cmd_buf, SNPRINTF_BUF_LEN, "stmia %1S!, {%%r12, %3S, %4S, %5S}", irn, irn, irn, irn); - arm_fprintf_format(out, cmd_buf, cmnt_buf, irn); + be_emit_cstring("\tldmia "); + be_emit_string(src); + be_emit_cstring("!, {"); + be_emit_string(t0); + be_emit_cstring(", "); + be_emit_string(t1); + be_emit_cstring(", "); + be_emit_string(t2); + be_emit_cstring(", "); + be_emit_string(t3); + be_emit_char('}'); + be_emit_finish_line_gas(NULL); + + be_emit_cstring("\tstmia "); + be_emit_string(tgt); + be_emit_cstring("!, {"); + be_emit_string(t0); + be_emit_cstring(", "); + be_emit_string(t1); + be_emit_cstring(", "); + be_emit_string(t2); + be_emit_cstring(", "); + be_emit_string(t3); + be_emit_char('}'); + be_emit_finish_line_gas(irn); --size; } } -static void emit_arm_SwitchJmp(ir_node *irn, void *env) { - char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; +static void emit_arm_SwitchJmp(const ir_node *irn) { const ir_edge_t *edge; ir_node *proj; - arm_emit_env_t *emit_env = env; - FILE *out = emit_env->out; int i; ir_node **projs; int n_projs; int block_nr; - int default_block_num; + ir_node *default_proj = NULL; block_nr = get_irn_node_nr(irn); - n_projs = get_arm_n_projs(irn); + n_projs = get_arm_SwitchJmp_n_projs(irn); projs = xcalloc(n_projs , sizeof(ir_node*)); @@ -547,59 +634,62 @@ static void emit_arm_SwitchJmp(ir_node *irn, void *env) { proj = get_edge_src_irn(edge); assert(is_Proj(proj) && "Only proj allowed at SwitchJmp"); - if (get_Proj_proj(proj) == get_arm_default_proj_num(irn)) - default_block_num = get_irn_node_nr(get_irn_link(proj)); + if (get_Proj_proj(proj) == get_arm_SwitchJmp_default_proj_num(irn)) + default_proj = proj; projs[get_Proj_proj(proj)] = proj; } - - // CMP %1S, n_projs - 1 - // BHI default - - - - lc_esnprintf(arm_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "cmp %1S, #%u", irn, n_projs - 1); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "", irn); - arm_fprintf_format(out, cmd_buf, cmnt_buf, irn); - - lc_esnprintf(arm_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "bhi BLOCK_%d", default_block_num); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "", irn); - arm_fprintf_format(out, cmd_buf, cmnt_buf, irn); - - - lc_esnprintf(arm_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "ldr %%r12, TABLE_%d_START", block_nr); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "", irn); - arm_fprintf_format(out, cmd_buf, cmnt_buf, irn); - - lc_esnprintf(arm_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "add %%r12, %%r12, %1S, LSL #2", irn); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "", irn); - arm_fprintf_format(out, cmd_buf, cmnt_buf, irn); - - lc_esnprintf(arm_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "ldr %%r15, [%%r12, #0]"); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "", irn); - arm_fprintf_format(out, cmd_buf, cmnt_buf, irn); - - // LDR %r12, .TABLE_X_START - // ADD %r12, %r12, [%1S, LSL #2] - // LDR %r15, %r12 - - fprintf(out, "TABLE_%d_START:\n\t.word\tTABLE_%d\n", block_nr, block_nr); - fprintf(out, "\t.align 2\n"); - fprintf(out, "TABLE_%d:\n", block_nr); - - - for ( i=0; iout; - entity *ent = be_Call_get_entity(irn); - char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; +static void emit_be_Call(const ir_node *irn) { + ir_entity *ent = be_Call_get_entity(irn); - if (ent) - lc_esnprintf(arm_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "bl %s", get_entity_ld_name(ent)); - else - lc_esnprintf(arm_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "%1D", get_irn_n(irn, be_pos_Call_ptr)); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* %+F (be_Call) */", irn); - arm_fprintf_format(F, cmd_buf, cmnt_buf, irn); + be_emit_cstring("\tbl "); + if (ent) { + set_entity_backend_marked(ent, 1); + be_emit_ident(get_entity_ld_ident(ent)); + } else { + arm_emit_source_register(irn, be_pos_Call_ptr); + } + be_emit_finish_line_gas(irn); } /** Emit an IncSP node */ -static void emit_be_IncSP(const ir_node *irn, arm_emit_env_t *emit_env) { - FILE *F = emit_env->out; - unsigned offs = be_get_IncSP_offset(irn); - if (offs) { - char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; - lc_esnprintf(arm_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "add %1D, %1S, #%O", irn, irn, irn ); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* IncSP(%O) */", irn); - arm_fprintf_format(F, cmd_buf, cmnt_buf, irn); +static void emit_be_IncSP(const ir_node *irn) { + int offs = be_get_IncSP_offset(irn); + + if (offs != 0) { + be_emit_cstring("\tadd "); + arm_emit_dest_register(irn, 0); + be_emit_cstring(", "); + arm_emit_source_register(irn, 0); + be_emit_cstring(", #"); + arm_emit_offset(irn); } else { - char cmnt_buf[SNPRINTF_BUF_LEN]; - lc_esnprintf(arm_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* omitted IncSP(%O) */", irn); - arm_fprintf_format(F, "", cmnt_buf, irn); + be_emit_cstring("\t/* omitted IncSP("); + arm_emit_offset(irn); + be_emit_cstring(") */"); } + be_emit_finish_line_gas(irn); } -// void emit_be_AddSP(const ir_node *irn, arm_emit_env_t *emit_env) { -// FILE *F = emit_env->out; -// char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; -// lc_esnprintf(arm_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "ADD %1D, %1S, %2S", irn, irn, irn ); -// lc_esnprintf(arm_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* AddSP(%2S) */", irn); -// lc_efprintf(arm_get_arg_env(), F, "\t%-35s %-60s /* %+F */\n", cmd_buf, cmnt_buf, irn); -// } - -static void emit_be_Copy(const ir_node *irn, arm_emit_env_t *emit_env) { - FILE *F = emit_env->out; +static void emit_be_Copy(const ir_node *irn) { ir_mode *mode = get_irn_mode(irn); - const lc_arg_env_t *arm_env = arm_get_arg_env(); - char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; if (get_in_reg(irn, 0) == get_out_reg(irn, 0)) { - lc_esnprintf(arm_env, cmnt_buf, SNPRINTF_BUF_LEN, "/* omitted Copy: %1S -> %1D */", irn, irn); - lc_efprintf(arm_env, F, "\t%-35s %-60s /* %+F */\n", "", cmnt_buf, irn); + be_emit_cstring("\t/* omitted Copy: "); + arm_emit_source_register(irn, 0); + be_emit_cstring(" -> "); + arm_emit_dest_register(irn, 0); + be_emit_finish_line_gas(irn); return; } if (mode_is_float(mode)) { - if (USE_FPA(emit_env->cg->isa)) { - lc_esnprintf(arm_env, cmd_buf, SNPRINTF_BUF_LEN, "mvf%M %1D, %1S", irn, irn, irn); - lc_esnprintf(arm_env, cmnt_buf, SNPRINTF_BUF_LEN, "/* Copy: %1S -> %1D */", irn, irn); - arm_fprintf_format(F, cmd_buf, cmnt_buf, irn); - } - else { + if (USE_FPA(isa)) { + be_emit_cstring("\tmvf"); + arm_emit_mode(irn); + be_emit_char(' '); + arm_emit_dest_register(irn, 0); + be_emit_cstring(", "); + arm_emit_source_register(irn, 0); + be_emit_finish_line_gas(irn); + } else { assert(0 && "move not supported for this mode"); + panic("emit_be_Copy: move not supported for this mode"); } - } else if (mode_is_numP(mode)) { - lc_esnprintf(arm_env, cmd_buf, SNPRINTF_BUF_LEN, "mov %1D, %1S", irn, irn); - lc_esnprintf(arm_env, cmnt_buf, SNPRINTF_BUF_LEN, "/* Copy: %1S -> %1D */", irn, irn); - arm_fprintf_format(F, cmd_buf, cmnt_buf, irn); + } else if (mode_is_data(mode)) { + be_emit_cstring("\tmov "); + arm_emit_dest_register(irn, 0); + be_emit_cstring(", "); + arm_emit_source_register(irn, 0); + be_emit_finish_line_gas(irn); } else { assert(0 && "move not supported for this mode"); + panic("emit_be_Copy: move not supported for this mode"); } -// emit_arm_Copy(irn, emit_env); } /** * Emit code for a Spill. */ -static void emit_be_Spill(const ir_node *irn, arm_emit_env_t *emit_env) { - FILE *F = emit_env->out; - ir_mode *mode = get_irn_mode(irn); - const lc_arg_env_t *arm_env = arm_get_arg_env(); - char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; +static void emit_be_Spill(const ir_node *irn) { + ir_mode *mode = get_irn_mode(be_get_Spill_val(irn)); if (mode_is_float(mode)) { - if (USE_FPA(emit_env->cg->isa)) { - lc_esnprintf(arm_env, cmd_buf, SNPRINTF_BUF_LEN, "stf %2S, [%1S, #%O]", irn, irn, irn ); - lc_esnprintf(arm_env, cmnt_buf, SNPRINTF_BUF_LEN, "/* Spill(%2S) -> (%1S) */", irn, irn); - arm_fprintf_format(F, cmd_buf, cmnt_buf, irn); - } - else { - assert(0 && "move not supported for this mode"); + if (USE_FPA(cg->isa)) { + be_emit_cstring("\tstf"); + arm_emit_fpa_postfix(mode); + be_emit_char(' '); + } else { + assert(0 && "spill not supported for this mode"); + panic("emit_be_Spill: spill not supported for this mode"); } } else if (mode_is_dataM(mode)) { - lc_esnprintf(arm_env, cmd_buf, SNPRINTF_BUF_LEN, "str %2S, [%1S, #%O]", irn, irn, irn ); - lc_esnprintf(arm_env, cmnt_buf, SNPRINTF_BUF_LEN, "/* Spill(%2S) -> (%1S) */", irn, irn); - arm_fprintf_format(F, cmd_buf, cmnt_buf, irn); + be_emit_cstring("\tstr "); } else { assert(0 && "spill not supported for this mode"); + panic("emit_be_Spill: spill not supported for this mode"); } + arm_emit_source_register(irn, 1); + be_emit_cstring(", ["); + arm_emit_source_register(irn, 0); + be_emit_cstring(", #"); + arm_emit_offset(irn); + be_emit_char(']'); + be_emit_finish_line_gas(irn); } /** * Emit code for a Reload. */ -static void emit_be_Reload(const ir_node* irn, arm_emit_env_t *emit_env) { - FILE *F = emit_env->out; +static void emit_be_Reload(const ir_node *irn) { ir_mode *mode = get_irn_mode(irn); - const lc_arg_env_t *arm_env = arm_get_arg_env(); - char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; if (mode_is_float(mode)) { - if (USE_FPA(emit_env->cg->isa)) { - lc_esnprintf(arm_env, cmd_buf, SNPRINTF_BUF_LEN, "ldf %1D, [%1S, #%O]", irn, irn, irn ); - lc_esnprintf(arm_env, cmnt_buf, SNPRINTF_BUF_LEN, "/* Reload(%1S) -> (%1D) */", irn, irn); - arm_fprintf_format(F, cmd_buf, cmnt_buf, irn); - } - else { - assert(0 && "move not supported for this mode"); + if (USE_FPA(cg->isa)) { + be_emit_cstring("\tldf"); + arm_emit_fpa_postfix(mode); + be_emit_char(' '); + } else { + assert(0 && "reload not supported for this mode"); + panic("emit_be_Reload: reload not supported for this mode"); } } else if (mode_is_dataM(mode)) { - lc_esnprintf(arm_env, cmd_buf, SNPRINTF_BUF_LEN, "ldr %1D, [%1S, #%O]", irn, irn, irn ); - lc_esnprintf(arm_env, cmnt_buf, SNPRINTF_BUF_LEN, "/* Reload(%1S) -> (%1D) */", irn, irn); - arm_fprintf_format(F, cmd_buf, cmnt_buf, irn); + be_emit_cstring("\tldr "); } else { assert(0 && "reload not supported for this mode"); + panic("emit_be_Reload: reload not supported for this mode"); } + arm_emit_dest_register(irn, 0); + be_emit_cstring(", ["); + arm_emit_source_register(irn, 0); + be_emit_cstring(", #"); + arm_emit_offset(irn); + be_emit_char(']'); + be_emit_finish_line_gas(irn); } -static void emit_be_Perm(const ir_node* irn, arm_emit_env_t *emit_env) { - FILE *F = emit_env->out; - ir_mode *mode = get_irn_mode(irn); - const lc_arg_env_t *arm_env = arm_get_arg_env(); - char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; - - lc_esnprintf(arm_env, cmd_buf, SNPRINTF_BUF_LEN, "eor %1S, %1S, %2S", irn, irn, irn); - lc_esnprintf(arm_env, cmnt_buf, SNPRINTF_BUF_LEN, "/* begin Perm(%1S, %2S) */", irn, irn); - arm_fprintf_format(F, cmd_buf, cmnt_buf, irn); - - lc_esnprintf(arm_env, cmd_buf, SNPRINTF_BUF_LEN, "eor %2S, %1S, %2S", irn, irn, irn); - lc_esnprintf(arm_env, cmnt_buf, SNPRINTF_BUF_LEN, " "); - arm_fprintf_format(F, cmd_buf, cmnt_buf, irn); - - lc_esnprintf(arm_env, cmd_buf, SNPRINTF_BUF_LEN, "eor %1S, %1S, %2S", irn, irn, irn); - lc_esnprintf(arm_env, cmnt_buf, SNPRINTF_BUF_LEN, "/* end Perm(%1S, %2S) */", irn, irn); - arm_fprintf_format(F, cmd_buf, cmnt_buf, irn); -} - -static void emit_be_StackParam(const ir_node *irn, arm_emit_env_t *emit_env) { - FILE *F = emit_env->out; - ir_mode *mode = get_irn_mode(irn); - const lc_arg_env_t *arm_env = arm_get_arg_env(); - char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; - - if (mode_is_float(mode)) { - if (USE_FPA(emit_env->cg->isa)) { - lc_esnprintf(arm_env, cmd_buf, SNPRINTF_BUF_LEN, "ldf %1D, [%1S, #%O]", irn, irn, irn ); - lc_esnprintf(arm_env, cmnt_buf, SNPRINTF_BUF_LEN, "/* StackParam: (%1S + %O) -> %1D */",irn , irn, irn, get_irn_n(irn, 0)); - arm_fprintf_format(F, cmd_buf, cmnt_buf, irn); - } - else { - assert(0 && "move not supported for this mode"); - } - } else { - lc_esnprintf(arm_env, cmd_buf, SNPRINTF_BUF_LEN, "ldr %1D, [%1S, #%O]", irn, irn, irn ); - lc_esnprintf(arm_env, cmnt_buf, SNPRINTF_BUF_LEN, "/* StackParam: (%1S + %O) -> %1D */",irn , irn, irn, get_irn_n(irn, 0)); - arm_fprintf_format(F, cmd_buf, cmnt_buf, irn); - } +static void emit_be_Perm(const ir_node *irn) { + be_emit_cstring("\teor "); + arm_emit_source_register(irn, 0); + be_emit_cstring(", "); + arm_emit_source_register(irn, 0); + be_emit_cstring(", "); + arm_emit_source_register(irn, 1); + be_emit_finish_line_gas(NULL); + + be_emit_cstring("\teor "); + arm_emit_source_register(irn, 1); + be_emit_cstring(", "); + arm_emit_source_register(irn, 0); + be_emit_cstring(", "); + arm_emit_source_register(irn, 1); + be_emit_finish_line_gas(NULL); + + be_emit_cstring("\teor "); + arm_emit_source_register(irn, 0); + be_emit_cstring(", "); + arm_emit_source_register(irn, 0); + be_emit_cstring(", "); + arm_emit_source_register(irn, 1); + be_emit_finish_line_gas(irn); } /************************************************************************/ /* emit */ /************************************************************************/ -static void emit_Jmp(ir_node *irn, void *env) { - char cmd_buf[SNPRINTF_BUF_LEN]; - arm_emit_env_t *emit_env = env; - FILE *F = emit_env->out; - const ir_edge_t *edge = get_irn_out_edge_first(irn); - const lc_arg_env_t *arm_env = arm_get_arg_env(); - ir_node *target_block = get_edge_src_irn(edge); - - lc_esnprintf(arm_env, cmd_buf, SNPRINTF_BUF_LEN, "b BLOCK_%d", get_irn_node_nr(target_block)); - arm_fprintf_format(F, cmd_buf, "/* unconditional Jump */", irn); -} - -static void emit_arm_fpaDbl2GP(const ir_node *n, arm_emit_env_t *env) { - FILE *F = env->out; - char cmd_buf[256]; - const lc_arg_env_t *arg_env = arm_get_arg_env(); +static void emit_Jmp(const ir_node *node) { + ir_node *block, *next_block; - lc_esnprintf(arg_env, cmd_buf, 256, "stfd %1S, [sp, #-8]! ", n); - arm_fprintf_format(F, cmd_buf, "/* Push fp to stack */", n); + /* for now, the code works for scheduled and non-schedules blocks */ + block = get_nodes_block(node); - lc_esnprintf(arg_env, cmd_buf, 256, "ldmfd sp!, {%2D, %1D} ", n, n); - arm_fprintf_format(F, cmd_buf, "/* Pop destination */", n); + /* we have a block schedule */ + next_block = sched_next_block(block); + if (get_cfop_target_block(node) != next_block) { + be_emit_cstring("\tb "); + arm_emit_cfop_target(node); + } else { + be_emit_cstring("\t/* fallthrough to "); + arm_emit_cfop_target(node); + be_emit_cstring(" */"); + } + be_emit_finish_line_gas(node); } -static void emit_silence(ir_node *irn, void *env) { - +static void emit_arm_fpaDbl2GP(const ir_node *irn) { + be_emit_cstring("\tstfd "); + arm_emit_source_register(irn, 0); + be_emit_cstring(", [sp, #-8]!"); + be_emit_pad_comment(); + be_emit_cstring("/* Push fp to stack */"); + be_emit_finish_line_gas(NULL); + + be_emit_cstring("\tldmfd sp!, {"); + arm_emit_dest_register(irn, 1); + be_emit_cstring(", "); + arm_emit_dest_register(irn, 0); + be_emit_char('}'); + be_emit_pad_comment(); + be_emit_cstring("/* Pop destination */"); + be_emit_finish_line_gas(irn); } +static void emit_arm_LdTls(const ir_node *irn) { + (void) irn; + panic("TLS not supported for this target\n"); + /* Er... our gcc does not support it... Install a newer toolchain. */ +} /*********************************************************************************** * _ __ _ @@ -814,16 +909,33 @@ static void emit_silence(ir_node *irn, void *env) { * ***********************************************************************************/ +static void emit_silence(const ir_node *irn) { + (void) irn; + /* Do nothing. */ +} + +/** + * The type of a emitter function. + */ +typedef void (emit_func)(const ir_node *irn); + +/** + * Set a node emitter. Make it a bit more type safe. + */ +static INLINE void set_emitter(ir_op *op, emit_func arm_emit_node) { + op->ops.generic = (op_func)arm_emit_node; +} + /** * Enters the emitter functions for handled nodes into the generic * pointer of an opcode. */ static void arm_register_emitters(void) { -#define ARM_EMIT(a) op_arm_##a->ops.generic = (op_func)emit_arm_##a -#define EMIT(a) op_##a->ops.generic = (op_func)emit_##a -#define BE_EMIT(a) op_be_##a->ops.generic = (op_func)emit_be_##a -#define SILENCE(a) op_##a->ops.generic = (op_func)emit_silence +#define ARM_EMIT(a) set_emitter(op_arm_##a, emit_arm_##a) +#define EMIT(a) set_emitter(op_##a, emit_##a) +#define BE_EMIT(a) set_emitter(op_be_##a, emit_be_##a) +#define SILENCE(a) set_emitter(op_##a, emit_silence) /* first clear the generic function pointer for all ops */ clear_irp_opcodes_generic_func(); @@ -832,33 +944,39 @@ static void arm_register_emitters(void) { arm_register_spec_emitters(); /* other emitter functions */ - ARM_EMIT(CondJmp); + ARM_EMIT(CmpBra); + ARM_EMIT(fpaCmfBra); + ARM_EMIT(fpaCmfeBra); ARM_EMIT(CopyB); // ARM_EMIT(CopyB_i); // ARM_EMIT(Const); ARM_EMIT(SymConst); ARM_EMIT(SwitchJmp); ARM_EMIT(fpaDbl2GP); + ARM_EMIT(fpaConst); + ARM_EMIT(LdTls); /* benode emitter */ BE_EMIT(Call); BE_EMIT(IncSP); -// BE_EMIT(AddSP); BE_EMIT(Copy); BE_EMIT(Spill); BE_EMIT(Reload); BE_EMIT(Perm); - BE_EMIT(StackParam); /* firm emitter */ EMIT(Jmp); /* noisy stuff */ #ifdef SILENCER + SILENCE(Start); SILENCE(Proj); SILENCE(Phi); SILENCE(be_Keep); SILENCE(be_CopyKeep); + SILENCE(be_RegParams); + SILENCE(be_Barrier); + SILENCE(be_Return); #endif #undef ARM_EMIT @@ -867,70 +985,156 @@ static void arm_register_emitters(void) { #undef SILENCE } -typedef void (node_emitter)(const ir_node *, void *); +static const char *last_name = NULL; +static unsigned last_line = -1; +static unsigned num = -1; /** - * Emits code for a node. + * Emit the debug support for node node. */ -static void arm_emit_node(const ir_node *irn, void *env) { - arm_emit_env_t *emit_env = env; - FILE *F = emit_env->out; - ir_op *op = get_irn_op(irn); - DEBUG_ONLY(firm_dbg_module_t *mod = emit_env->mod;) +static void arm_emit_dbg(const ir_node *irn) { + dbg_info *db = get_irn_dbg_info(irn); + unsigned lineno; + const char *fname = ir_retrieve_dbg_info(db, &lineno); + + if (! cg->birg->main_env->options->stabs_debug_support) + return; + + if (fname) { + if (last_name != fname) { + last_line = -1; + be_dbg_include_begin(cg->birg->main_env->db_handle, fname); + last_name = fname; + } + if (last_line != lineno) { + char name[64]; + + snprintf(name, sizeof(name), ".LM%u", ++num); + last_line = lineno; + be_dbg_line(cg->birg->main_env->db_handle, lineno, name); + be_emit_string(name); + be_emit_cstring(":\n"); + be_emit_write_line(); + } + } +} - DBG((mod, LEVEL_1, "emitting code for %+F\n", irn)); +/** + * Emits code for a node. + */ +static void arm_emit_node(const ir_node *irn) { + ir_op *op = get_irn_op(irn); if (op->ops.generic) { - node_emitter *emit = (node_emitter *)op->ops.generic; - emit(irn, env); + emit_func *emit = (emit_func *)op->ops.generic; + arm_emit_dbg(irn); + (*emit)(irn); + } else { + be_emit_cstring("\t/* TODO */"); + be_emit_finish_line_gas(irn); } - else { - ir_fprintf(F, "\t%-35s /* %+F %G */\n", "", irn, irn); +} + +/** + * emit the block label if needed. + */ +static void arm_emit_block_header(ir_node *block, ir_node *prev) +{ + int n_cfgpreds; + int need_label; + int i, arity; + ir_exec_freq *exec_freq = cg->birg->exec_freq; + + need_label = 0; + n_cfgpreds = get_Block_n_cfgpreds(block); + if (n_cfgpreds == 1) { + ir_node *pred = get_Block_cfgpred(block, 0); + ir_node *pred_block = get_nodes_block(pred); + + /* we don't need labels for fallthrough blocks, however switch-jmps + * are no fallthroughs */ + if (pred_block == prev && + !(is_Proj(pred) && is_arm_SwitchJmp(get_Proj_pred(pred)))) { + need_label = 0; + } else { + need_label = 1; + } + } else { + need_label = 1; } + + if (need_label) { + arm_emit_block_name(block); + be_emit_char(':'); + + be_emit_pad_comment(); + be_emit_cstring(" /* preds:"); + + /* emit list of pred blocks in comment */ + arity = get_irn_arity(block); + for (i = 0; i < arity; ++i) { + ir_node *predblock = get_Block_cfgpred_block(block, i); + be_emit_irprintf(" %d", get_irn_node_nr(predblock)); + } + } else { + be_emit_cstring("\t/* "); + arm_emit_block_name(block); + be_emit_cstring(": "); + } + if (exec_freq != NULL) { + be_emit_irprintf(" freq: %f", + get_block_execfreq(exec_freq, block)); + } + be_emit_cstring(" */\n"); + be_emit_write_line(); } /** * Walks over the nodes in a block connected by scheduling edges * and emits code for each node. */ -void arm_gen_block(ir_node *block, void *env) { +static void arm_gen_block(ir_node *block, ir_node *prev_block) { ir_node *irn; - fprintf(((arm_emit_env_t *)env)->out, "BLOCK_%d:\n", get_irn_node_nr(block)); + arm_emit_block_header(block, prev_block); + arm_emit_dbg(block); sched_foreach(block, irn) { - arm_emit_node(irn, env); + arm_emit_node(irn); } } - /** * Emits code for function start. */ -void arm_emit_start(FILE *F, ir_graph *irg) { - entity *ent = get_irg_entity(irg); +void arm_func_prolog(ir_graph *irg) { + ir_entity *ent = get_irg_entity(irg); const char *irg_name = get_entity_ld_name(ent); - arm_switch_section(F, SECTION_TEXT); - fprintf(F, "\t.align 2\n"); + + be_emit_write_line(); + be_gas_emit_switch_section(GAS_SECTION_TEXT); + be_emit_cstring("\t.align 2\n"); if (get_entity_visibility(ent) == visibility_external_visible) - fprintf(F, "\t.global %s\n", irg_name); - fprintf(F, "%s:\n", irg_name); + be_emit_irprintf("\t.global %s\n", irg_name); + be_emit_irprintf("%s:\n", irg_name); } /** * Emits code for function end */ void arm_emit_end(FILE *F, ir_graph *irg) { + (void) irg; fprintf(F, "\t.ident \"firmcc\"\n"); } /** + * Block-walker: * Sets labels for control flow nodes (jump target) - * TODO: Jump optimization */ -void arm_gen_labels(ir_node *block, void *env) { +static void arm_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); @@ -938,33 +1142,39 @@ void arm_gen_labels(ir_node *block, void *env) { } } +/** + * Compare two entries of the symbol or tarval set. + */ +static int cmp_sym_or_tv(const void *elt, const void *key, size_t size) { + const sym_or_tv_t *p1 = elt; + const sym_or_tv_t *p2 = key; + (void) size; + + /* as an identifier NEVER can point to a tarval, it's enough + to compare it this way */ + return p1->u.generic != p2->u.generic; +} /** * Main driver. Emits the code for one routine. */ -void arm_gen_routine(FILE *F, ir_graph *irg, const arm_code_gen_t *cg) { - SymConstEntry *entry; - arm_emit_env_t emit_env; - ir_node **blk_sched; - int i, n; +void arm_gen_routine(const arm_code_gen_t *arm_cg, ir_graph *irg) { + ir_node **blk_sched; + int i, n; + ir_node *last_block = NULL; - emit_env.out = F; - emit_env.arch_env = cg->arch_env; - emit_env.cg = cg; - emit_env.symbols = NULL; - obstack_init(&emit_env.obst); - FIRM_DBG_REGISTER(emit_env.mod, "firm.be.arm.emit"); - - /* set the global arch_env (needed by print hooks) */ - arch_env = cg->arch_env; + cg = arm_cg; + isa = (const arm_isa_t *)cg->arch_env->isa; + arch_env = cg->arch_env; + sym_or_tv = new_set(cmp_sym_or_tv, 8); arm_register_emitters(); /* create the block schedule. For now, we don't need it earlier. */ - blk_sched = sched_create_block_schedule(cg->irg); + blk_sched = be_create_block_schedule(cg->irg, cg->birg->exec_freq); - arm_emit_start(F, irg); - irg_block_walk_graph(irg, arm_gen_labels, NULL, &emit_env); + arm_func_prolog(irg); + irg_block_walk_graph(irg, arm_gen_labels, NULL, NULL); n = ARR_LEN(blk_sched); for (i = 0; i < n;) { @@ -976,17 +1186,48 @@ void arm_gen_routine(FILE *F, ir_graph *irg, const arm_code_gen_t *cg) { /* set here the link. the emitter expects to find the next block here */ set_irn_link(block, next_bl); - arm_gen_block(block, &emit_env); + arm_gen_block(block, last_block); + last_block = block; } /* emit SymConst values */ - if (emit_env.symbols) - fprintf(F, "\t.align 2\n"); - - for (entry = emit_env.symbols; entry; entry = entry->next) { - fprintf(F, ".L%u:\n", entry->label); - lc_efprintf(arm_get_arg_env(), F, "\t.word\t%C\n", entry->symconst); + if (set_count(sym_or_tv) > 0) { + sym_or_tv_t *entry; + + be_emit_cstring("\t.align 2\n"); + + foreach_set(sym_or_tv, entry) { + be_emit_irprintf(".L%u:\n", entry->label); + + if (entry->is_ident) { + be_emit_cstring("\t.word\t"); + be_emit_ident(entry->u.id); + be_emit_char('\n'); + be_emit_write_line(); + } else { + tarval *tv = entry->u.tv; + int i, size = get_mode_size_bytes(get_tarval_mode(tv)); + unsigned v; + + /* beware: ARM fpa uses big endian format */ + for (i = ((size + 3) & ~3) - 4; i >= 0; i -= 4) { + /* get 32 bits */ + v = get_tarval_sub_bits(tv, i+3); + v = (v << 8) | get_tarval_sub_bits(tv, i+2); + v = (v << 8) | get_tarval_sub_bits(tv, i+1); + v = (v << 8) | get_tarval_sub_bits(tv, i+0); + be_emit_irprintf("\t.word\t%u\n", v); + be_emit_write_line(); + } + } + } + be_emit_char('\n'); + be_emit_write_line(); } + del_set(sym_or_tv); +} - obstack_free(&emit_env.obst, NULL); +void arm_init_emitter(void) +{ + FIRM_DBG_REGISTER(dbg, "firm.be.arm.emit"); }