X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;ds=sidebyside;f=ir%2Fbe%2Farm%2Farm_emitter.c;h=7405e6d7d261ac23cfbc2445dbfb102ab84d9e70;hb=b7936c30acf06e28df82497df4ff2779e6b84b4b;hp=206d249533e8728ba820060304c8dfed9136c1ac;hpb=1660476d1960bb87d086167ce7c859e39a25e100;p=libfirm diff --git a/ir/be/arm/arm_emitter.c b/ir/be/arm/arm_emitter.c index 206d24953..7405e6d7d 100644 --- a/ir/be/arm/arm_emitter.c +++ b/ir/be/arm/arm_emitter.c @@ -1,5 +1,29 @@ -/* arm emitter */ -/* $Id$ */ +/* + * 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 #ifdef HAVE_CONFIG_H #include "config.h" @@ -12,14 +36,23 @@ #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 "../be_dbgout.h" #include "arm_emitter.h" +#include "arm_optimize.h" #include "gen_arm_emitter.h" #include "arm_nodes_attr.h" #include "arm_new_nodes.h" @@ -28,103 +61,16 @@ #include "../benode_t.h" -#define SNPRINTF_BUF_LEN 128 - -static const arch_env_t *arch_env = NULL; - - -/************************************************************* - * _ _ __ _ _ - * (_) | | / _| | | | | - * _ __ _ __ _ _ __ | |_| |_ | |__ ___| |_ __ ___ _ __ - * | '_ \| '__| | '_ \| __| _| | '_ \ / _ \ | '_ \ / _ \ '__| - * | |_) | | | | | | | |_| | | | | | __/ | |_) | __/ | - * | .__/|_| |_|_| |_|\__|_| |_| |_|\___|_| .__/ \___|_| - * | | | | - * |_| |_| - *************************************************************/ - -int is_immediate_node(ir_node *irn) { - if (is_arm_Add_i(irn) || is_arm_Sub_i(irn)) - return 1; - if (is_arm_Shr_i(irn) || is_arm_Shr_i(irn) || is_arm_Shl_i(irn)) - return 1; - if (is_arm_And_i(irn) || is_arm_Or_i(irn) || is_arm_Eor_i(irn)) - return 1; - if (is_arm_Or_Shl_i(irn)) - return 1; - return 0; -} - -/** - * Return a const or symconst as string. - */ -static const char *node_const_to_str(ir_node *n) { - char buffer[SNPRINTF_BUF_LEN]; - - if ( is_immediate_node(n) ) { - long longvalue = get_tarval_long(get_arm_value(n)); - char *str; - assert(longvalue < 0x1000 && "constant doesn't fit in shifter_operand"); - snprintf(buffer, SNPRINTF_BUF_LEN - 1, "#%ld << %ld", longvalue & 0xff, (longvalue >> 8) << 1 ); - str = xmalloc(strlen(buffer) * sizeof(char)); - strcpy(str, buffer); - return str; - } - if ( is_arm_Const(n) || is_arm_Const_Neg(n) ) { - tarval *tv = get_arm_value(n); - if ( mode_is_int(get_tarval_mode(tv)) ) { - long longvalue = get_tarval_long(get_arm_value(n)); - char *str; - assert(longvalue < 0x1000 && "constant doesn't fit in shifter_operand"); - snprintf(buffer, SNPRINTF_BUF_LEN - 1, "#%ld << %ld", longvalue & 0xff, (longvalue >> 8) << 1 ); - str = xmalloc(strlen(buffer) * sizeof(char)); - strcpy(str, buffer); - return str; - } else { - return "found something else in arm_const"; - } - } else if ( is_arm_SymConst(n) ) { - return get_arm_symconst_label(n); - } else { - assert( 0 && "das ist gar keine Konstante"); - return NULL; - } - -} +#define BLOCK_PREFIX ".L" -/** - * Returns node's offset as string. - */ -static char *node_offset_to_str(ir_node *n) { - char buffer[SNPRINTF_BUF_LEN]; - char *result; - 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); - } 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; - } else { - return "node_offset_to_str will fuer diesen Knotentyp noch implementiert werden"; - } - snprintf(buffer, SNPRINTF_BUF_LEN, "%d", offset); - result = xmalloc(sizeof(char)*(strlen(buffer) + 1)); - strcpy(result, buffer); - return result; -} +#define SNPRINTF_BUF_LEN 128 -/* 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; -} +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; /** * Returns the register at in position pos. @@ -141,356 +87,547 @@ static const arch_register_t *get_in_reg(const ir_node *irn, int pos) { reg = arch_get_irn_register(arch_env, op); - /* ONLY TEMPORARY WORK-AROUND */ -// if (!reg) { -// printf("FIXME\n"); -// reg = &arm_general_purpose_regs[REG_MURX]; -// } - assert(reg && "no in register found"); + + /* 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 *irn, int pos) { - ir_node *proj; - const arch_register_t *reg = NULL; - - assert(get_irn_n_edges(irn) > pos && "Invalid OUT position"); +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; +} - /* 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; - } - } - } +/** + * Emit the name of the source register at given input position. + */ +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)); +} - assert(reg && "no out register found"); - return reg; +/** + * Emit the name of the destination register at given output position. + */ +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 the number of the in register at position pos. + * Emit a node's offset. */ -int get_arm_reg_nr(ir_node *irn, int pos, int in_out) { - const arch_register_t *reg; +void arm_emit_offset(const ir_node *node) { + int offset = 0; + ir_opcode opc = get_irn_opcode(node); - if (in_out == 1) { - reg = get_in_reg(irn, pos); - } - else { - reg = get_out_reg(irn, pos); + if (opc == beo_Reload || opc == beo_Spill) { + ir_entity *ent = be_get_frame_entity(node); + offset = get_entity_offset(ent); + } else { + assert(!"unimplemented arm_emit_offset for this node type"); + panic("unimplemented arm_emit_offset for this node type"); } + be_emit_irprintf("%d", offset); +} - return arch_register_get_index(reg); +/** + * Emit the arm fpa instruction suffix depending on the mode. + */ +static void arm_emit_fpa_postfix(const ir_mode *mode) { + int bits = get_mode_size_bits(mode); + char c = 'e'; + + if (bits == 32) + c = 's'; + else if (bits == 64) + c = 'd'; + be_emit_char(c); } /** - * Returns the name of the in register at position pos. + * Emit the instruction suffix depending on the mode. */ -const char *get_arm_reg_name(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_name(reg); + arm_emit_fpa_postfix(mode); } /** - * Get the register name for a node. + * Emit a const or SymConst value. */ -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); +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 { + 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) -{ - 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); + be_emit_irprintf(", %s #%l", arm_shf_mod_name(mod), v); } - else { /* 'O' */ - buf = node_offset_to_str(X); - } - - 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; + /** - * Determines the SSE suffix depending on the mode. + * Returns a unique label. This number will not be used a second time. */ -static int arm_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_appendable_snadd(app, "(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'); +static unsigned get_unique_label(void) { + static unsigned id = 0; + return ++id; } /** - * Return the arm printf arg environment. - * We use the firm environment with some additional handlers. + * Emit a SymConst. */ -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 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(env == NULL) { - /* extend the firm printer */ - env = firm_get_arg_env(); - //lc_arg_new_env(); + /* 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); +} - 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); +/** + * Emit a floating point fpa constant. + */ +static void emit_arm_fpaConst(const ir_node *irn) { + sym_or_tv_t key, *entry; + unsigned label; + ir_mode *mode; + + 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(' '); + + arm_emit_dest_register(irn, 0); + be_emit_irprintf(", .L%u", label); + be_emit_finish_line_gas(irn); } /** - * Formated print of commands and comments. + * Returns the next block in a block schedule. */ -static void arm_fprintf_format(FILE *F, char *cmd_buf, char *cmnt_buf, ir_node *irn) { - lc_efprintf(arm_get_arg_env(), F, "\t%-35s %-60s /* %+F */\n", cmd_buf, cmnt_buf, irn); +static ir_node *sched_next_block(const ir_node *block) { + return get_irn_link(block); } -/* - * Add a number to a prefix. This number will not be used a second time. +/** + * Returns the target block for a control flow node. */ -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; +static ir_node *get_cfop_target_block(const ir_node *irn) { + return get_irn_link(irn); } +/** + * Emits a block label for the given block. + */ +static void arm_emit_block_name(const ir_node *block) { + if (has_Block_label(block)) { + be_emit_string(be_gas_block_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 target label for a control flow node. + * Emit 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); +static void arm_emit_cfop_target(const ir_node *irn) { + ir_node *block = get_cfop_target_block(irn); - snprintf(buf, SNPRINTF_BUF_LEN, "BLOCK_%ld", get_irn_node_nr(bl)); - return buf; + arm_emit_block_name(block); } -/************************************************************************/ -/* emit_arm */ -/************************************************************************/ - -static void emit_arm_SymConst(ir_node *irn, void *env) { - arm_emit_env_t *emit_env = env; - FILE *out = emit_env->out; - char buffer1[SNPRINTF_BUF_LEN]; - char *skip_label = get_unique_label(buffer1, SNPRINTF_BUF_LEN, ".L"); - char buffer2[SNPRINTF_BUF_LEN]; - char *indi_label = get_unique_label(buffer2, SNPRINTF_BUF_LEN, ".L"); - fprintf( out, "\tB %s\t\t\t\t\t/* start of indirect SymConst */\n", skip_label ); - fprintf( out, "\t.align 2\n" ); - fprintf( out, "%s:\n", indi_label ); - lc_efprintf(arm_get_arg_env(), out, "\t.word\t%C\n", irn); - fprintf( out, "\t.align 2\n" ); - fprintf( out, "%s:\n", skip_label ); - lc_efprintf(arm_get_arg_env(), out, "\tLDR %1D, %s\t\t\t/* end of indirect SymConst */\n", irn, indi_label); -} - -static void emit_arm_CondJmp(ir_node *irn, void *env) { - arm_emit_env_t *emit_env = env; - FILE *out = emit_env->out; +/** + * Emit a Compare with conditional branch. + */ +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]; - - - 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; + 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); + 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) { - fprintf(out, "\tB BLOCK_%ld\t\t\t/* false case */\n", get_irn_node_nr(false_block)); + /* always false: should not happen */ + be_emit_cstring("\tb "); + arm_emit_cfop_target(proj_false); + be_emit_finish_line_gas(proj_false); } else if (proj_num == pn_Cmp_True) { - fprintf(out, "\tB BLOCK_%ld\t\t\t/* true case */\n", get_irn_node_nr(true_block)); + /* always true: should not happen */ + be_emit_cstring("\tb "); + arm_emit_cfop_target(proj_true); + be_emit_finish_line_gas(proj_true); } else { 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); - - 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("\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, "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); - - 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); + be_emit_cstring("\tfmstat"); + be_emit_pad_comment(); + be_emit_cstring("/* FCSPR -> CPSR */"); + be_emit_finish_line_gas(NULL); } else { - switch(proj_num) { - case pn_Cmp_Eq: suffix = "EQ"; break; - case pn_Cmp_Lt: suffix = "LT"; break; - case pn_Cmp_Le: suffix = "LE"; break; - case pn_Cmp_Gt: suffix = "GT"; break; - 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"); - } + 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; - 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); + proj_true = proj_false; + proj_false = t; + proj_num = get_negated_pnc(proj_num, mode_Iu); + } + switch (proj_num) { + case pn_Cmp_Eq: suffix = "eq"; break; + case pn_Cmp_Lt: suffix = "lt"; break; + case pn_Cmp_Le: suffix = "le"; break; + case pn_Cmp_Gt: suffix = "gt"; break; + case pn_Cmp_Ge: suffix = "ge"; break; + case pn_Cmp_Lg: suffix = "ne"; break; + case pn_Cmp_Leg: suffix = "al"; break; + default: assert(!"Cmp unsupported"); suffix = "al"; + } + 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); + } - 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); + /* 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(), 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); + 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); } } } -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*)); @@ -498,59 +635,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); - lc_efprintf(arm_get_arg_env(), out, "\t%-35s %-60s /* %+F */\n", 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); - lc_efprintf(arm_get_arg_env(), out, "\t%-35s %-60s /* %+F */\n", 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); - lc_efprintf(arm_get_arg_env(), out, "\t%-35s %-60s /* %+F */\n", 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); - lc_efprintf(arm_get_arg_env(), out, "\t%-35s %-60s /* %+F */\n", 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); - lc_efprintf(arm_get_arg_env(), out, "\t%-35s %-60s /* %+F */\n", 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 *target_entity = be_Call_get_entity(irn); - const char *target_name = get_entity_name(target_entity); - fprintf(out, "\tBL %s\t\t\t\t/* Call */\n", target_name); -} - -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); - lc_efprintf(arm_get_arg_env(), F, "\t%-35s %-60s /* %+F */\n", cmd_buf, cmnt_buf, irn); +static void emit_be_Call(const ir_node *irn) { + ir_entity *ent = be_Call_get_entity(irn); + + be_emit_cstring("\tbl "); + if (ent) { + set_entity_backend_marked(ent, 1); + be_emit_ident(get_entity_ld_ident(ent)); } else { - char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; - lc_esnprintf(arm_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, ""); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* omitted IncSP(%O) */", irn); - lc_efprintf(arm_get_arg_env(), F, "\t%-35s %-60s /* %+F */\n", cmd_buf, cmnt_buf, irn); + arm_emit_source_register(irn, be_pos_Call_ptr); } + 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); -// } +/** Emit an IncSP node */ +static void emit_be_IncSP(const ir_node *irn) { + int offs = -be_get_IncSP_offset(irn); + + if (offs != 0) { + if (offs < 0) { + be_emit_cstring("\tsub "); + offs = -offs; + } else { + be_emit_cstring("\tadd "); + } + arm_emit_dest_register(irn, 0); + be_emit_cstring(", "); + arm_emit_source_register(irn, 0); + be_emit_irprintf(", #0x%X", offs); + } else { + /* omitted IncSP(0) */ + return; + } + be_emit_finish_line_gas(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); - assert( (mode != mode_E) && "IEEE Extended FP not supported"); if (get_in_reg(irn, 0) == get_out_reg(irn, 0)) { - char cmd_buf[256], cmnt_buf[256]; - lc_esnprintf(arm_get_arg_env(), cmd_buf, 256, ""); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, 256, "/* omitted Copy: %1S -> %1D */", irn, irn); - lc_efprintf(arm_get_arg_env(), F, "\t%-35s %-60s /* %+F */\n", cmd_buf, cmnt_buf, irn); + /* omitted Copy */ return; } - if (mode == mode_F) { - char cmd_buf[256], cmnt_buf[256]; - lc_esnprintf(arm_get_arg_env(), cmd_buf, 256, "FCPYS %1D, %1S", irn, irn); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, 256, "/* Copy: %1S -> %1D */", irn, irn); - lc_efprintf(arm_get_arg_env(), F, "\t%-35s %-60s /* %+F */\n", cmd_buf, cmnt_buf, irn); - } else if (mode == mode_D) { - char cmd_buf[256], cmnt_buf[256]; - lc_esnprintf(arm_get_arg_env(), cmd_buf, 256, "FCPYD %1D, %1S", irn, irn); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, 256, "/* Copy: %1S -> %1D */", irn, irn); - lc_efprintf(arm_get_arg_env(), F, "\t%-35s %-60s /* %+F */\n", cmd_buf, cmnt_buf, irn); - } else if (mode_is_numP(mode)) { - char cmd_buf[256], cmnt_buf[256]; - lc_esnprintf(arm_get_arg_env(), cmd_buf, 256, "MOV %1D, %1S", irn, irn); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, 256, "/* Copy: %1S -> %1D */", irn, irn); - lc_efprintf(arm_get_arg_env(), F, "\t%-35s %-60s /* %+F */\n", cmd_buf, cmnt_buf, irn); + if (mode_is_float(mode)) { + 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_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); } -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); - assert( (mode != mode_E) && "IEEE Extended FP not supported"); - if (mode_is_dataM(mode)) { - char cmd_buf[256], cmnt_buf[256]; - lc_esnprintf(arm_get_arg_env(), cmd_buf, 256, "STR %2S, [%1S, #%O]", irn, irn, irn ); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, 256, "/* Spill(%2S) -> (%1S) */", irn, irn); - lc_efprintf(arm_get_arg_env(), F, "\t%-35s %-60s /* %+F */\n", cmd_buf, cmnt_buf, irn); +/** + * Emit code for a Spill. + */ +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(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)) { + 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); } -static void emit_be_Reload(const ir_node* irn, arm_emit_env_t *emit_env) { - FILE *F = emit_env->out; +/** + * Emit code for a Reload. + */ +static void emit_be_Reload(const ir_node *irn) { ir_mode *mode = get_irn_mode(irn); - assert( (mode != mode_E) && "IEEE Extended FP not supported"); - if (mode_is_dataM(mode)) { - char cmd_buf[256], cmnt_buf[256]; - lc_esnprintf(arm_get_arg_env(), cmd_buf, 256, "LDR %1D, [%1S, #%O]", irn, irn, irn ); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, 256, "/* Reload(%1S) -> (%1D) */", irn, irn); - lc_efprintf(arm_get_arg_env(), F, "\t%-35s %-60s /* %+F */\n", cmd_buf, cmnt_buf, irn); + + if (mode_is_float(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)) { + 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); - assert( (mode != mode_E) && "IEEE Extended FP not supported"); - lc_efprintf(arm_get_arg_env(), F, "\tEOR %1S, %1S, %2S\t\t\t/* begin Perm(%1S, %2S) */\n", irn, irn, irn, irn, irn); - lc_efprintf(arm_get_arg_env(), F, "\tEOR %2S, %1S, %2S\n", irn, irn, irn); - lc_efprintf(arm_get_arg_env(), F, "\tEOR %1S, %1S, %2S\t\t\t/* end Perm(%1S, %2S) */\n", irn, irn, irn, irn, 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); - char cmd_buf[256], cmnt_buf[256]; - assert( (mode != mode_E) && "IEEE Extended FP not supported"); - - lc_esnprintf(arm_get_arg_env(), cmd_buf, 256, "LDR %1D, [%1S, #%O]", irn, irn, irn ); - lc_esnprintf(arm_get_arg_env(), cmnt_buf, 256, "/* StackParam: (%1S + %O) -> %1D */",irn , irn, irn, get_irn_n(irn, 0)); - lc_efprintf(arm_get_arg_env(), F, "\t%-35s %-60s /* %+F */\n", 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) { - arm_emit_env_t *emit_env = env; - FILE *out = emit_env->out; - const ir_edge_t *edge = get_irn_out_edge_first(irn); - ir_node *target_block = get_edge_src_irn(edge); - fprintf(out, "\tB BLOCK_%ld\t\t\t/* unconditional Jump */\n", get_irn_node_nr(target_block)); -} +static void emit_Jmp(const ir_node *node) { + ir_node *block, *next_block; -static void emit_Proj(ir_node *irn, void *env) { + /* for now, the code works for scheduled and non-schedules blocks */ + block = get_nodes_block(node); + /* 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_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. */ +} /*********************************************************************************** * _ __ _ @@ -700,15 +909,33 @@ static void emit_Proj(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 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(); @@ -717,99 +944,163 @@ static void arm_register_emitters(void) { arm_register_spec_emitters(); /* other emitter functions */ - ARM_EMIT(CondJmp); -// ARM_EMIT(SwitchJmp); - ARM_EMIT(CopyB); + 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); - + EMIT(Jmp); /* noisy stuff */ #ifdef SILENCER - EMIT(Proj); + 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 #undef BE_EMIT #undef EMIT +#undef SILENCE } /** * Emits code for a node. */ -static void arm_emit_node(const ir_node *irn, void *env) { - arm_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)); +static void arm_emit_node(const ir_node *irn) { + ir_op *op = get_irn_op(irn); if (op->ops.generic) { - void (*emit)(const ir_node *, void *) = (void (*)(const ir_node *, void *))op->ops.generic; - (*emit)(irn, env); + emit_func *emit = (emit_func *)op->ops.generic; + be_dbg_set_dbg_info(get_irn_dbg_info(irn)); + (*emit)(irn); + } else { + be_emit_cstring("\t/* TODO */"); + be_emit_finish_line_gas(irn); } - else { - ir_fprintf(F, "\t\t\t\t\t/* %+F */\n", 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; - if (! is_Block(block)) - return; - - fprintf(((arm_emit_env_t *)env)->out, "BLOCK_%ld:\n", get_irn_node_nr(block)); + arm_emit_block_header(block, prev_block); + be_dbg_set_dbg_info(get_irn_dbg_info(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) { - const char *irg_name = get_entity_name(get_irg_entity(irg)); - fprintf(F, "\t.text\n"); - fprintf(F, "\t.align 2\n"); - fprintf(F, "\t.global %s\n", irg_name); - fprintf(F, "%s:\n", irg_name); +void arm_func_prolog(ir_graph *irg) { + ir_entity *ent = get_irg_entity(irg); + const char *irg_name = get_entity_ld_name(ent); + + 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) + 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); @@ -817,25 +1108,92 @@ void arm_gen_labels(ir_node *block, void *env) { } } - /** - * Main driver + * Compare two entries of the symbol or tarval set. */ -void arm_gen_routine(FILE *F, ir_graph *irg, const arm_code_gen_t *cg) { - arm_emit_env_t emit_env; +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; +} - emit_env.out = F; - emit_env.arch_env = cg->arch_env; - emit_env.cg = cg; - FIRM_DBG_REGISTER(emit_env.mod, "firm.be.arm.emit"); +/** + * Main driver. Emits the code for one routine. + */ +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; - /* 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; + arch_env = cg->arch_env; + sym_or_tv = new_set(cmp_sym_or_tv, 8); arm_register_emitters(); - arm_emit_start(F, irg); - irg_block_walk_graph(irg, arm_gen_labels, NULL, &emit_env); - irg_walk_blkwise_graph(irg, NULL, arm_gen_block, &emit_env); - arm_emit_end(F, irg); + /* create the block schedule. For now, we don't need it earlier. */ + blk_sched = be_create_block_schedule(cg->irg, cg->birg->exec_freq); + + arm_func_prolog(irg); + irg_block_walk_graph(irg, arm_gen_labels, NULL, NULL); + + n = ARR_LEN(blk_sched); + for (i = 0; i < n;) { + ir_node *block, *next_bl; + + block = blk_sched[i]; + ++i; + next_bl = i < n ? blk_sched[i] : NULL; + + /* set here the link. the emitter expects to find the next block here */ + set_irn_link(block, next_bl); + arm_gen_block(block, last_block); + last_block = block; + } + + /* emit SymConst values */ + 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); +} + +void arm_init_emitter(void) +{ + FIRM_DBG_REGISTER(dbg, "firm.be.arm.emit"); }