X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=ir%2Fbe%2Farm%2Farm_emitter.c;h=de458a0f3832c945d6e6f67881167f4db0c3ecb1;hb=18011a85e62d81f6d8d08957192fa34d03b747c5;hp=a4682a4870682c522e18a95abe69dd48e6c176bb;hpb=1f335ea95cd82d50c613dbee022d5555be668f51;p=libfirm diff --git a/ir/be/arm/arm_emitter.c b/ir/be/arm/arm_emitter.c index a4682a487..de458a0f3 100644 --- a/ir/be/arm/arm_emitter.c +++ b/ir/be/arm/arm_emitter.c @@ -1,132 +1,75 @@ -#define SILENCER -/* 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. + */ -#ifdef HAVE_CONFIG_H +/** + * @file + * @brief arm emitter + * @author Oliver Richter, Tobias Gneist, Michael Beck + * @version $Id$ + */ #include "config.h" -#endif #include +#include #include "xmalloc.h" #include "tv.h" #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.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" #include "arm_map_regs.h" #include "gen_arm_regalloc_if.h" -#include "../benode_t.h" +#include "../benode.h" #define SNPRINTF_BUF_LEN 128 -static const arch_env_t *arch_env = NULL; - -/** - * Switch to a new section - */ -void arm_switch_section(FILE *f, sections sec) { - static sections curr_sec = NO_SECTION; - - if (curr_sec == sec) - return; - - curr_sec = sec; - switch (sec) { - - case NO_SECTION: - break; - - case SECTION_TEXT: - fprintf(f, "\t.text\n"); - break; - - case SECTION_DATA: - fprintf(f, "\t.data\n"); - break; - - default: - assert(0); - } -} - -/************************************************************* - * _ _ __ _ _ - * (_) | | / _| | | | | - * _ __ _ __ _ _ __ | |_| |_ | |__ ___| |_ __ ___ _ __ - * | '_ \| '__| | '_ \| __| _| | '_ \ / _ \ | '_ \ / _ \ '__| - * | |_) | | | | | | | |_| | | | | | __/ | |_) | __/ | - * | .__/|_| |_|_| |_|\__|_| |_| |_|\___|_| .__/ \___|_| - * | | | | - * |_| |_| - *************************************************************/ - -/** - * Returns non-zero if a mode has a Immediate attribute. - */ -int is_immediate_node(ir_node *irn) { - arm_attr_t *attr = get_arm_attr(irn); - return ARM_GET_SHF_MOD(attr) == ARM_SHF_IMM; -} - -/** - * Return a const or SymConst as string. - */ -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; -} - -/** - * Returns node's offset as string. - */ -static const char *node_offset_to_str(ir_node *n, char *buf, int buflen) { - int offset = 0; - ir_op *irn_op = get_irn_op(n); - - if (irn_op == op_be_StackParam) { - ir_entity *ent = be_get_frame_entity(n); - offset = get_entity_offset(ent); - } else if (irn_op == op_be_Reload || irn_op == op_be_Spill) { - ir_entity *ent = be_get_frame_entity(n); - offset = get_entity_offset(ent); - } else if (irn_op == op_be_IncSP) { - offset = - be_get_IncSP_offset(n); - } else { - return "node_offset_to_str will fuer diesen Knotentyp noch implementiert werden"; - } - snprintf(buf, buflen, "%d", offset); - return buf; -} - -/* 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 arm_code_gen_t *cg; +static set *sym_or_tv; /** * Returns the register at in position pos. */ -static const arch_register_t *get_in_reg(const ir_node *irn, int pos) { +static const arch_register_t *get_in_reg(const ir_node *irn, int pos) +{ ir_node *op; const arch_register_t *reg = NULL; @@ -136,802 +79,948 @@ static const arch_register_t *get_in_reg(const ir_node *irn, int pos) { in register we need. */ op = get_irn_n(irn, pos); - reg = arch_get_irn_register(arch_env, op); + reg = arch_get_irn_register(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 */ + /* in case of a joker register: just return a valid register */ + if (arch_register_type_is(reg, joker)) { + const arch_register_req_t *req = arch_get_register_req(irn, pos); - 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; - } + 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); } } - - assert(reg && "no out register found"); return reg; } -/** - * Returns the number of the in register at position pos. - */ -int get_arm_reg_nr(ir_node *irn, int pos, int in_out) { - const arch_register_t *reg; - - if (in_out == 1) { - reg = get_in_reg(irn, pos); - } - else { - reg = get_out_reg(irn, pos); - } - - return arch_register_get_index(reg); -} /** - * Returns the name of the in register at position pos. + * Returns the register at out position pos. */ -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); - } - else { - reg = get_out_reg(irn, 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(node); + } else if (is_arm_irn(node)) { + reg = arch_irn_get_register(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(proj); + break; + } + } + } - return arch_register_get_name(reg); + assert(reg && "no out register found"); + return 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) +void arm_emit_source_register(const ir_node *node, int pos) { - const char *buf; - ir_node *X = arg->v_ptr; - int nr = occ->width - 1; + const arch_register_t *reg = get_in_reg(node, pos); + be_emit_string(arch_register_get_name(reg)); +} - if (!X) - return lc_appendable_snadd(app, "(null)", 6); +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)); +} - 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_offset(const ir_node *node) +{ + const arm_load_store_attr_t *attr = get_arm_load_store_attr_const(node); + assert(attr->base.is_load_store); - lc_appendable_chadd(app, '%'); - return lc_appendable_snadd(app, buf, strlen(buf)); + be_emit_irprintf("0x%X", attr->offset); } /** - * Returns the tarval or offset of an arm node as a string. + * Emit the arm fpa instruction suffix depending on the mode. */ -static int arm_const_to_str(lc_appendable_t *app, - const lc_arg_occ_t *occ, const lc_arg_value_t *arg) +static void arm_emit_fpa_postfix(const ir_mode *mode) { - char buffer[SNPRINTF_BUF_LEN]; - const char *buf; - ir_node *X = arg->v_ptr; + int bits = get_mode_size_bits(mode); + char c = 'e'; - if (!X) - return lc_appendable_snadd(app, "(null)", 6); + if (bits == 32) + c = 's'; + else if (bits == 64) + c = 'd'; + be_emit_char(c); +} - if (occ->conversion == 'C') { - buf = node_const_to_str(X, buffer, sizeof(buffer)); - } - else { /* 'O' */ - buf = node_offset_to_str(X, buffer, sizeof(buffer)); - } +void arm_emit_float_load_store_mode(const ir_node *node) +{ + const arm_load_store_attr_t *attr = get_arm_load_store_attr_const(node); + arm_emit_fpa_postfix(attr->load_store_mode); +} - return lc_appendable_snadd(app, buf, strlen(buf)); +void arm_emit_float_arithmetic_mode(const ir_node *node) +{ + const arm_farith_attr_t *attr = get_arm_farith_attr_const(node); + arm_emit_fpa_postfix(attr->mode); } -/** - * Returns the tarval or offset of an arm node as a string. - */ -static int arm_shift_str(lc_appendable_t *app, - const lc_arg_occ_t *occ, const lc_arg_value_t *arg) +void arm_emit_symconst(const ir_node *node) { - char buffer[SNPRINTF_BUF_LEN]; - ir_node *irn = arg->v_ptr; - arm_shift_modifier mod; + const arm_SymConst_attr_t *symconst = get_arm_SymConst_attr_const(node); + ir_entity *entity = symconst->entity; - if (!irn) - return lc_appendable_snadd(app, "(null)", 6); + be_gas_emit_entity(entity); - mod = get_arm_shift_modifier(irn); - if (ARM_HAS_SHIFT(mod)) { - long v = get_tarval_long(get_arm_value(irn)); + /* TODO do something with offset */ +} - snprintf(buffer, sizeof(buffer), ", %s #%ld", arm_shf_mod_name(mod), v); - return lc_appendable_snadd(app, buffer, strlen(buffer)); +void arm_emit_load_mode(const ir_node *node) +{ + const arm_load_store_attr_t *attr = get_arm_load_store_attr_const(node); + ir_mode *mode = attr->load_store_mode; + int bits = get_mode_size_bits(mode); + bool is_signed = mode_is_signed(mode); + if (bits == 16) { + be_emit_string(is_signed ? "sh" : "h"); + } else if (bits == 8) { + be_emit_string(is_signed ? "sb" : "b"); + } else { + assert(bits == 32); } - return 0; } -/** - * Determines the instruction suffix depending on the mode. - */ -static int arm_get_mode_suffix(lc_appendable_t *app, - const lc_arg_occ_t *occ, const lc_arg_value_t *arg) +void arm_emit_store_mode(const ir_node *node) { - 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); + const arm_load_store_attr_t *attr = get_arm_load_store_attr_const(node); + ir_mode *mode = attr->load_store_mode; + int bits = get_mode_size_bits(mode); + if (bits == 16) { + be_emit_cstring("h"); + } else if (bits == 8) { + be_emit_cstring("b"); + } else { + assert(bits == 32); + } +} - 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'); +static void emit_shf_mod_name(arm_shift_modifier_t mod) +{ + switch (mod) { + case ARM_SHF_ASR_REG: + case ARM_SHF_ASR_IMM: + be_emit_cstring("asr"); + return; + case ARM_SHF_LSL_REG: + case ARM_SHF_LSL_IMM: + be_emit_cstring("lsl"); + return; + case ARM_SHF_LSR_REG: + case ARM_SHF_LSR_IMM: + be_emit_cstring("lsr"); + return; + case ARM_SHF_ROR_REG: + case ARM_SHF_ROR_IMM: + be_emit_cstring("ror"); + return; + default: + break; + } + panic("can't emit this shf_mod_name %d", (int) mod); } -/** - * Return the arm printf arg environment. - * We use the firm environment with some additional handlers. - */ -const lc_arg_env_t *arm_get_arg_env(void) { - static lc_arg_env_t *env = NULL; +void arm_emit_shifter_operand(const ir_node *node) +{ + const arm_shifter_operand_t *attr = get_irn_generic_attr_const(node); + + switch (attr->shift_modifier) { + case ARM_SHF_REG: + arm_emit_source_register(node, get_irn_arity(node) - 1); + return; + case ARM_SHF_IMM: { + unsigned val = attr->immediate_value; + val = (val >> attr->shift_immediate) + | (val << (32-attr->shift_immediate)); + val &= 0xFFFFFFFF; + be_emit_irprintf("#0x%X", val); + return; + } + case ARM_SHF_ASR_IMM: + case ARM_SHF_LSL_IMM: + case ARM_SHF_LSR_IMM: + case ARM_SHF_ROR_IMM: + arm_emit_source_register(node, get_irn_arity(node) - 1); + be_emit_cstring(", "); + emit_shf_mod_name(attr->shift_modifier); + be_emit_irprintf(" #0x%X", attr->shift_immediate); + return; - 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 }; + case ARM_SHF_ASR_REG: + case ARM_SHF_LSL_REG: + case ARM_SHF_LSR_REG: + case ARM_SHF_ROR_REG: + arm_emit_source_register(node, get_irn_arity(node) - 2); + be_emit_cstring(", "); + emit_shf_mod_name(attr->shift_modifier); + be_emit_cstring(" "); + arm_emit_source_register(node, get_irn_arity(node) - 1); + return; - if (env == NULL) { - /* extend the firm printer */ - env = firm_get_arg_env(); - //lc_arg_new_env(); + case ARM_SHF_RRX: + arm_emit_source_register(node, get_irn_arity(node) - 1); + panic("RRX shifter emitter TODO"); - 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); + case ARM_SHF_INVALID: + break; } - - return env; + panic("Invalid shift_modifier while emitting %+F", node); } -/** - * 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); -} +/** An entry in the sym_or_tv set. */ +typedef struct sym_or_tv_t { + union { + ir_entity *entity; /**< An entity. */ + tarval *tv; /**< A tarval. */ + const void *generic; /**< For generic compare. */ + } u; + unsigned label; /**< the associated label. */ + bool is_entity; /**< true if an entity is stored. */ +} sym_or_tv_t; /** * Returns a unique label. This number will not be used a second time. */ -static unsigned get_unique_label(void) { +static unsigned get_unique_label(void) +{ static unsigned id = 0; return ++id; } +static void emit_constant_name(const sym_or_tv_t *entry) +{ + be_emit_irprintf("%sC%u", be_gas_get_private_prefix(), entry->label); +} /** - * Returns the target label for a control flow node. + * Emit a SymConst. */ -static char *get_cfop_target(const ir_node *irn, char *buf) { - ir_node *bl = get_irn_link(irn); +static void emit_arm_SymConst(const ir_node *irn) +{ + const arm_SymConst_attr_t *attr = get_arm_SymConst_attr_const(irn); + sym_or_tv_t key, *entry; + unsigned label; + + key.u.entity = attr->entity; + key.is_entity = true; + 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; + + /* load the symbol indirect */ + be_emit_cstring("\tldr "); + arm_emit_dest_register(irn, 0); + be_emit_cstring(", "); + emit_constant_name(entry); + be_emit_finish_line_gas(irn); +} - snprintf(buf, SNPRINTF_BUF_LEN, "BLOCK_%ld", get_irn_node_nr(bl)); - return buf; +static void emit_arm_FrameAddr(const ir_node *irn) +{ + const arm_SymConst_attr_t *attr = get_irn_generic_attr_const(irn); + + be_emit_cstring("\tadd "); + arm_emit_dest_register(irn, 0); + be_emit_cstring(", "); + arm_emit_source_register(irn, 0); + be_emit_cstring(", "); + be_emit_irprintf("#0x%X", attr->fp_offset); + be_emit_finish_line_gas(irn); } /** - * Emit a SymConst + * Emit a floating point fpa constant. */ -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; +static void emit_arm_fConst(const ir_node *irn) +{ + sym_or_tv_t key, *entry; + unsigned label; + ir_mode *mode; - lc_esnprintf(arg_env, cmd_buf, 256, "ldr %1D, .L%u", irn, entry->label); - arm_fprintf_format(F, cmd_buf, "/* indirect SymConst */", irn); + key.u.tv = get_fConst_value(irn); + key.is_entity = false; + 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; + + /* 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_cstring(", "); + emit_constant_name(entry); + be_emit_finish_line_gas(irn); } /** * Returns the next block in a block schedule. */ -static ir_node *sched_next_block(ir_node *block) { +static ir_node *sched_next_block(const ir_node *block) +{ return get_irn_link(block); } /** - * Emit a conditional jump. + * Returns the target block for a control flow node. + */ +static ir_node *get_cfop_target_block(const ir_node *irn) +{ + return get_irn_link(irn); +} + +/** + * Emit the target label for a control flow node. + */ +static void arm_emit_cfop_target(const ir_node *irn) +{ + ir_node *block = get_cfop_target_block(irn); + + be_gas_emit_block_name(block); +} + +/** + * 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_B(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; + pn_Cmp pnc = get_arm_CondJmp_pnc(irn); + const arm_cmp_attr_t *cmp_attr = get_irn_generic_attr_const(op1); + bool is_signed = !cmp_attr->is_unsigned; + assert(is_arm_Cmp(op1) || is_arm_Tst(op1)); 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; } } - if (proj_num == pn_Cmp_False) { - /* always false: should not happen */ - fprintf(out, "\tb BLOCK_%ld\t\t\t/* false case */\n", get_irn_node_nr(false_block)); - } else if (proj_num == pn_Cmp_True) { - /* always true: should not happen */ - fprintf(out, "\tb BLOCK_%ld\t\t\t/* true case */\n", get_irn_node_nr(true_block)); - } else { - ir_node *block = get_nodes_block(irn); + if (cmp_attr->ins_permuted) { + pnc = get_mirrored_pnc(pnc); + } - if (mode_is_float(opmode)) { - suffix = "ICHWILLIMPLEMENTIERTWERDEN"; + /* for now, the code works for scheduled and non-schedules blocks */ + block = get_nodes_block(irn); - 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); + /* we have a block schedule */ + next_block = sched_next_block(block); - 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); - } else { - if (true_block == sched_next_block(block)) { - /* negate it */ - proj_num = get_negated_pnc(proj_num, opmode); - } - 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"); suffix = "al"; - } + assert(pnc != pn_Cmp_False); + assert(pnc != pn_Cmp_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); - } + 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; - 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); + proj_true = proj_false; + proj_false = t; + pnc = get_negated_pnc(pnc, mode_Iu); + } - 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%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); - } - } + switch (pnc) { + case pn_Cmp_Eq: suffix = "eq"; break; + case pn_Cmp_Lt: suffix = is_signed ? "lt" : "lo"; break; + case pn_Cmp_Le: suffix = is_signed ? "le" : "ls"; break; + case pn_Cmp_Gt: suffix = is_signed ? "gt" : "hi"; break; + case pn_Cmp_Ge: suffix = is_signed ? "ge" : "hs"; break; + case pn_Cmp_Lg: suffix = "ne"; break; + case pn_Cmp_Leg: suffix = "al"; break; + default: panic("Cmp has unsupported pnc"); + } + + /* emit the true proj */ + be_emit_irprintf("\tb%s ", suffix); + arm_emit_cfop_target(proj_true); + be_emit_finish_line_gas(proj_true); + + 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("\tb "); + 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(); +/** 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) +{ + const arm_CopyB_attr_t *attr = get_irn_generic_attr_const(irn); + unsigned size = attr->size; + + 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) { + fprintf(stderr, "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*)); + projs = XMALLOCNZ(ir_node*, n_projs); foreach_out_edge(irn, edge) { 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; - ir_entity *ent = be_Call_get_entity(irn); - char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; - - 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); -} - /** Emit an IncSP node */ -static void emit_be_IncSP(const ir_node *irn, arm_emit_env_t *emit_env) { - FILE *F = emit_env->out; - int offs = be_get_IncSP_offset(irn); +static void emit_be_IncSP(const ir_node *irn) +{ + int offs = -be_get_IncSP_offset(irn); if (offs != 0) { - 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); + 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 { - 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); + /* omitted IncSP(0) */ + return; } + 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); + /* omitted Copy */ 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 { - assert(0 && "move not supported for this mode"); + if (USE_FPA(cg->isa)) { + be_emit_cstring("\tmvf"); + 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 { + 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]; - - 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"); - } - } 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); - } else { - assert(0 && "spill not supported for this mode"); - } +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 code for a Reload. - */ -static void emit_be_Reload(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_MemPerm(const ir_node *node) +{ + int i; + int memperm_arity; + int sp_change = 0; + + /* TODO: this implementation is slower than necessary. + The longterm goal is however to avoid the memperm node completely */ + + memperm_arity = be_get_MemPerm_entity_arity(node); + if (memperm_arity > 12) + panic("memperm with more than 12 inputs not supported yet"); + + for (i = 0; i < memperm_arity; ++i) { + int offset; + ir_entity *entity = be_get_MemPerm_in_entity(node, i); + + /* spill register */ + be_emit_irprintf("\tstr r%d, [sp, #-4]!", i); + be_emit_finish_line_gas(node); + sp_change += 4; + /* load from entity */ + offset = get_entity_offset(entity) + sp_change; + be_emit_irprintf("\tldr r%d, [sp, #%d]", i, offset); + be_emit_finish_line_gas(node); + } - 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"); - } - } 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); - } else { - assert(0 && "reload not supported for this mode"); + for (i = memperm_arity-1; i >= 0; --i) { + int offset; + ir_entity *entity = be_get_MemPerm_out_entity(node, i); + + /* store to new entity */ + offset = get_entity_offset(entity) + sp_change; + be_emit_irprintf("\tstr r%d, [sp, #%d]", i, offset); + be_emit_finish_line_gas(node); + /* restore register */ + be_emit_irprintf("\tldr r%d, [sp], #4", i); + sp_change -= 4; + be_emit_finish_line_gas(node); } + assert(sp_change == 0); } -static void emit_be_Perm(const ir_node* irn, arm_emit_env_t *emit_env) { - FILE *F = emit_env->out; - 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_Return(const ir_node *node) +{ + be_emit_cstring("\tmov pc, lr"); + be_emit_finish_line_gas(node); +} - 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); +static void emit_arm_Jmp(const ir_node *node) +{ + ir_node *block, *next_block; - 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); -} + /* for now, the code works for scheduled and non-schedules blocks */ + block = get_nodes_block(node); -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"); - } + /* 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 { - 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); + be_emit_cstring("\t/* fallthrough to "); + arm_emit_cfop_target(node); + be_emit_cstring(" */"); } + be_emit_finish_line_gas(node); } -/************************************************************************/ -/* 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(); - - lc_esnprintf(arg_env, cmd_buf, 256, "stfd %1S, [sp, #-8]! ", n); - arm_fprintf_format(F, cmd_buf, "/* Push fp to stack */", n); - - lc_esnprintf(arg_env, cmd_buf, 256, "ldmfd sp!, {%2D, %1D} ", n, n); - arm_fprintf_format(F, cmd_buf, "/* Pop destination */", n); +static void emit_nothing(const ir_node *irn) +{ + (void) irn; } -static void emit_silence(ir_node *irn, void *env) { +/** + * 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; } - -/*********************************************************************************** - * _ __ _ - * (_) / _| | | - * _ __ ___ __ _ _ _ __ | |_ _ __ __ _ _ __ ___ _____ _____ _ __| | __ - * | '_ ` _ \ / _` | | '_ \ | _| '__/ _` | '_ ` _ \ / _ \ \ /\ / / _ \| '__| |/ / - * | | | | | | (_| | | | | | | | | | | (_| | | | | | | __/\ V V / (_) | | | < - * |_| |_| |_|\__,_|_|_| |_| |_| |_| \__,_|_| |_| |_|\___| \_/\_/ \___/|_| |_|\_\ - * - ***********************************************************************************/ - /** * 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 - +static void arm_register_emitters(void) +{ /* first clear the generic function pointer for all ops */ clear_irp_opcodes_generic_func(); /* register all emitter functions defined in spec */ arm_register_spec_emitters(); - /* other emitter functions */ - ARM_EMIT(CondJmp); - ARM_EMIT(CopyB); -// ARM_EMIT(CopyB_i); -// ARM_EMIT(Const); - ARM_EMIT(SymConst); - ARM_EMIT(SwitchJmp); - ARM_EMIT(fpaDbl2GP); - - /* 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(Proj); - SILENCE(Phi); - SILENCE(be_Keep); - SILENCE(be_CopyKeep); -#endif - -#undef ARM_EMIT -#undef BE_EMIT -#undef EMIT -#undef SILENCE -} - -typedef void (node_emitter)(const ir_node *, void *); + /* custom emitter */ + set_emitter(op_arm_B, emit_arm_B); + set_emitter(op_arm_CopyB, emit_arm_CopyB); + set_emitter(op_arm_fConst, emit_arm_fConst); + set_emitter(op_arm_FrameAddr, emit_arm_FrameAddr); + set_emitter(op_arm_Jmp, emit_arm_Jmp); + set_emitter(op_arm_SwitchJmp, emit_arm_SwitchJmp); + set_emitter(op_arm_SymConst, emit_arm_SymConst); + set_emitter(op_be_Copy, emit_be_Copy); + set_emitter(op_be_CopyKeep, emit_be_Copy); + set_emitter(op_be_IncSP, emit_be_IncSP); + set_emitter(op_be_MemPerm, emit_be_MemPerm); + set_emitter(op_be_Perm, emit_be_Perm); + set_emitter(op_be_Return, emit_be_Return); + + /* no need to emit anything for the following nodes */ + set_emitter(op_Phi, emit_nothing); + set_emitter(op_be_Keep, emit_nothing); + set_emitter(op_be_Start, emit_nothing); + set_emitter(op_be_Barrier, emit_nothing); +} /** * Emits code for a 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;) - - 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) { - node_emitter *emit = (node_emitter *)op->ops.generic; - emit(irn, env); - } - else { - ir_fprintf(F, "\t%-35s /* %+F %G */\n", "", irn, irn); + emit_func *emit = (emit_func *)op->ops.generic; + be_dbg_set_dbg_info(get_irn_dbg_info(irn)); + (*emit)(irn); + } else { + panic("Error: No emit handler for node %+F (graph %+F)\n", + irn, current_ir_graph); } } /** - * Walks over the nodes in a block connected by scheduling edges - * and emits code for each node. + * emit the block label if needed. */ -void arm_gen_block(ir_node *block, void *env) { - ir_node *irn; - - fprintf(((arm_emit_env_t *)env)->out, "BLOCK_%ld:\n", get_irn_node_nr(block)); - sched_foreach(block, irn) { - arm_emit_node(irn, env); +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 = be_get_irg_exec_freq(cg->irg); + + 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) { + be_gas_emit_block_name(block); + be_emit_char(':'); -/** - * Emits code for function start. - */ -void arm_emit_start(FILE *F, 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_pad_comment(); + be_emit_cstring(" /* preds:"); - if (get_entity_visibility(ent) == visibility_external_visible) - fprintf(F, "\t.global %s\n", irg_name); - fprintf(F, "%s:\n", irg_name); + /* 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/* "); + be_gas_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(); } /** - * Emits code for function end + * Walks over the nodes in a block connected by scheduling edges + * and emits code for each node. */ -void arm_emit_end(FILE *F, ir_graph *irg) { - fprintf(F, "\t.ident \"firmcc\"\n"); +static void arm_gen_block(ir_node *block, ir_node *prev_block) +{ + ir_node *irn; + + 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); + } } /** + * 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); @@ -939,33 +1028,42 @@ void arm_gen_labels(ir_node *block, void *env) { } } - /** - * Main driver. Emits the code for one routine. + * Compare two entries of the symbol or tarval set. */ -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; - - 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; +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; +} + +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; + ir_entity *entity = get_irg_entity(irg); + + cg = arm_cg; + sym_or_tv = new_set(cmp_sym_or_tv, 8); + + be_gas_elf_type_char = '%'; arm_register_emitters(); - /* create the block schedule. For now, we don't need it earlier. */ - blk_sched = sched_create_block_schedule(cg->irg, cg->birg->exec_freq); + be_dbg_method_begin(entity); + + /* create the block schedule */ + blk_sched = be_create_block_schedule(irg); + + be_gas_emit_function_prolog(entity, 4); - arm_emit_start(F, irg); - irg_block_walk_graph(irg, arm_gen_labels, NULL, &emit_env); + irg_block_walk_graph(irg, arm_gen_labels, NULL, NULL); n = ARR_LEN(blk_sched); for (i = 0; i < n;) { @@ -977,17 +1075,53 @@ 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) { + emit_constant_name(entry); + be_emit_cstring(":\n"); + be_emit_write_line(); + + if (entry->is_entity) { + be_emit_cstring("\t.word\t"); + be_gas_emit_entity(entry->u.entity); + 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); + + be_gas_emit_function_epilog(entity); + be_dbg_method_end(); +} - obstack_free(&emit_env.obst, NULL); +void arm_init_emitter(void) +{ + FIRM_DBG_REGISTER(dbg, "firm.be.arm.emit"); }