From: Michael Beck Date: Mon, 27 Mar 2006 11:54:07 +0000 (+0000) Subject: Added arm backend from BE praktikum X-Git-Url: http://nsz.repo.hu/git/?a=commitdiff_plain;h=021dd42c11eca475bae9fb4176a6b8f02f471c43;p=libfirm Added arm backend from BE praktikum --- diff --git a/ir/be/arm/arm_emitter.c b/ir/be/arm/arm_emitter.c new file mode 100644 index 000000000..787ed0cf3 --- /dev/null +++ b/ir/be/arm/arm_emitter.c @@ -0,0 +1,844 @@ +/* arm emitter */ +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "xmalloc.h" +#include "tv.h" +#include "iredges.h" +#include "debug.h" +#include "irgwalk.h" +#include "irprintf.h" +#include "irop_t.h" +#include "irprog_t.h" +#include "irargs_t.h" + +#include "../besched.h" + +#include "arm_emitter.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" + +#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]; + ir_mode *mode = get_irn_mode(n); + + if ( is_immediate_node(n) ) { + tarval *tv = get_arm_value(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; + } + +} + +/** + * 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; +} + +/* We always pass the ir_node which is a pointer. */ +static int arm_get_arg_type(const lc_arg_occ_t *occ) { + return lc_arg_type_ptr; +} + + +/** + * Returns the register at in position pos. + */ +static const arch_register_t *get_in_reg(const ir_node *irn, int pos) { + ir_node *op; + const arch_register_t *reg = NULL; + + assert(get_irn_arity(irn) > pos && "Invalid IN position"); + + /* The out register of the operator at position pos is the + in register we need. */ + op = get_irn_n(irn, pos); + + reg = arch_get_irn_register(arch_env, op); + + /* ONLY TEMPORARY WORK-AROUND */ +// if (!reg) { +// printf("FIXME\n"); +// reg = &arm_general_purpose_regs[REG_MURX]; +// } + + assert(reg && "no in register found"); + return reg; +} + +/** + * Returns the register at out position pos. + */ +static const arch_register_t *get_out_reg(const ir_node *irn, int pos) { + ir_node *proj; + const arch_register_t *reg = NULL; + + assert(get_irn_n_edges(irn) > pos && "Invalid OUT position"); + + /* 1st case: irn is not of mode_T, so it has only */ + /* one OUT register -> good */ + /* 2nd case: irn is of mode_T -> collect all Projs and ask the */ + /* Proj with the corresponding projnum for the register */ + + if (get_irn_mode(irn) != mode_T) { + reg = arch_get_irn_register(arch_env, irn); + } + else if (is_arm_irn(irn)) { + reg = get_arm_out_reg(irn, pos); + } + else { + const ir_edge_t *edge; + + foreach_out_edge(irn, edge) { + proj = get_edge_src_irn(edge); + assert(is_Proj(proj) && "non-Proj from mode_T node"); + if (get_Proj_proj(proj) == pos) { + reg = arch_get_irn_register(arch_env, proj); + break; + } + } + } + + assert(reg && "no out register found"); + return reg; +} + +/** + * 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. + */ +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); + } + + return arch_register_get_name(reg); +} + +/** + * Get the register name for a node. + */ +static int arm_get_reg_name(lc_appendable_t *app, + const lc_arg_occ_t *occ, const lc_arg_value_t *arg) +{ + const char *buf; + ir_node *X = arg->v_ptr; + int nr = occ->width - 1; + + if (!X) + return lc_appendable_snadd(app, "(null)", 6); + + if (occ->conversion == 'S') { + buf = get_arm_reg_name(X, nr, 1); + } + else { /* 'D' */ + buf = get_arm_reg_name(X, nr, 0); + } + + 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; + + if (!X) + return lc_appendable_snadd(app, "(null)", 6); + + if (occ->conversion == 'C') { + buf = node_const_to_str(X); + } + else { /* 'O' */ + buf = node_offset_to_str(X); + } + + return lc_appendable_snadd(app, buf, strlen(buf)); +} + +/** + * Determines the SSE 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) +{ + 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'); +} + +/** + * 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; + + 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 }; + + if(env == NULL) { + /* extend the firm printer */ + env = firm_get_arg_env(); + //lc_arg_new_env(); + + 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); + } + + return env; +} + +/** + * Formated print of commands and comments. + */ +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); +} + +/* + * Add a number to a prefix. This number will not be used a second time. + */ +static char *get_unique_label(char *buf, size_t buflen, const char *prefix) { + static unsigned long id = 0; + snprintf(buf, buflen, "%s%lu", prefix, ++id); + return buf; +} + + +/** + * Returns the target label for a control flow node. + */ +static char *get_cfop_target(const ir_node *irn, char *buf) { + ir_node *bl = get_irn_link(irn); + + snprintf(buf, SNPRINTF_BUF_LEN, "BLOCK_%ld", get_irn_node_nr(bl)); + return buf; +} + +/************************************************************************/ +/* 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; + const ir_edge_t *edge; + ir_node *true_block = NULL; + ir_node *false_block = NULL; + 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; + } else { + assert(0 && "tertium non datur! (CondJmp)"); + } + } + + if (proj_num == pn_Cmp_False) { + fprintf(out, "\tB BLOCK_%d\t\t\t/* false case */\n", get_irn_node_nr(false_block)); + } else if (proj_num == pn_Cmp_True) { + fprintf(out, "\tB BLOCK_%d\t\t\t/* true case */\n", get_irn_node_nr(true_block)); + } 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); + + 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); + } 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"); + } + + 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); + + 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); + } + } +} + +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(); + + 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); + + assert ( size > 0 && "CopyB needs size > 0" ); + if (size & 3) + size += 4; + size >>= 2; + 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); + 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); + 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); + 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); + --size; + } +} + +static void emit_arm_SwitchJmp(ir_node *irn, void *env) { + char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; + 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; + + block_nr = get_irn_node_nr(irn); + n_projs = get_arm_n_projs(irn); + + projs = xcalloc(n_projs , sizeof(ir_node*)); + + 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)); + + 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); + } 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); + } +} + +// 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; + 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); + 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); + } else { + assert(0 && "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); + } else { + assert(0 && "spill not supported for this mode"); + } +} + +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); + 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); + } else { + assert(0 && "reload not supported for this mode"); + } +} + +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); +} + +/************************************************************************/ +/* 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_%d\t\t\t/* unconditional Jump */\n", get_irn_node_nr(target_block)); +} + +static void emit_Proj(ir_node *irn, void *env) { + +} + + + +/*********************************************************************************** + * _ __ _ + * (_) / _| | | + * _ __ ___ __ _ _ _ __ | |_ _ __ __ _ _ __ ___ _____ _____ _ __| | __ + * | '_ ` _ \ / _` | | '_ \ | _| '__/ _` | '_ ` _ \ / _ \ \ /\ / / _ \| '__| |/ / + * | | | | | | (_| | | | | | | | | | | (_| | | | | | | __/\ 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 + + /* 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(SwitchJmp); + ARM_EMIT(CopyB); +// ARM_EMIT(CopyB_i); +// ARM_EMIT(Const); + ARM_EMIT(SymConst); + ARM_EMIT(SwitchJmp); + + /* 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 + EMIT(Proj); +#endif + +#undef ARM_EMIT +#undef BE_EMIT +#undef EMIT +} + +/** + * 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)); + + if (op->ops.generic) { + void (*emit)(const ir_node *, void *) = (void (*)(const ir_node *, void *))op->ops.generic; + (*emit)(irn, env); + } + else { + ir_fprintf(F, "\t\t\t\t\t/* %+F */\n", irn); + } +} + +/** + * Walks over the nodes in a block connected by scheduling edges + * and emits code for each node. + */ +void arm_gen_block(ir_node *block, void *env) { + ir_node *irn; + + if (! is_Block(block)) + return; + + fprintf(((arm_emit_env_t *)env)->out, "BLOCK_%ld:\n", get_irn_node_nr(block)); + sched_foreach(block, irn) { + arm_emit_node(irn, env); + } +} + + +/** + * 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); +} + +/** + * Emits code for function end + */ +void arm_emit_end(FILE *F, ir_graph *irg) { + const char *irg_name = get_entity_name(get_irg_entity(irg)); +} + +/** + * Sets labels for control flow nodes (jump target) + * TODO: Jump optimization + */ +void arm_gen_labels(ir_node *block, void *env) { + ir_node *pred; + int n = get_Block_n_cfgpreds(block); + + for (n--; n >= 0; n--) { + pred = get_Block_cfgpred(block, n); + set_irn_link(pred, block); + } +} + + +/** + * Main driver + */ +void arm_gen_routine(FILE *F, ir_graph *irg, const arm_code_gen_t *cg) { + arm_emit_env_t emit_env; + + emit_env.mod = firm_dbg_register("firm.be.arm.emit"); + emit_env.out = F; + emit_env.arch_env = cg->arch_env; + emit_env.cg = cg; + + /* set the global arch_env (needed by print hooks) */ + arch_env = cg->arch_env; + + 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); +} diff --git a/ir/be/arm/arm_emitter.h b/ir/be/arm/arm_emitter.h new file mode 100644 index 000000000..787a7d4f1 --- /dev/null +++ b/ir/be/arm/arm_emitter.h @@ -0,0 +1,28 @@ +#ifndef _arm_EMITTER_H_ +#define _arm_EMITTER_H_ + +#include "irargs_t.h" // this also inlucdes +#include "irnode.h" +#include "debug.h" + +#include "../bearch.h" + +#include "bearch_arm_t.h" + +typedef struct _arm_emit_env_t { + firm_dbg_module_t *mod; + FILE *out; + const arch_env_t *arch_env; + const arm_code_gen_t *cg; +} arm_emit_env_t; + +const lc_arg_env_t *arm_get_arg_env(void); + +void equalize_dest_src(FILE *F, ir_node *n); + +int get_arm_reg_nr(ir_node *irn, int posi, int in_out); +const char *get_arm_in_reg_name(ir_node *irn, int pos); + +void arm_gen_routine(FILE *F, ir_graph *irg, const arm_code_gen_t *cg); + +#endif /* _arm_EMITTER_H_ */ diff --git a/ir/be/arm/arm_gen_decls.c b/ir/be/arm/arm_gen_decls.c new file mode 100644 index 000000000..964452e63 --- /dev/null +++ b/ir/be/arm/arm_gen_decls.c @@ -0,0 +1,596 @@ +/** + * Dumps global variables and constants as arm assembler. + * @date 14.02.2006 + * @version $Id$ + */ + +#include +#include +#include +#include + +#include "xmalloc.h" +#include + +#ifdef obstack_chunk_alloc +# undef obstack_chunk_alloc +# define obstack_chunk_alloc xmalloc +#else +# define obstack_chunk_alloc xmalloc +# define obstack_chunk_free free +#endif + +#include "tv.h" +#include "irnode.h" +#include "entity.h" +#include "irprog.h" + +#include "arm_gen_decls.h" + +/************************************************************************/ + +/* + * returns the highest bit value + */ +static unsigned highest_bit(unsigned v) +{ + int res = -1; + + if (v >= (1U << 16U)) { + res += 16; + v >>= 16; + } + if (v >= (1U << 8U)) { + res += 8; + v >>= 8; + } + if (v >= (1U << 4U)) { + res += 4; + v >>= 4; + } + if (v >= (1U << 2U)) { + res += 2; + v >>= 2; + } + if (v >= (1U << 1U)) { + res += 1; + v >>= 1; + } + if (v >= 1) + res += 1; + + return res; +} + +/* + * output the alignment + */ +static void arm_dump_align(struct obstack *obst, int align) +{ + int h = highest_bit(align); + + if ((1 << h) < align) + ++h; + align = (1 << h); + + if (align > 1) + obstack_printf(obst, "\t.align %d\n", align); +} + +static void dump_arith_tarval(struct obstack *obst, tarval *tv, int bytes) +{ + switch (bytes) { + + case 1: + obstack_printf(obst, "0x%02x", get_tarval_sub_bits(tv, 0)); + break; + + case 2: + obstack_printf(obst, "0x%02x%02x", get_tarval_sub_bits(tv, 1), get_tarval_sub_bits(tv, 0)); + break; + + case 4: + obstack_printf(obst, "0x%02x%02x%02x%02x", + get_tarval_sub_bits(tv, 3), get_tarval_sub_bits(tv, 2), get_tarval_sub_bits(tv, 1), get_tarval_sub_bits(tv, 0)); + break; + + case 8: + obstack_printf(obst, "0x%02x%02x%02x%02x%02x%02x%02x%02x", + get_tarval_sub_bits(tv, 7), get_tarval_sub_bits(tv, 6), get_tarval_sub_bits(tv, 5), get_tarval_sub_bits(tv, 4), + get_tarval_sub_bits(tv, 3), get_tarval_sub_bits(tv, 2), get_tarval_sub_bits(tv, 1), get_tarval_sub_bits(tv, 0)); + break; + + case 10: + case 12: + break; + + default: + fprintf(stderr, "Try to dump an tarval with %d bytes\n", bytes); + assert(0); + } +} + +/* + * dump an arithmetic tarval + */ +static void arm_dump_arith_tarval(struct obstack *obst, tarval *tv, int bytes) +{ + switch (bytes) { + + case 1: + obstack_printf(obst, "\t.byte\t"); + break; + + case 2: + obstack_printf(obst, "\t.value\t"); + break; + + case 4: + obstack_printf(obst, "\t.long\t"); + break; + + case 8: + obstack_printf(obst, "\t.quad\t"); + break; + + case 10: + case 12: + break; + + default: + fprintf(stderr, "Try to dump an tarval with %d bytes\n", bytes); + assert(0); + } + dump_arith_tarval(obst, tv, bytes); +} + + +/* + * dump an atomic value + */ +static void do_dump_atomic_init(struct obstack *obst, ir_node *init) +{ + ir_mode *mode = get_irn_mode(init); + int bytes = get_mode_size_bytes(mode); + tarval *tv; + + switch (get_irn_opcode(init)) { + + case iro_Cast: + do_dump_atomic_init(obst, get_Cast_op(init)); + return; + + case iro_Conv: + do_dump_atomic_init(obst, get_Conv_op(init)); + return; + + case iro_Const: + tv = get_Const_tarval(init); + + /* beware of old stuff */ + assert(! mode_is_reference(mode)); + + /* it's a arithmetic value */ + dump_arith_tarval(obst, tv, bytes); + return; + + case iro_SymConst: + switch (get_SymConst_kind(init)) { + case symconst_addr_name: + obstack_printf(obst, "%s", get_id_str(get_SymConst_name(init))); + break; + + case symconst_addr_ent: + obstack_printf(obst, "%s", get_entity_ld_name(get_SymConst_entity(init))); + break; + + case symconst_size: + obstack_printf(obst, "%d", get_type_size_bytes(get_SymConst_type(init))); + break; + + default: + assert(0 && "dump_atomic_init(): don't know how to init from this SymConst"); + } + return; + + case iro_Add: + do_dump_atomic_init(obst, get_Add_left(init)); + obstack_printf(obst, " + "); + do_dump_atomic_init(obst, get_Add_right(init)); + return; + + case iro_Sub: + do_dump_atomic_init(obst, get_Sub_left(init)); + obstack_printf(obst, " - "); + do_dump_atomic_init(obst, get_Sub_right(init)); + return; + + case iro_Mul: + do_dump_atomic_init(obst, get_Mul_left(init)); + obstack_printf(obst, " * "); + do_dump_atomic_init(obst, get_Mul_right(init)); + return; + + default: + assert(0 && "dump_atomic_init(): unknown IR-node"); + } +} + +/* + * dump an atomic value + */ +static void dump_atomic_init(struct obstack *obst, ir_node *init) +{ + ir_mode *mode = get_irn_mode(init); + int bytes = get_mode_size_bytes(mode); + + switch (bytes) { + + case 1: + obstack_printf(obst, "\t.byte\t"); + break; + + case 2: + obstack_printf(obst, "\t.value\t"); + break; + + case 4: + obstack_printf(obst, "\t.long\t"); + break; + + case 8: + obstack_printf(obst, "\t.quad\t"); + break; + + case 10: + case 12: + /* handled in arith */ + break; + + default: + fprintf(stderr, "Try to dump an tarval with %d bytes\n", bytes); + assert(0); + } + + do_dump_atomic_init(obst, init); + obstack_printf(obst, "\n"); +} + +/************************************************************************/ +/* Routines to dump global variables */ +/************************************************************************/ + +/** + * Determine if an entity is a string constant + * @param ent The entity + * @return 1 if it is a string constant, 0 otherwise + */ +static int ent_is_string_const(entity *ent) +{ + int res = 0; + ir_type *ty; + + ty = get_entity_type(ent); + + /* if it's an array */ + if (is_Array_type(ty)) { + ir_type *elm_ty = get_array_element_type(ty); + + /* and the array's element type is primitive */ + if (is_Primitive_type(elm_ty)) { + ir_mode *mode = get_type_mode(elm_ty); + + /* + * and the mode of the element type is an int of + * the same size as the byte mode + */ + if (mode_is_int(mode) + && get_mode_size_bits(mode) == get_mode_size_bits(mode_Bs)) + { + int i, c, n; + + n = get_compound_ent_n_values(ent); + for (i = 0; i < n; ++i) { + ir_node *irn = get_compound_ent_value(ent, i); + if(get_irn_opcode(irn) != iro_Const) + return 0; + + c = (int) get_tarval_long(get_Const_tarval(irn)); + + if((i < n - 1 && !(isgraph(c) || isspace(c))) + || (i == n - 1 && c != '\0')) + return 0; + } + + res = 1; + } + } + } + + return res; +} + +/** + * Dump a atring constant. + * No checks are made!! + * @param obst The obst to dump on. + * @param ent The entity to dump. + */ +static void dump_string_cst(struct obstack *obst, entity *ent) +{ + int i, n; + + obstack_printf(obst, "\t.string \""); + n = get_compound_ent_n_values(ent); + + for (i = 0; i < n-1; ++i) { + ir_node *irn; + int c; + + irn = get_compound_ent_value(ent, i); + c = (int) get_tarval_long(get_Const_tarval(irn)); + + switch (c) { + case '"' : obstack_printf(obst, "\\\""); break; + case '\n': obstack_printf(obst, "\\n"); break; + case '\r': obstack_printf(obst, "\\r"); break; + case '\t': obstack_printf(obst, "\\t"); break; + default : + if (isprint(c)) + obstack_printf(obst, "%c", c); + else + obstack_printf(obst, "%O", c); + break; + } + } + obstack_printf(obst, "\"\n"); +} + +struct arr_info { + int n_elems; + int visit_cnt; + int size; +}; + +/* + * Dumps the initialization of global variables that are not + * "uninitialized". + */ +static void dump_global(struct obstack *rdata_obstack, struct obstack *data_obstack, struct obstack *comm_obstack, entity *ent) +{ + ir_type *ty = get_entity_type(ent); + const char *ld_name = get_entity_ld_name(ent); + int align, h; + struct obstack *obst = data_obstack; + + /* + * FIXME: did NOT work for partly constant values + */ + if (! is_Method_type(ty)) { + ent_variability variability = get_entity_variability(ent); + visibility visibility = get_entity_visibility(ent); + + if (variability == variability_constant) { + /* a constant entity, put it on the rdata */ + obst = rdata_obstack; + } + + /* check, wether it is initialized, if yes create data */ + if (variability != variability_uninitialized) { + if (visibility == visibility_external_visible) { + obstack_printf(obst, ".globl\t%s\n", ld_name); + } + obstack_printf(obst, "\t.type\t%s,%%object\n", ld_name); + obstack_printf(obst, "\t.size\t%s,%d\n", ld_name, (get_type_size_bits(ty) + 7) >> 3); + + align = get_type_alignment_bytes(ty); + arm_dump_align(obst, align); + + obstack_printf(obst, "%s:\n", ld_name); + + if (is_atomic_type(ty)) { + if (get_entity_visibility(ent) != visibility_external_allocated) + dump_atomic_init(obst, get_atomic_ent_value(ent)); + } + else { + int i, size = 0; + + if (ent_is_string_const(ent)) { + dump_string_cst(obst, ent); + } + else if (is_Array_type(ty)) { + int filler; + + /* potential spare values should be already included! */ + for (i = 0; i < get_compound_ent_n_values(ent); ++i) { + entity *step = get_compound_ent_value_member(ent, i); + ir_type *stype = get_entity_type(step); + + if (get_type_mode(stype)) { + int align = (get_type_alignment_bits(stype) + 7) >> 3; + int n = size % align; + + if (n > 0) { + obstack_printf(obst, "\t.zero\t%d\n", align - n); + size += align - n; + } + } + dump_atomic_init(obst, get_compound_ent_value(ent, i)); + size += get_type_size_bytes(stype); + } + filler = get_type_size_bytes(ty) - size; + + if (filler > 0) + obstack_printf(obst, "\t.zero\t%d\n", filler); + } + else if (is_compound_type(ty)) { + ir_node **vals; + int type_size, j; + + /* Compound entities are NOT sorted. + * The sorting strategy used doesn't work for `value' compound fields nor + * for partially_constant entities. + */ + + /* + * in the worst case, every entity allocates one byte, so the type + * size should be equal or bigger the number of fields + */ + type_size = get_type_size_bytes(ty); + vals = xcalloc(type_size, sizeof(*vals)); + + /* collect the values and store them at the offsets */ + for(i = 0; i < get_compound_ent_n_values(ent); ++i) { + int graph_length, aipos, offset; + struct arr_info *ai; + int all_n = 1; + compound_graph_path *path = get_compound_ent_value_path(ent, i); + + /* get the access path to the costant value */ + graph_length = get_compound_graph_path_length(path); + ai = xcalloc(graph_length, sizeof(struct arr_info)); + + /* We wanna know how many arrays are on the path to the entity. We also have to know how + * many elements each array holds to calculate the offset for the entity. */ + for (j = 0; j < graph_length; j++) { + entity *step = get_compound_graph_path_node(path, j); + ir_type *step_type = get_entity_type(step); + int ty_size = (get_type_size_bits(step_type) + 7) >> 3; + int k, n = 0; + + if (is_Array_type(step_type)) + for (k = 0; k < get_array_n_dimensions(step_type); k++) + n += get_tarval_long(get_Const_tarval(get_array_upper_bound(step_type, k))); + if (n) all_n *= n; + ai[j].n_elems = n ? all_n + 1 : 0; + ai[j].visit_cnt = 0; + ai[j].size = ty_size; + } + + aipos = graph_length - 1; + if (aipos) aipos--; + + for (offset = j = 0; j < graph_length; j++) { + entity *step = get_compound_graph_path_node(path, j); + ir_type *step_type = get_entity_type(step); + int ent_ofs = get_entity_offset_bytes(step); + int stepsize = 0; + + /* add all positive offsets (= offsets in structs) */ + if (ent_ofs >= 0) offset += ent_ofs; + + if (j == graph_length - 1) { + stepsize = (get_type_size_bits(step_type) + 7) >> 3; + + /* Search the next free position in vals depending on the information from above (ai). */ + while (vals[offset]) { + if (ai[aipos].visit_cnt < ai[aipos].n_elems) { + offset += stepsize; + ai[aipos].visit_cnt++; + } + else + while (aipos >= 0 && ai[aipos].visit_cnt == ai[aipos].n_elems) { + stepsize = ai[aipos--].size; + offset += stepsize; + } + } + + assert(aipos >= 0 && "couldn't store entity"); + vals[offset] = get_compound_ent_value(ent, i); + } + } + + free(ai); + } + + /* now write them sorted */ + for(i = 0; i < type_size; ) { + if (vals[i]) { + dump_atomic_init(obst, vals[i]); + i += (get_mode_size_bytes(get_irn_mode(vals[i]))); + } + else { + /* a gap */ + obstack_printf(obst, "\t.byte\t0\n"); + ++i; + } + } + free(vals); + } + else { + assert(0 && "unsupported type"); + } + } + obstack_printf(obst, "\n"); + } + else if (visibility != visibility_external_allocated) { + if (visibility == visibility_local) { + obstack_printf(comm_obstack, "\t.local\t%s\n", ld_name); + } + + /* calculate the alignment */ + align = get_type_alignment_bytes(ty); + h = highest_bit(align); + + if ((1 << h) < align) + ++h; + align = (1 << h); + + if (align < 1) + align = 1; + + obstack_printf(comm_obstack, "\t.comm\t%s,%d,%d\n", ld_name, (get_type_size_bits(ty) + 7) >> 3, align); + } + } +} + +/* + * Dumps declarations of global variables and the initialization code. + */ +void arm_dump_globals(struct obstack *rdata_obstack, struct obstack *data_obstack, struct obstack *comm_obstack) +{ + ir_type *gt = get_glob_type(); + int i, n = get_class_n_members(gt); + + for (i = 0; i < n; i++) + dump_global(rdata_obstack, data_obstack, comm_obstack, get_class_member(gt, i)); +} + +/************************************************************************/ + +void arm_gen_decls(FILE *out) { + struct obstack rodata, data, comm; + int size; + char *cp; + + obstack_init(&rodata); + obstack_init(&data); + obstack_init(&comm); + + arm_dump_globals(&rodata, &data, &comm); + + size = obstack_object_size(&data); + cp = obstack_finish(&data); + if (size > 0) { + fprintf(out, "\t.data\n"); + fwrite(cp, 1, size, out); + } + + size = obstack_object_size(&rodata); + cp = obstack_finish(&rodata); + if (size > 0) { + fprintf(out, "\t.section\t.rodata\n"); + fwrite(cp, 1, size, out); + } + + size = obstack_object_size(&comm); + cp = obstack_finish(&comm); + if (size > 0) { + fprintf(out, "\t.common\n"); + fwrite(cp, 1, size, out); + } + + obstack_free(&rodata, NULL); + obstack_free(&data, NULL); + obstack_free(&comm, NULL); +} diff --git a/ir/be/arm/arm_gen_decls.h b/ir/be/arm/arm_gen_decls.h new file mode 100644 index 000000000..2f2c10f62 --- /dev/null +++ b/ir/be/arm/arm_gen_decls.h @@ -0,0 +1,9 @@ +#ifndef _ARM_GEN_DECLS_H_ +#define _ARM_GEN_DECLS_H_ + +/** + * Generate all entities. + */ +void arm_gen_decls(FILE *out); + +#endif /* _ARM_GEN_DECLS_H_ */ diff --git a/ir/be/arm/arm_map_regs.c b/ir/be/arm/arm_map_regs.c new file mode 100644 index 000000000..387166329 --- /dev/null +++ b/ir/be/arm/arm_map_regs.c @@ -0,0 +1,91 @@ +/** + * Register mapping for firm nodes. Stolen from bearch_firm :) + * $Id$ + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "arm_map_regs.h" +#include "arm_new_nodes.h" + +#include "gen_arm_regalloc_if.h" + + +static const arch_register_t *gpreg_param_reg_std[] = { + &arm_general_purpose_regs[REG_R0], + &arm_general_purpose_regs[REG_R1], + &arm_general_purpose_regs[REG_R2], + &arm_general_purpose_regs[REG_R3], +}; + +const arch_register_t *arm_get_RegParam_reg(int n) { + assert(n < 4 && n >=0 && "register param > 3 angefordert"); + return gpreg_param_reg_std[n]; +} + +/* Mapping to store registers in firm nodes */ + +struct arm_irn_reg_assoc { + const ir_node *irn; + const arch_register_t *reg; +}; + +int arm_cmp_irn_reg_assoc(const void *a, const void *b, size_t len) { + const struct arm_irn_reg_assoc *x = a; + const struct arm_irn_reg_assoc *y = b; + + return x->irn != y->irn; +} + +static struct arm_irn_reg_assoc *get_irn_reg_assoc(const ir_node *irn, set *reg_set) { + struct arm_irn_reg_assoc templ; + unsigned int hash; + + templ.irn = irn; + templ.reg = NULL; + hash = HASH_PTR(irn); + + return set_insert(reg_set, &templ, sizeof(templ), hash); +} + +void arm_set_firm_reg(ir_node *irn, const arch_register_t *reg, set *reg_set) { + struct arm_irn_reg_assoc *assoc = get_irn_reg_assoc(irn, reg_set); + assoc->reg = reg; +} + +const arch_register_t *arm_get_firm_reg(const ir_node *irn, set *reg_set) { + struct arm_irn_reg_assoc *assoc = get_irn_reg_assoc(irn, reg_set); + return assoc->reg; +} + + + +/** + * Translates the projnum into a "real" argument position for register + * requirements dependend on the predecessor. + */ +long arm_translate_proj_pos(const ir_node *proj) { + ir_node *pred = get_Proj_pred(proj); + long nr = get_Proj_proj(proj); + + if (is_arm_Load(pred) || is_arm_fLoadd(pred) || is_arm_fLoads(pred)) { + if (nr == pn_Load_res) + return 0; + assert(0 && "unsupported Proj(Load) number"); + } + else if (is_arm_Store(pred) || is_arm_fStores(pred) || is_arm_fStored(pred)) { + return 0; + } + else if (is_arm_fDiv(pred)) { + if (nr == pn_Quot_res) + return 0; + else + assert(0 && "there should be no more Projs for a fDiv"); + } + +// assert(0 && "unsupported Proj(X)"); + return nr; +} diff --git a/ir/be/arm/arm_map_regs.h b/ir/be/arm/arm_map_regs.h new file mode 100644 index 000000000..45b9ba516 --- /dev/null +++ b/ir/be/arm/arm_map_regs.h @@ -0,0 +1,18 @@ +#ifndef _arm_MAP_REGS_H_ +#define _arm_MAP_REGS_H_ + +#include "irnode.h" +#include "set.h" + +#include "../bearch.h" +#include "arm_nodes_attr.h" + +const arch_register_t *arm_get_RegParam_reg(int n); + +int arm_cmp_irn_reg_assoc(const void *a, const void *b, size_t len); +void arm_set_firm_reg(ir_node *irn, const arch_register_t *reg, set *reg_set); +const arch_register_t *arm_get_firm_reg(const ir_node *irn, set *reg_set); + +long arm_translate_proj_pos(const ir_node *proj); + +#endif /* _arm_MAP_REGS_H_ */ diff --git a/ir/be/arm/arm_new_nodes.c b/ir/be/arm/arm_new_nodes.c new file mode 100644 index 000000000..69ba1c20a --- /dev/null +++ b/ir/be/arm/arm_new_nodes.c @@ -0,0 +1,599 @@ +/** + * This file implements the creation of the achitecture specific firm opcodes + * and the coresponding node constructors for the arm assembler irg. + * $Id$ + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _WIN32 +#include +#else +#include +#endif + +#include + +#include "irprog_t.h" +#include "irgraph_t.h" +#include "irnode_t.h" +#include "irmode_t.h" +#include "ircons_t.h" +#include "iropt_t.h" +#include "irop.h" +#include "firm_common_t.h" +#include "irvrfy_t.h" +#include "irprintf.h" + +#include "../bearch.h" + +#include "arm_nodes_attr.h" +#include "arm_new_nodes.h" +#include "gen_arm_regalloc_if.h" + +#include "../beabi.h" +#include "bearch_arm_t.h" + + +/*********************************************************************************** + * _ _ _ __ + * | | (_) | | / _| + * __| |_ _ _ __ ___ _ __ ___ _ __ _ _ __ | |_ ___ _ __| |_ __ _ ___ ___ + * / _` | | | | '_ ` _ \| '_ \ / _ \ '__| | | '_ \| __/ _ \ '__| _/ _` |/ __/ _ \ + * | (_| | |_| | | | | | | |_) | __/ | | | | | | || __/ | | || (_| | (_| __/ + * \__,_|\__,_|_| |_| |_| .__/ \___|_| |_|_| |_|\__\___|_| |_| \__,_|\___\___| + * | | + * |_| + ***********************************************************************************/ + +/** + * Returns a string containing the names of all registers within the limited bitset + */ +static char *get_limited_regs(const arch_register_req_t *req, char *buf, int max) { + bitset_t *bs = bitset_alloca(req->cls->n_regs); + char *p = buf; + int size = 0; + int i, cnt; + + req->limited(NULL, bs); + + for (i = 0; i < req->cls->n_regs; i++) { + if (bitset_is_set(bs, i)) { + cnt = snprintf(p, max - size, " %s", req->cls->regs[i].name); + if (cnt < 0) { + fprintf(stderr, "dumper problem, exiting\n"); + exit(1); + } + + p += cnt; + size += cnt; + + if (size >= max) + break; + } + } + + return buf; +} + +/** + * Dumps the register requirements for either in or out. + */ +static void dump_reg_req(FILE *F, ir_node *n, const arm_register_req_t **reqs, int inout) { + char *dir = inout ? "out" : "in"; + int max = inout ? get_arm_n_res(n) : get_irn_arity(n); + char *buf = alloca(1024); + int i; + + memset(buf, 0, 1024); + + if (reqs) { + for (i = 0; i < max; i++) { + fprintf(F, "%sreq #%d =", dir, i); + + if (reqs[i]->req.type == arch_register_req_type_none) { + fprintf(F, " n/a"); + } + + if (reqs[i]->req.type & arch_register_req_type_normal) { + fprintf(F, " %s", reqs[i]->req.cls->name); + } + + if (reqs[i]->req.type & arch_register_req_type_limited) { + fprintf(F, " %s", get_limited_regs(&reqs[i]->req, buf, 1024)); + } + + if (reqs[i]->req.type & arch_register_req_type_should_be_same) { + ir_fprintf(F, " same as %+F", get_irn_n(n, reqs[i]->same_pos)); + } + + if (reqs[i]->req.type & arch_register_req_type_should_be_different) { + ir_fprintf(F, " different from %+F", get_irn_n(n, reqs[i]->different_pos)); + } + + fprintf(F, "\n"); + } + + fprintf(F, "\n"); + } + else { + fprintf(F, "%sreq = N/A\n", dir); + } +} + + +/** + * Dumper interface for dumping arm nodes in vcg. + * @param n the node to dump + * @param F the output file + * @param reason indicates which kind of information should be dumped + * @return 0 on success or != 0 on failure + */ +static int dump_node_arm(ir_node *n, FILE *F, dump_reason_t reason) { + ir_mode *mode = NULL; + int bad = 0; + int i; + arm_attr_t *attr; + const arm_register_req_t **reqs; + const arch_register_t **slots; + + switch (reason) { + case dump_node_opcode_txt: + fprintf(F, "%s", get_irn_opname(n)); + break; + + case dump_node_mode_txt: + mode = get_irn_mode(n); + + if (mode) { + fprintf(F, "[%s]", get_mode_name(mode)); + } + else { + fprintf(F, "[?NOMODE?]"); + } + break; + + case dump_node_nodeattr_txt: + + /* TODO: dump some attributes which should show up */ + /* in node name in dump (e.g. consts or the like) */ + + break; + + case dump_node_info_txt: + attr = get_arm_attr(n); + fprintf(F, "=== arm attr begin ===\n"); + + /* dump IN requirements */ + if (get_irn_arity(n) > 0) { + reqs = get_arm_in_req_all(n); + dump_reg_req(F, n, reqs, 0); + } + + /* dump OUT requirements */ + if (attr->n_res > 0) { + reqs = get_arm_out_req_all(n); + dump_reg_req(F, n, reqs, 1); + } + + /* dump assigned registers */ + slots = get_arm_slots(n); + if (slots && attr->n_res > 0) { + for (i = 0; i < attr->n_res; i++) { + if (slots[i]) { + fprintf(F, "reg #%d = %s\n", i, slots[i]->name); + } + else { + fprintf(F, "reg #%d = n/a\n", i); + } + } + } + fprintf(F, "\n"); + + /* dump n_res */ + fprintf(F, "n_res = %d\n", get_arm_n_res(n)); + + /* dump flags */ + fprintf(F, "flags ="); + if (attr->flags == arch_irn_flags_none) { + fprintf(F, " none"); + } + else { + if (attr->flags & arch_irn_flags_dont_spill) { + fprintf(F, " unspillable"); + } + if (attr->flags & arch_irn_flags_rematerializable) { + fprintf(F, " remat"); + } + if (attr->flags & arch_irn_flags_ignore) { + fprintf(F, " ignore"); + } + } + fprintf(F, " (%d)\n", attr->flags); + + if (get_arm_value(n)) { + if (is_arm_CopyB(n)) { + fprintf(F, "size = %u\n", get_tarval_long(get_arm_value(n))); + } else { + if (mode_is_float(get_irn_mode(n))) { + fprintf(F, "float value = (%lf)\n", get_tarval_double(get_arm_value(n))); + } else if (mode_is_int(get_irn_mode(n))) { + long v = get_tarval_long(get_arm_value(n)); + fprintf(F, "long value = %ld (0x%08lx)\n", v, v); + } else if (mode_is_reference(get_irn_mode(n))) { + fprintf(F, "pointer\n"); + } else { + assert(0 && "unbehandelter Typ im const-Knoten"); + } + } + } + if (get_arm_proj_num(n) >= 0) { + fprintf(F, "proj_num = (%d)\n", get_arm_proj_num(n)); + } + /* TODO: dump all additional attributes */ + + fprintf(F, "=== arm attr end ===\n"); + /* end of: case dump_node_info_txt */ + break; + } + + + return bad; +} + + + +/*************************************************************************************************** + * _ _ _ __ _ _ _ _ + * | | | | | | / / | | | | | | | | + * __ _| |_| |_ _ __ ___ ___| |_ / /_ _ ___| |_ _ __ ___ ___| |_| |__ ___ __| |___ + * / _` | __| __| '__| / __|/ _ \ __| / / _` |/ _ \ __| | '_ ` _ \ / _ \ __| '_ \ / _ \ / _` / __| + * | (_| | |_| |_| | \__ \ __/ |_ / / (_| | __/ |_ | | | | | | __/ |_| | | | (_) | (_| \__ \ + * \__,_|\__|\__|_| |___/\___|\__/_/ \__, |\___|\__| |_| |_| |_|\___|\__|_| |_|\___/ \__,_|___/ + * __/ | + * |___/ + ***************************************************************************************************/ + +/** + * Wraps get_irn_generic_attr() as it takes no const ir_node, so we need to do a cast. + * Firm was made by people hating const :-( + */ +arm_attr_t *get_arm_attr(const ir_node *node) { + assert(is_arm_irn(node) && "need arm node to get attributes"); + return (arm_attr_t *)get_irn_generic_attr((ir_node *)node); +} + +/** + * Returns the argument register requirements of a arm node. + */ +const arm_register_req_t **get_arm_in_req_all(const ir_node *node) { + arm_attr_t *attr = get_arm_attr(node); + return attr->in_req; +} + +/** + * Returns the result register requirements of an arm node. + */ +const arm_register_req_t **get_arm_out_req_all(const ir_node *node) { + arm_attr_t *attr = get_arm_attr(node); + return attr->out_req; +} + +/** + * Returns the argument register requirement at position pos of an arm node. + */ +const arm_register_req_t *get_arm_in_req(const ir_node *node, int pos) { + arm_attr_t *attr = get_arm_attr(node); + return attr->in_req[pos]; +} + +/** + * Returns the result register requirement at position pos of an arm node. + */ +const arm_register_req_t *get_arm_out_req(const ir_node *node, int pos) { + arm_attr_t *attr = get_arm_attr(node); + return attr->out_req[pos]; +} + +/** + * Sets the OUT register requirements at position pos. + */ +void set_arm_req_out(ir_node *node, const arm_register_req_t *req, int pos) { + arm_attr_t *attr = get_arm_attr(node); + attr->out_req[pos] = req; +} + +/** + * Sets the complete OUT requirements of node. + */ +void set_arm_req_out_all(ir_node *node, const arm_register_req_t **reqs) { + arm_attr_t *attr = get_arm_attr(node); + attr->out_req = reqs; +} + +/** + * Sets the IN register requirements at position pos. + */ +void set_arm_req_in(ir_node *node, const arm_register_req_t *req, int pos) { + arm_attr_t *attr = get_arm_attr(node); + attr->in_req[pos] = req; +} + +/** + * Returns the register flag of an arm node. + */ +arch_irn_flags_t get_arm_flags(const ir_node *node) { + arm_attr_t *attr = get_arm_attr(node); + return attr->flags; +} + +/** + * Sets the register flag of an arm node. + */ +void set_arm_flags(const ir_node *node, arch_irn_flags_t flags) { + arm_attr_t *attr = get_arm_attr(node); + attr->flags = flags; +} + +/** + * Returns the result register slots of an arm node. + */ +const arch_register_t **get_arm_slots(const ir_node *node) { + arm_attr_t *attr = get_arm_attr(node); + return attr->slots; +} + +/** + * Returns the name of the OUT register at position pos. + */ +const char *get_arm_out_reg_name(const ir_node *node, int pos) { + arm_attr_t *attr = get_arm_attr(node); + + assert(is_arm_irn(node) && "Not an arm node."); + assert(pos < attr->n_res && "Invalid OUT position."); + assert(attr->slots[pos] && "No register assigned"); + + return arch_register_get_name(attr->slots[pos]); +} + +/** + * Returns the index of the OUT register at position pos within its register class. + */ +int get_arm_out_regnr(const ir_node *node, int pos) { + arm_attr_t *attr = get_arm_attr(node); + + assert(is_arm_irn(node) && "Not an arm node."); + assert(pos < attr->n_res && "Invalid OUT position."); + assert(attr->slots[pos] && "No register assigned"); + + return arch_register_get_index(attr->slots[pos]); +} + +/** + * Returns the OUT register at position pos. + */ +const arch_register_t *get_arm_out_reg(const ir_node *node, int pos) { + arm_attr_t *attr = get_arm_attr(node); + + assert(is_arm_irn(node) && "Not an arm node."); + assert(pos < attr->n_res && "Invalid OUT position."); + assert(attr->slots[pos] && "No register assigned"); + + return attr->slots[pos]; +} + +/** + * Sets the number of results. + */ +void set_arm_n_res(ir_node *node, int n_res) { + arm_attr_t *attr = get_arm_attr(node); + attr->n_res = n_res; +} + +/** + * Returns the number of results. + */ +int get_arm_n_res(const ir_node *node) { + arm_attr_t *attr = get_arm_attr(node); + return attr->n_res; +} +/** + * Returns the tarvalue + */ +tarval *get_arm_value(const ir_node *node) { + arm_attr_t *attr = get_arm_attr(node); + return attr->value; +} + +/** + * Sets the tarvalue + */ +void set_arm_value(ir_node *node, tarval *tv) { + arm_attr_t *attr = get_arm_attr(node); + attr->value = tv; +} + +/** + * Returns the proj num + */ +int get_arm_proj_num(const ir_node *node) { + arm_attr_t *attr = get_arm_attr(node); + return attr->proj_num; +} + +/** + * Sets the proj num + */ +void set_arm_proj_num(ir_node *node, int proj_num) { + arm_attr_t *attr = get_arm_attr(node); + attr->proj_num = proj_num; +} + +/** + * Returns the SymConst label + */ +const char *get_arm_symconst_label(ir_node *node) { + arm_attr_t *attr = get_arm_attr(node); + return attr->symconst_label; +} + +/** + * Sets the SymConst label + */ +void set_arm_symconst_label(ir_node *node, const char *symconst_label) { + arm_attr_t *attr = get_arm_attr(node); + attr->symconst_label = symconst_label; +} + + +/** + * Returns the number of projs. + */ +int get_arm_n_projs(ir_node *node) { + arm_attr_t *attr = get_arm_attr(node); + return attr->n_projs; +} + +/** + * Sets the number of projs. + */ +void set_arm_n_projs(ir_node *node, int n_projs) { + arm_attr_t *attr = get_arm_attr(node); + attr->n_projs = n_projs; +} + +/** + * Returns the default_proj_num. + */ +long get_arm_default_proj_num(ir_node *node) { + arm_attr_t *attr = get_arm_attr(node); + return attr->default_proj_num; +} + +/** + * Sets the default_proj_num. + */ +void set_arm_default_proj_num(ir_node *node, long default_proj_num) { + arm_attr_t *attr = get_arm_attr(node); + attr->default_proj_num = default_proj_num; +} + + + +void init_arm_attributes(ir_node *node, int flags, const arm_register_req_t ** in_reqs, + const arm_register_req_t ** out_reqs, int n_res) { + arm_attr_t *attr = get_arm_attr(node); + attr->in_req = in_reqs; + attr->out_req = out_reqs; + attr->n_res = n_res; + attr->flags = flags; + attr->slots = xcalloc(n_res, sizeof(attr->slots[0])); + attr->value = NULL; + attr->proj_num = -42; + attr->symconst_label = NULL; + attr->n_projs = 0; + attr->default_proj_num = 0; +} + +static int arm_comp_condJmp(arm_attr_t *attr_a, arm_attr_t *attr_b) { + return 1; +} + +ir_node *arm_new_NoReg_gp(arm_code_gen_t *cg) { + return be_abi_get_callee_save_irn(cg->birg->abi, &arm_general_purpose_regs[REG_RXX]); +} + +ir_node *arm_new_NoReg_fp(arm_code_gen_t *cg) { + return be_abi_get_callee_save_irn(cg->birg->abi, &arm_floating_point_regs[REG_FXX]); +} + + + + +/*************************************************************************************** + * _ _ _ + * | | | | | | + * _ __ ___ __| | ___ ___ ___ _ __ ___| |_ _ __ _ _ ___| |_ ___ _ __ ___ + * | '_ \ / _ \ / _` |/ _ \ / __/ _ \| '_ \/ __| __| '__| | | |/ __| __/ _ \| '__/ __| + * | | | | (_) | (_| | __/ | (_| (_) | | | \__ \ |_| | | |_| | (__| || (_) | | \__ \ + * |_| |_|\___/ \__,_|\___| \___\___/|_| |_|___/\__|_| \__,_|\___|\__\___/|_| |___/ + * + ***************************************************************************************/ + +/* limit the possible registers for sp in arm_StoreStackM4Inc */ +static void limit_reg_arm_StoreStackM4Inc_sp(void *_unused, bitset_t *bs) { + bs = bitset_clear_all(bs); /* disallow all register (positive constraints given) */ + bitset_set(bs, 14); /* allow r13 */ + bitset_clear(bs, 13); /* disallow ignore reg r12 */ + bitset_clear(bs, 14); /* disallow ignore reg r13 */ + bitset_clear(bs, 15); /* disallow ignore reg r15 */ + bitset_clear(bs, 16); /* disallow ignore reg rxx */ + bitset_clear(bs, 17); /* disallow ignore reg MURX */ +} + +static const arm_register_req_t _arm_req_sp = { + { + arch_register_req_type_limited, + &arm_reg_classes[CLASS_arm_general_purpose], + limit_reg_arm_StoreStackM4Inc_sp, + NULL, /* limit environment */ + NULL, /* same node */ + NULL /* different node */ + }, + 0, + 0 +}; + +/* construct Store: Store(ptr, val, mem) = ST ptr,val */ +ir_node *new_r_arm_StoreStackMInc(ir_graph *irg, ir_node *block, ir_node *mem, ir_node *sp, + int n_regs, ir_node **regs, ir_mode *mode) { + ir_node *res; + ir_node *in[16]; + int flags = 0; + static const arm_register_req_t *_in_req_arm_StoreStackM4Inc[] = + { + &arm_default_req_none, + &_arm_req_sp, + &arm_default_req_arm_general_purpose, + &arm_default_req_arm_general_purpose, + &arm_default_req_arm_general_purpose, + &arm_default_req_arm_general_purpose, + &arm_default_req_arm_general_purpose, + &arm_default_req_arm_general_purpose, + &arm_default_req_arm_general_purpose, + &arm_default_req_arm_general_purpose, + &arm_default_req_arm_general_purpose, + &arm_default_req_arm_general_purpose, + &arm_default_req_arm_general_purpose, + &arm_default_req_arm_general_purpose, + &arm_default_req_arm_general_purpose, + &arm_default_req_arm_general_purpose, + }; + + assert(n_regs <= 15); + + in[0] = mem; + in[1] = sp; + memcpy(&in[2], regs, n_regs * sizeof(in[0])); + res = new_ir_node(NULL, irg, block, op_arm_StoreStackM4Inc, mode, 2 + n_regs, in); + flags |= arch_irn_flags_rematerializable; /* op can be easily recalculated */ + + /* init node attributes */ + init_arm_attributes(res, flags, _in_req_arm_StoreStackM4Inc, NULL, 0); + + res = optimize_node(res); + irn_vrfy_irg(res, irg); + + return res; +} + +/** + * Register additional opcodes here. + */ +static void arm_register_additional_opcodes(int cur_opcode) { +} + + +/* Include the generated constructor functions */ +#include "gen_arm_new_nodes.c.inl" diff --git a/ir/be/arm/arm_new_nodes.h b/ir/be/arm/arm_new_nodes.h new file mode 100644 index 000000000..1f899f648 --- /dev/null +++ b/ir/be/arm/arm_new_nodes.h @@ -0,0 +1,159 @@ +#ifndef _arm_NEW_NODES_H_ +#define _arm_NEW_NODES_H_ + +/** + * Function prototypes for the assembler ir node constructors. + * $Id$ + */ + +#include "arm_nodes_attr.h" +#include "bearch_arm_t.h" + +/*************************************************************************************************** + * _ _ _ __ _ _ _ _ + * | | | | | | / / | | | | | | | | + * __ _| |_| |_ _ __ ___ ___| |_ / /_ _ ___| |_ _ __ ___ ___| |_| |__ ___ __| |___ + * / _` | __| __| '__| / __|/ _ \ __| / / _` |/ _ \ __| | '_ ` _ \ / _ \ __| '_ \ / _ \ / _` / __| + * | (_| | |_| |_| | \__ \ __/ |_ / / (_| | __/ |_ | | | | | | __/ |_| | | | (_) | (_| \__ \ + * \__,_|\__|\__|_| |___/\___|\__/_/ \__, |\___|\__| |_| |_| |_|\___|\__|_| |_|\___/ \__,_|___/ + * __/ | + * |___/ + ***************************************************************************************************/ + +/** + * Returns the attributes of an arm node. + */ +arm_attr_t *get_arm_attr(const ir_node *node); + +/** + * Returns the argument register requirements of an arm node. + */ +const arm_register_req_t **get_arm_in_req_all(const ir_node *node); + +/** + * Returns the result register requirements of an arm node. + */ +const arm_register_req_t **get_arm_out_req_all(const ir_node *node); + +/** + * Returns the argument register requirements of an arm node. + */ +const arm_register_req_t *get_arm_in_req(const ir_node *node, int pos); + +/** + * Returns the result register requirements of an arm node. + */ +const arm_register_req_t *get_arm_out_req(const ir_node *node, int pos); + +/** + * Sets the OUT register requirements at position pos. + */ +void set_arm_req_out(ir_node *node, const arm_register_req_t *req, int pos); + +/** + * Sets the complete OUT requirements of node. + */ +void set_arm_req_out_all(ir_node *node, const arm_register_req_t **reqs); + +/** + * Sets the IN register requirements at position pos. + */ +void set_arm_req_in(ir_node *node, const arm_register_req_t *req, int pos); + +/** + * Returns the register flag of an arm node. + */ +arch_irn_flags_t get_arm_flags(const ir_node *node); + +/** + * Sets the register flag of an arm node. + */ +void set_arm_flags(const ir_node *node, arch_irn_flags_t flags); + +/** + * Returns the result register slots of an arm node. + */ +const arch_register_t **get_arm_slots(const ir_node *node); + +/** + * Returns the name of the OUT register at position pos. + */ +const char *get_arm_out_reg_name(const ir_node *node, int pos); + +/** + * Returns the index of the OUT register at position pos within its register class. + */ +int get_arm_out_regnr(const ir_node *node, int pos); + +/** + * Returns the OUT register at position pos. + */ +const arch_register_t *get_arm_out_reg(const ir_node *node, int pos); + +/** + * Sets the number of results. + */ +void set_arm_n_res(ir_node *node, int n_res); + +/** + * Returns the number of results. + */ +int get_arm_n_res(const ir_node *node); + +void init_arm_attributes(ir_node *node, int flags, const arm_register_req_t ** in_reqs, + const arm_register_req_t ** out_reqs, int n_res); + +ir_node *arm_new_NoReg_gp(arm_code_gen_t *cg); +ir_node *arm_new_NoReg_fp(arm_code_gen_t *cg); + +/** + * Returns the tarval + */ +tarval *get_arm_value(const ir_node *node); + +/** + * Sets the tarval + */ +void set_arm_value(ir_node *node, tarval *tv); + +/** + * Returns the proj num + */ +int get_arm_proj_num(const ir_node *node); + +/** + * Sets the proj num + */ +void set_arm_proj_num(ir_node *node, int proj_num); + +const char *get_arm_symconst_label(ir_node *node); +void set_arm_symconst_label(ir_node *node, const char *symconst_label); + +ir_node *new_r_arm_StoreStackMInc(ir_graph *irg, ir_node *block, ir_node *mem, ir_node *sp, + int n_regs, ir_node **regs, ir_mode *mode); + +/** + * Returns the number of projs. + */ +int get_arm_n_projs(ir_node *node); + +/** + * Sets the number of projs. + */ +void set_arm_n_projs(ir_node *node, int n_projs); + +/** + * Returns the default_proj_num. + */ +long get_arm_default_proj_num(ir_node *node); + +/** + * Sets the default_proj_num. + */ +void set_arm_default_proj_num(ir_node *node, long default_proj_num); + + +/* Include the generated headers */ +#include "gen_arm_new_nodes.h" + +#endif /* _arm_NEW_NODES_H_ */ diff --git a/ir/be/arm/arm_nodes_attr.h b/ir/be/arm/arm_nodes_attr.h new file mode 100644 index 000000000..b7e9de7e8 --- /dev/null +++ b/ir/be/arm/arm_nodes_attr.h @@ -0,0 +1,31 @@ +#ifndef _ARM_NODES_ATTR_H_ +#define _ARM_NODES_ATTR_H_ + +#include "../bearch.h" +#include "../../common/firm_types.h" + + +typedef struct _arm_register_req_t { + const arch_register_req_t req; + int same_pos; /**<< in case of "should be same" we need to remember the pos to get the irn */ + int different_pos; /**<< in case of "should be different" we need to remember the pos to get the irn */ +} arm_register_req_t; + + +typedef struct _arm_attr_t { + arch_irn_flags_t flags; /**<< indicating if spillable, rematerializeable ... etc. */ + int n_res; /**<< number of results for this node */ + + const arm_register_req_t **in_req; /**<< register requirements for arguments */ + const arm_register_req_t **out_req; /**<< register requirements for results */ + + const arch_register_t **slots; /**<< register slots for assigned registers */ + + tarval *value; + const char *symconst_label; + int proj_num; + int n_projs; + long default_proj_num; +} arm_attr_t; + +#endif /* _ARM_NODES_ATTR_H_ */ diff --git a/ir/be/arm/arm_spec.pl b/ir/be/arm/arm_spec.pl new file mode 100644 index 000000000..0fcb9ffbb --- /dev/null +++ b/ir/be/arm/arm_spec.pl @@ -0,0 +1,833 @@ +# Creation: 2006/02/13 +# $Id$ +# This is a template specification for the Firm-Backend + +# the cpu architecture (ia32, ia64, mips, sparc, ppc, ...) + +$arch = "arm"; + +# $comment_string = 'WIRHABENKEINEKOMMENTARE'; +$comment_string = '/*'; + +# the number of additional opcodes you want to register +$additional_opcodes = 0; + +# The node description is done as a perl hash initializer with the +# following structure: +# +# %nodes = ( +# +# => { +# "op_flags" => "N|L|C|X|I|F|Y|H|c|K", +# "irn_flags" => "R|N|I" +# "arity" => "0|1|2|3 ... |variable|dynamic|any", +# "state" => "floats|pinned|mem_pinned|exc_pinned", +# "args" => [ +# { "type" => "type 1", "name" => "name 1" }, +# { "type" => "type 2", "name" => "name 2" }, +# ... +# ], +# "comment" => "any comment for constructor", +# "reg_req" => { "in" => [ "reg_class|register" ], "out" => [ "reg_class|register|in_rX" ] }, +# "cmp_attr" => "c source code for comparing node attributes", +# "emit" => "emit code with templates", +# "rd_constructor" => "c source code which constructs an ir_node" +# }, +# +# ... # (all nodes you need to describe) +# +# ); # close the %nodes initializer + +# op_flags: flags for the operation, OPTIONAL (default is "N") +# the op_flags correspond to the firm irop_flags: +# N irop_flag_none +# L irop_flag_labeled +# C irop_flag_commutative +# X irop_flag_cfopcode +# I irop_flag_ip_cfopcode +# F irop_flag_fragile +# Y irop_flag_forking +# H irop_flag_highlevel +# c irop_flag_constlike +# K irop_flag_keep +# +# irn_flags: special node flags, OPTIONAL (default is 0) +# following irn_flags are supported: +# R rematerializeable +# N not spillable +# I ignore for register allocation +# +# state: state of the operation, OPTIONAL (default is "floats") +# +# arity: arity of the operation, MUST NOT BE OMITTED +# +# args: the OPTIONAL arguments of the node constructor (debug, irg and block +# are always the first 3 arguments and are always autmatically +# created) +# If this key is missing the following arguments will be created: +# for i = 1 .. arity: ir_node *op_i +# ir_mode *mode +# +# comment: OPTIONAL comment for the node constructor +# +# rd_constructor: for every operation there will be a +# new_rd__ function with the arguments from above +# which creates the ir_node corresponding to the defined operation +# you can either put the complete source code of this function here +# +# This key is OPTIONAL. If omitted, the following constructor will +# be created: +# if (!op__) assert(0); +# for i = 1 to arity +# set in[i] = op_i +# done +# res = new_ir_node(db, irg, block, op__, mode, arity, in) +# return res +# +# NOTE: rd_constructor and args are only optional if and only if arity is 0,1,2 or 3 + +# register types: +# 0 - no special type +# 1 - caller save (register must be saved by the caller of a function) +# 2 - callee save (register must be saved by the called function) +# 4 - ignore (do not assign this register) +# NOTE: Last entry of each class is the largest Firm-Mode a register can hold +%reg_classes = ( + "general_purpose" => [ + { "name" => "r0", "type" => 1 }, + { "name" => "r1", "type" => 1 }, + { "name" => "r2", "type" => 1 }, + { "name" => "r3", "type" => 1 }, + { "name" => "r4", "type" => 2 }, + { "name" => "r5", "type" => 2 }, + { "name" => "r6", "type" => 2 }, + { "name" => "r7", "type" => 2 }, + { "name" => "r8", "type" => 2 }, + { "name" => "r9", "type" => 2 }, + { "name" => "r10", "type" => 2 }, + { "name" => "r11", "type" => 2 }, + { "name" => "r12", "type" => 6 }, # reserved for linker + { "name" => "r13", "type" => 6 }, # this is our stack pointer + { "name" => "r14", "type" => 3 }, # this is our return address + { "name" => "r15", "type" => 6 }, # this is our program counter + { "name" => "rxx", "type" => 6 }, # dummy register for no_mem + { "name" => "MURX", "type" => 6 }, # this is our workaround-register + { "mode" => "mode_P" } + ], + "floating_point" => [ + { "name" => "f0", "type" => 1 }, + { "name" => "f1", "type" => 1 }, + { "name" => "f2", "type" => 1 }, + { "name" => "f3", "type" => 1 }, + { "name" => "f4", "type" => 2 }, + { "name" => "f5", "type" => 2 }, + { "name" => "f6", "type" => 2 }, + { "name" => "f7", "type" => 2 }, + { "name" => "fxx", "type" => 6 }, # dummy register for no_mem + { "mode" => "mode_D" } + ] +); # %reg_classes + +#--------------------------------------------------# +# _ # +# (_) # +# _ __ _____ __ _ _ __ ___ _ __ ___ # +# | '_ \ / _ \ \ /\ / / | | '__| / _ \| '_ \/ __| # +# | | | | __/\ V V / | | | | (_) | |_) \__ \ # +# |_| |_|\___| \_/\_/ |_|_| \___/| .__/|___/ # +# | | # +# |_| # +#--------------------------------------------------# + +%nodes = ( + +#-----------------------------------------------------------------# +# _ _ _ # +# (_) | | | | # +# _ _ __ | |_ ___ __ _ ___ _ __ _ __ ___ __| | ___ ___ # +# | | '_ \| __/ _ \/ _` |/ _ \ '__| | '_ \ / _ \ / _` |/ _ \/ __| # +# | | | | | || __/ (_| | __/ | | | | | (_) | (_| | __/\__ \ # +# |_|_| |_|\__\___|\__, |\___|_| |_| |_|\___/ \__,_|\___||___/ # +# __/ | # +# |___/ # +#-----------------------------------------------------------------# + +# commutative operations + +"Add" => { + "op_flags" => "C", + "irn_flags" => "R", + "comment" => "construct Add: Add(a, b) = Add(b, a) = a + b", + "reg_req" => { "in" => [ "general_purpose", "general_purpose" ], "out" => [ "general_purpose" ] }, + "emit" => '. ADD %D1, %S1, %S2\t\t\t/* Add(%S1, %S2) -> %D1, (%A1, %A2) */' +}, + +"Add_i" => { + "irn_flags" => "R", + "comment" => "construct Add: Add(a, const) = Add(const, a) = a + const", + "reg_req" => { "in" => [ "general_purpose" ], "out" => [ "general_purpose" ] }, + "emit" => '. ADD %D1, %S1, %C\t\t\t/* Add(%C, %S1) -> %D1, (%A1, const) */', + "cmp_attr" => +' if (attr_a->value == NULL || attr_b->value == NULL) + return 1; + + return get_tarval_long(attr_a->value) != get_tarval_long(attr_b->value); +' +}, + +"Mul" => { + #"op_flags" => "C", + "irn_flags" => "R", + "comment" => "construct Mul: Mul(a, b) = Mul(b, a) = a * b", + "reg_req" => { "in" => [ "general_purpose", "general_purpose" ], "out" => [ "!in_r1" ] }, + "emit" =>'. MUL %D1, %S1, %S2\t\t\t/* Mul(%S1, %S2) -> %D1, (%A1, %A2) */' +}, + +"And" => { + "op_flags" => "C", + "irn_flags" => "R", + "comment" => "construct And: And(a, b) = And(b, a) = a AND b", + "reg_req" => { "in" => [ "general_purpose", "general_purpose" ], "out" => [ "general_purpose" ] }, + "emit" => '. AND %D1, %S1, %S2\t\t/* And(%S1, %S2) -> %D1, (%A1, %A2) */' +}, + +"And_i" => { + "irn_flags" => "R", + "comment" => "construct And: And(a, const) = And(const, a) = a AND const", + "reg_req" => { "in" => [ "general_purpose" ], "out" => [ "general_purpose" ] }, + "emit" => '. AND %D1, %S1, %C\t\t\t/* And(%C, %S1) -> %D1, (%A1, const) */', + "cmp_attr" => +' if (attr_a->value == NULL || attr_b->value == NULL) + return 1; + + return get_tarval_long(attr_a->value) != get_tarval_long(attr_b->value); +' +}, + +"Or" => { + "op_flags" => "C", + "irn_flags" => "R", + "comment" => "construct Or: Or(a, b) = Or(b, a) = a OR b", + "reg_req" => { "in" => [ "general_purpose", "general_purpose" ], "out" => [ "general_purpose" ] }, + "emit" => '. ORR %D1, %S1, %S2\t\t/* Or(%S1, %S2) -> %D1, (%A1, %A2) */' +}, + +"Or_i" => { + "irn_flags" => "R", + "comment" => "construct Or: Or(a, const) = Or(const, a) = a OR const", + "reg_req" => { "in" => [ "general_purpose" ], "out" => [ "general_purpose" ] }, + "emit" => '. ORR %D1, %S1, %C\t\t\t/* Or(%C, %S1) -> %D1, (%A1, const) */', + "cmp_attr" => +' if (attr_a->value == NULL || attr_b->value == NULL) + return 1; + + return get_tarval_long(attr_a->value) != get_tarval_long(attr_b->value); +' +}, + +"Or_Shl_i" => { + "irn_flags" => "R", + "comment" => "construct Or with immediate shifter operand", + "reg_req" => { "in" => [ "general_purpose", "general_purpose" ], "out" => [ "general_purpose" ] }, + "emit" => '. ORR %D1, %S1, %S2 LSL %C\t\t\t/* Or_Shl_i(%S1, %S2 << %C) -> %D1, (%A1, const) */', + "cmp_attr" => +' if (attr_a->value == NULL || attr_b->value == NULL) + return 1; + + return get_tarval_long(attr_a->value) != get_tarval_long(attr_b->value); +' +}, + +"Eor" => { + "op_flags" => "C", + "irn_flags" => "R", + "comment" => "construct Eor: Eor(a, b) = Eor(b, a) = a EOR b", + "reg_req" => { "in" => [ "general_purpose", "general_purpose" ], "out" => [ "general_purpose" ] }, + "emit" => '. EOR %D1, %S1, %S2\t\t\t/* Xor(%S1, %S2) -> %D1, (%A1, %A2) */' +}, + +"Eor_i" => { + "irn_flags" => "R", + "comment" => "construct Eor: Eor(a, const) = Eor(const, a) = a EOR const", + "reg_req" => { "in" => [ "general_purpose" ], "out" => [ "general_purpose" ] }, + "emit" => '. EOR %D1, %S1, %C\t\t\t/* Xor(%C, %S1) -> %D1, (%A1, const) */', + "cmp_attr" => +' if (attr_a->value == NULL || attr_b->value == NULL) + return 1; + + return get_tarval_long(attr_a->value) != get_tarval_long(attr_b->value); +' +}, + +# not commutative operations + +"Sub" => { + "irn_flags" => "R", + "comment" => "construct Sub: Sub(a, b) = a - b", + "reg_req" => { "in" => [ "general_purpose", "general_purpose" ], "out" => [ "general_purpose" ] }, + "emit" => '. SUB %D1, %S1, %S2\t\t\t/* Sub(%S1, %S2) -> %D1, (%A1, %A2) */' +}, + +"Sub_i" => { + "irn_flags" => "R", + "comment" => "construct Sub: Sub(a, const) = a - const", + "reg_req" => { "in" => [ "general_purpose" ], "out" => [ "general_purpose" ] }, + "emit" => '. SUB %D1, %S1, %C\t\t\t/* Sub(%S1, %C) -> %D1, (%A1, const) */', + "cmp_attr" => +' if (attr_a->value == NULL || attr_b->value == NULL) + return 1; + + return get_tarval_long(attr_a->value) != get_tarval_long(attr_b->value); +' +}, + +"Shl" => { + "irn_flags" => "R", + "comment" => "construct Shl: Shl(a, b) = a << b", + "reg_req" => { "in" => [ "general_purpose", "general_purpose" ], "out" => [ "general_purpose" ] }, + "emit" => '. MOV %D1, %S1, LSL %S2\t/* Shl(%S1, %S2) -> %D1, (%A1, %A2) */' +}, + +"Shl_i" => { + "irn_flags" => "R", + "comment" => "construct Shl: Shl(a, const) = a << const", + "reg_req" => { "in" => [ "general_purpose" ], "out" => [ "general_purpose" ] }, + "emit" => '. MOV %D1, %S1, LSL %C\t\t\t/* Shl(%S1, %C) -> %D1, (%A1, const) */', + "cmp_attr" => +' if (attr_a->value == NULL || attr_b->value == NULL) + return 1; + + return get_tarval_long(attr_a->value) != get_tarval_long(attr_b->value); +' +}, + +"Shr" => { + "irn_flags" => "R", + "comment" => "construct Shr: Shr(a, b) = a >> b", + "reg_req" => { "in" => [ "general_purpose", "general_purpose" ], "out" => [ "in_r1" ] }, + "emit" => '. MOV %D1, %S1, LSR %S2\t\t\t/* Shr(%S1, %S2) -> %D1, (%A1, %A2) */' +}, + +"Shr_i" => { + "irn_flags" => "R", + "comment" => "construct Shr: Shr(a, const) = a >> const", + "reg_req" => { "in" => [ "general_purpose" ], "out" => [ "general_purpose" ] }, + "emit" => '. MOV %D1, %S1, LSR %C\t\t\t/* Shr(%S1, %C) -> %D1, (%A1, const) */', + "cmp_attr" => +' if (attr_a->value == NULL || attr_b->value == NULL) + return 1; + + return get_tarval_long(attr_a->value) != get_tarval_long(attr_b->value); +' +}, + +"Shrs" => { + "irn_flags" => "R", + "comment" => "construct Shrs: Shrs(a, b) = a >> b", + "reg_req" => { "in" => [ "general_purpose", "general_purpose" ], "out" => [ "in_r1" ] }, + "emit" => '. MOV %D1, %S1, ASR %S2\t\t\t\t\t/* Shrs(%S1, %S2) -> %D1, (%A1, %A2) */' +}, + +"Shrs_i" => { + "irn_flags" => "R", + "comment" => "construct Shrs: Shrs(a, const) = a >> const", + "reg_req" => { "in" => [ "general_purpose" ], "out" => [ "general_purpose" ] }, + "emit" => '. MOV %D1, %S1, ASR %C\t\t\t/* Shrs(%S1, %C) -> %D1, (%A1, const) */', + "cmp_attr" => +' if (attr_a->value == NULL || attr_b->value == NULL) + return 1; + + return get_tarval_long(attr_a->value) != get_tarval_long(attr_b->value); +' +}, + +#"Div" => { +# "irn_flags" => "R", +# "comment" => "construct Div: Div(a, b) = a / b", +# "reg_req" => { "in" => [ "general_purpose", "general_purpose" ], "out" => [ "general_purpose" ] }, +# "emit" =>'. div %S1, %S2, %D1\t\t\t/* div(%S1, %S2) -> %D1, (%A1, %A2) */' +#}, +# +#"Div_i" => { +# "irn_flags" => "R", +# "comment" => "construct Div: Div(a, const) = a / const", +# "reg_req" => { "in" => [ "general_purpose" ], "out" => [ "general_purpose" ] }, +# "emit" => '. div %S1, %C, %D1\t\t\t/* signed div(%C, %S1) -> %D1, (%A1, const) */' +#}, +# +#"Mod" => { +# "irn_flags" => "R", +# "comment" => "construct Mod: Mod(a, b) = a % b", +# "reg_req" => { "in" => [ "general_purpose", "general_purpose" ], "out" => [ "general_purpose" ] }, +# "emit" =>'. mod %S1, %S2, %D1\t\t\t/* mod(%S1, %S2) -> %D1, (%A1, %A2) */' +#}, +# +#"Mod_i" => { +# "irn_flags" => "R", +# "comment" => "construct mod: Mod(a, const) = a % const", +# "reg_req" => { "in" => [ "general_purpose" ], "out" => [ "general_purpose" ] }, +# "emit" => '. mod %S1, %C, %D1\t\t\t/* mod(%C, %S1) -> %D1, (%A1, const) */' +#}, +# +#"DivMod" => { +# "irn_flags" => "R", +# "comment" => "construct DivMod: DivMod(a, b) = a / b", +# "reg_req" => { "in" => [ "general_purpose", "general_purpose" ], "out" => [ "general_purpose" ] }, +# "emit" =>'. div %S1, %S2, %D1\t\t\t/* div(%S1, %S2) -> %D1, (%A1, %A2) */' +#}, +# +#"Div_i" => { +# "irn_flags" => "R", +# "comment" => "construct DivMod: DivMod(a, const) = a / const", +# "reg_req" => { "in" => [ "general_purpose" ], "out" => [ "general_purpose" ] }, +# "emit" => '. div %S1, %C, %D1\t\t\t/* signed div(%C, %S1) -> %D1, (%A1, const) */' +#}, + +#"RotR" => { +# "irn_flags" => "R", +# "comment" => "construct RotR: RotR(a, b) = a ROTR b", +# "reg_req" => { "in" => [ "general_purpose", "general_purpose" ], "out" => [ "general_purpose" ] }, +# "emit" => '. MOV %D1, %S1, ROR %S2\t\t\t/* RotR(%S1, %S2) -> %D1, (%A1, %A2) */' +## "emit" => '. ror %S1, %S2, %D1\t\t\t/* RotR(%S1, %S2) -> %D1, (%A1, %A2) */' +#}, + +#"RotL" => { +# "irn_flags" => "R", +# "comment" => "construct RotL: RotL(a, b) = a ROTL b", +# "reg_req" => { "in" => [ "general_purpose", "general_purpose" ], "out" => [ "general_purpose" ] }, +# "emit" => '. rol %S1, %S2, %D1\t\t\t/* RotL(%S1, %S2) -> %D1, (%A1, %A2) */' +#}, + +#"RotL_i" => { +# "irn_flags" => "R", +# "comment" => "construct RotL: RotL(a, const) = a ROTL const", +# "reg_req" => { "in" => [ "general_purpose" ], "out" => [ "general_purpose" ] }, +# "emit" => '. rol %S1, %C, %D1\t\t\t/* RotL(%S1, %C) -> %D1, (%A1, const) */' +#}, + +"Minus" => { + "irn_flags" => "R", + "comment" => "construct Minus: Minus(a) = -a", + "reg_req" => { "in" => [ "general_purpose" ], "out" => [ "general_purpose" ] }, + "emit" => '. RSB %D1, %S1, #0\t\t\t/* Neg(%S1) -> %D1, (%A1) */' +}, + +#"Inc" => { +# "irn_flags" => "R", +# "comment" => "construct Increment: Inc(a) = a++", +# "reg_req" => { "in" => [ "general_purpose" ], "out" => [ "general_purpose" ] }, +# "emit" => '. inc %S1, %D1\t\t\t/* Inc(%S1) -> %D1, (%A1) */' +#}, +# +#"Dec" => { +# "irn_flags" => "R", +# "comment" => "construct Decrement: Dec(a) = a--", +# "reg_req" => { "in" => [ "general_purpose" ], "out" => [ "general_purpose" ] }, +# "emit" => '. dec %S1, %D1\t\t\t/* Dec(%S1) -> %D1, (%A1) */' +#}, + +"Not" => { + "arity" => 1, +# "remat" => 1, + "irn_flags" => "R", + "comment" => "construct Not: Not(a) = !a", + "reg_req" => { "in" => [ "general_purpose" ], "out" => [ "general_purpose" ] }, + "emit" => '. MVN %D1, %S1\t\t\t/* Not(%S1) -> %D1, (%A1) */' +}, + +"Abs" => { + "irn_flags" => "R", + "comment" => "construct Abs: Abs(a) = |a|", + "reg_req" => { "in" => [ "general_purpose" ], "out" => [ "general_purpose" ] }, + "emit" => +'. MOVS %S1, %S1, #0\t\t\t/* set condition flag */\n +. RSBMI %D1, %S1, #0\t\t\t/* Neg(%S1) -> %D1, (%A1) */' +}, + +# other operations + +"EmptyReg" => { + "op_flags" => "c", + "irn_flags" => "R", + "comment" => "just to get an emptz register for calculations", + "reg_req" => { "out" => [ "general_purpose" ] }, + "emit" => '. /* %D1 now available for calculations */', + "cmp_attr" => 'return 1;' +}, + +"Copy" => { + "comment" => "implements a register copy", + "reg_req" => { "in" => [ "general_purpose" ], "out" => [ "general_purpose" ] }, +}, + +"CopyB" => { + "op_flags" => "F|H", + "state" => "pinned", + "comment" => "implements a memcopy: CopyB(dst, src, size, mem) == memcpy(dst, src, size)", + "reg_req" => { "in" => [ "!r13", "!r13", "general_purpose", "general_purpose", "general_purpose", "none" ], "out" => [ "none" ] }, +}, + +"Const" => { + "irn_flags" => "R", + "comment" => "represents an integer constant", + "reg_req" => { "out" => [ "general_purpose" ] }, + "emit" => '. MOV %D1, %C \t\t\t/* Mov Const into register */', + "cmp_attr" => +' if (attr_a->value == NULL || attr_b->value == NULL) + return 1; + + return get_tarval_long(attr_a->value) != get_tarval_long(attr_b->value); +' +}, + +"Const_Neg" => { + "irn_flags" => "R", + "comment" => "represents a negated integer constant", + "reg_req" => { "out" => [ "general_purpose" ] }, + "emit" => '. MVN %D1, %C \t\t\t/* Mov negated Const into register */', + "cmp_attr" => +' if (attr_a->value == NULL || attr_b->value == NULL) + return 1; + + return get_tarval_long(attr_a->value) != get_tarval_long(attr_b->value); +' +}, + +"SymConst" => { + "op_flags" => "c", + "irn_flags" => "R", + "comment" => "represents a symbolic constant", + "reg_req" => { "out" => [ "general_purpose" ] }, +# "emit" => '. LDR %D1, %C\t\t\t/* Mov Const into register */', + "cmp_attr" => +' if (attr_a->value == NULL || attr_b->value == NULL) + return 1; + + return get_tarval_long(attr_a->value) != get_tarval_long(attr_b->value); +' +}, + +"CondJmp" => { + "op_flags" => "L|X|Y", + "comment" => "construct conditional jump: CMP A, B && JMPxx LABEL", + "cmp_attr" => " return arm_comp_condJmp(attr_a, attr_b);\n", + "reg_req" => { "in" => [ "general_purpose", "general_purpose" ], "out" => [ "none", "none"] }, +}, + +"SwitchJmp" => { + "op_flags" => "L|X|Y", + "comment" => "construct switch", + "reg_req" => { "in" => [ "general_purpose" ], "out" => [ "none" ] }, + "cmp_attr" => " return 0;\n", +}, + +# Load / Store + +"Load" => { + "op_flags" => "L|F", + "irn_flags" => "R", + "state" => "exc_pinned", + "comment" => "construct Load: Load(ptr, mem) = LD ptr -> reg", + "reg_req" => { "in" => [ "general_purpose", "none" ], "out" => [ "general_purpose" ] }, + "emit" => '. LDR %D1, [%S1, #0]\t\t\t/* Load((%S1)) -> %D1, (%A1) */' +# "emit" => '. LDR %D1, %S1, %O\t\t\t/* Load((%S1)) -> %D1, (%A1) */' +}, + +"Loadb" => { + "op_flags" => "L|F", + "irn_flags" => "R", + "state" => "exc_pinned", + "comment" => "construct Load: Load(ptr, mem) = LD ptr -> reg", + "reg_req" => { "in" => [ "general_purpose", "none" ], "out" => [ "general_purpose" ] }, + "emit" => '. LDRB %D1, [%S1, #0]\t\t\t/* Load((%S1)) -> %D1, (%A1) */' +# "emit" => '. LDRB %D1, %S1, %O\t\t\t/* Load((%S1)) -> %D1, (%A1) */' +}, + +"Loadbs" => { + "op_flags" => "L|F", + "irn_flags" => "R", + "state" => "exc_pinned", + "comment" => "construct Load: Load(ptr, mem) = LD ptr -> reg", + "reg_req" => { "in" => [ "general_purpose", "none" ], "out" => [ "general_purpose" ] }, + "emit" => '. LDRSB %D1, [%S1, #0]\t\t\t/* Load((%S1)) -> %D1, (%A1) */' +# "emit" => '. LDRSB %D1, %S1, %O\t\t\t/* Load((%S1)) -> %D1, (%A1) */' +}, + +"Loadh" => { + "op_flags" => "L|F", + "irn_flags" => "R", + "state" => "exc_pinned", + "comment" => "construct Load: Load(ptr, mem) = LD ptr -> reg", + "reg_req" => { "in" => [ "general_purpose", "none" ], "out" => [ "general_purpose" ] }, + "emit" => '. LDRH %D1, [%S1, #0]\t\t\t/* Load((%S1)) -> %D1, (%A1) */' +# "emit" => '. LDRH %D1, %S1, %O\t\t\t/* Load((%S1)) -> %D1, (%A1) */' +}, + +"Loadhs" => { + "op_flags" => "L|F", + "irn_flags" => "R", + "state" => "exc_pinned", + "comment" => "construct Load: Load(ptr, mem) = LD ptr -> reg", + "reg_req" => { "in" => [ "general_purpose", "none" ], "out" => [ "general_purpose" ] }, + "emit" => '. LDRSH %D1, [%S1, #0]\t\t\t/* Load((%S1)) -> %D1, (%A1) */' +# "emit" => '. LDRSH %D1, %S1, %O\t\t\t/* Load((%S1)) -> %D1, (%A1) */' +}, + +"Storeb" => { + "op_flags" => "L|F", + "irn_flags" => "R", + "state" => "exc_pinned", + "comment" => "construct Store: Store(ptr, val, mem) = ST ptr,val", + "reg_req" => { "in" => [ "general_purpose", "general_purpose", "none" ] }, + "emit" => '. STRB %S2, [%S1, #0]\t\t\t/* Store(%S2) -> (%S1), (%A1, %A2) */' +# "emit" => '. movl %S2, %O(%S1)\t\t\t/* Store(%S2) -> (%S1), (%A1, %A2) */' +}, + +"Storebs" => { + "op_flags" => "L|F", + "irn_flags" => "R", + "state" => "exc_pinned", + "comment" => "construct Store: Store(ptr, val, mem) = ST ptr,val", + "reg_req" => { "in" => [ "general_purpose", "general_purpose", "none" ] }, + "emit" => '. STRSB %S2, [%S1, #0]\t\t\t/* Store(%S2) -> (%S1), (%A1, %A2) */' +# "emit" => '. movl %S2, %O(%S1)\t\t\t/* Store(%S2) -> (%S1), (%A1, %A2) */' +}, + +"Storeh" => { + "op_flags" => "L|F", + "irn_flags" => "R", + "state" => "exc_pinned", + "comment" => "construct Store: Store(ptr, val, mem) = ST ptr,val", + "reg_req" => { "in" => [ "general_purpose", "general_purpose", "none" ] }, + "emit" => '. STRH %S2, [%S1, #0]\t\t\t/* Store(%S2) -> (%S1), (%A1, %A2) */' +# "emit" => '. movl %S2, %O(%S1)\t\t\t/* Store(%S2) -> (%S1), (%A1, %A2) */' +}, + +"Storehs" => { + "op_flags" => "L|F", + "irn_flags" => "R", + "state" => "exc_pinned", + "comment" => "construct Store: Store(ptr, val, mem) = ST ptr,val", + "reg_req" => { "in" => [ "general_purpose", "general_purpose", "none" ] }, + "emit" => '. STRSH%S2, [%S1, #0]\t\t\t/* Store(%S2) -> (%S1), (%A1, %A2) */' +# "emit" => '. movl %S2, %O(%S1)\t\t\t/* Store(%S2) -> (%S1), (%A1, %A2) */' +}, + +"Store" => { + "op_flags" => "L|F", + "irn_flags" => "R", + "state" => "exc_pinned", + "comment" => "construct Store: Store(ptr, val, mem) = ST ptr,val", + "reg_req" => { "in" => [ "general_purpose", "general_purpose", "none" ] }, + "emit" => '. STR %S2, [%S1, #0]\t\t\t/* Store(%S2) -> (%S1), (%A1, %A2) */' +# "emit" => '. movl %S2, %O(%S1)\t\t\t/* Store(%S2) -> (%S1), (%A1, %A2) */' +}, + +"StoreStackM4Inc" => { + "op_flags" => "L|F", + "irn_flags" => "R", + "state" => "exc_pinned", + "comment" => "construct Store: Store(ptr, val, mem) = ST ptr,val", + "reg_req" => { "in" => [ "r13", "general_purpose", "general_purpose", "general_purpose", "general_purpose", "none" ], "out" => [ "general_purpose", "none" ] }, + "emit" => '. STMFD %S1!, {%S2, %S3, %S4, %S5}\t\t\t/* Store multiple on Stack*/' +}, + +"LoadStackM3" => { + "op_flags" => "L|F", + "irn_flags" => "R", + "state" => "exc_pinned", + "comment" => "construct Load: Load(ptr, mem) = LD ptr -> reg", + "reg_req" => { "in" => [ "r13", "none" ], "out" => [ "general_purpose", "general_purpose", "general_purpose", "none" ] }, + "emit" => '. LDMFD %S1, {%D1, %D2, %D3}\t\t\t/* Load multiple from Stack */' +}, + + + + + + + + +#--------------------------------------------------------# +# __ _ _ _ # +# / _| | | | | | # +# | |_| | ___ __ _| |_ _ __ ___ __| | ___ ___ # +# | _| |/ _ \ / _` | __| | '_ \ / _ \ / _` |/ _ \/ __| # +# | | | | (_) | (_| | |_ | | | | (_) | (_| | __/\__ \ # +# |_| |_|\___/ \__,_|\__| |_| |_|\___/ \__,_|\___||___/ # +#--------------------------------------------------------# + +# commutative operations + +"fAddd" => { + "op_flags" => "C", + "irn_flags" => "R", + "comment" => "construct DP FP Add: Add(a, b) = Add(b, a) = a + b", + "reg_req" => { "in" => [ "floating_point", "floating_point" ], "out" => [ "floating_point" ] }, + "emit" => '. FADDD %D1, %S1, %S2\t\t\t/* FP Add(%S1, %S2) -> %D1 */' +}, + +"fAdds" => { + "op_flags" => "C", + "irn_flags" => "R", + "comment" => "construct SP FP Add: Add(a, b) = Add(b, a) = a + b", + "reg_req" => { "in" => [ "floating_point", "floating_point" ], "out" => [ "floating_point" ] }, + "emit" => '. FADDS %D1, %S1, %S2\t\t\t/* FP Add(%S1, %S2) -> %D1 */' +}, + +"fMuls" => { + "op_flags" => "C", + "comment" => "construct FP Mul: Mul(a, b) = Mul(b, a) = a * b", + "reg_req" => { "in" => [ "floating_point", "floating_point" ], "out" => [ "floating_point" ] }, + "emit" =>'. FMULS %D1, %S1, %S2\t\t\t/* FP Mul(%S1, %S2) -> %D1 */' +}, + +"fMuld" => { + "op_flags" => "C", + "comment" => "construct FP Mul: Mul(a, b) = Mul(b, a) = a * b", + "reg_req" => { "in" => [ "floating_point", "floating_point" ], "out" => [ "floating_point" ] }, + "emit" =>'. FMULD %D1, %S1, %S2\t\t\t/* FP Mul(%S1, %S2) -> %D1 */' +}, + +"fDivs" => { + "comment" => "construct FP Div: Div(a, b) = a / b", + "reg_req" => { "in" => [ "floating_point", "floating_point" ], "out" => [ "floating_point" ] }, + "emit" =>'. FDIVS %D1, %S1, %S2\t\t\t/* FP Div(%S1, %S2) -> %D1 */' +}, + +"fDivd" => { + "comment" => "construct FP Div: Div(a, b) = a / b", + "reg_req" => { "in" => [ "floating_point", "floating_point" ], "out" => [ "floating_point" ] }, + "emit" =>'. FDIVD %D1, %S1, %S2\t\t\t/* FP Div(%S1, %S2) -> %D1 */' +}, + +"fDiv" => { + "comment" => "construct FP Div: Div(a, b) = a / b", + "reg_req" => { "in" => [ "floating_point", "floating_point" ], "out" => [ "floating_point" ] }, + "emit" =>'. FDIV %D1, %S1, %S2\t\t\t/* FP Div(%S1, %S2) -> %D1 */' +}, + +"fMax" => { + "op_flags" => "C", + "irn_flags" => "R", + "comment" => "construct FP Max: Max(a, b) = Max(b, a) = a > b ? a : b", + "reg_req" => { "in" => [ "floating_point", "floating_point" ], "out" => [ "floating_point" ] }, + "emit" =>'. fmax %S1, %S2, %D1\t\t\t/* FP Max(%S1, %S2) -> %D1 */' +}, + +"fMin" => { + "op_flags" => "C", + "irn_flags" => "R", + "comment" => "construct FP Min: Min(a, b) = Min(b, a) = a < b ? a : b", + "reg_req" => { "in" => [ "floating_point", "floating_point" ], "out" => [ "floating_point" ] }, + "emit" =>'. fmin %S1, %S2, %D1\t\t\t/* FP Min(%S1, %S2) -> %D1 */' +}, + +# not commutative operations + +"fSubs" => { + "irn_flags" => "R", + "comment" => "construct FP Sub: Sub(a, b) = a - b", + "reg_req" => { "in" => [ "floating_point", "floating_point" ], "out" => [ "floating_point" ] }, + "emit" => '. FSUBS %D1, %S1, %S2\t\t\t/* FP Sub(%S1, %S2) -> %D1 */' +}, + +"fSubd" => { + "irn_flags" => "R", + "comment" => "construct FP Sub: Sub(a, b) = a - b", + "reg_req" => { "in" => [ "floating_point", "floating_point" ], "out" => [ "floating_point" ] }, + "emit" => '. FSUBD %D1, %S1, %S2\t\t\t/* FP Sub(%S1, %S2) -> %D1 */' +}, + +"fMinus" => { + "irn_flags" => "R", + "comment" => "construct FP Minus: Minus(a) = -a", + "reg_req" => { "in" => [ "floating_point" ], "out" => [ "floating_point" ] }, + "emit" => '. fneg %S1, %D1\t\t\t/* FP Minus(%S1) -> %D1 */' +}, + +"fAbss" => { + "irn_flags" => "R", + "comment" => "construct FP Absolute value: fAbss(a) = |a|", + "reg_req" => { "in" => [ "floating_point" ], "out" => [ "floating_point" ] }, + "emit" => '. FABSS %D1, %S1\t\t\t/* FP Abss(%S1) -> %D1 */' +}, + +"fAbsd" => { + "irn_flags" => "R", + "comment" => "construct FP Absolute value: fAbsd(a) = |a|", + "reg_req" => { "in" => [ "floating_point" ], "out" => [ "floating_point" ] }, + "emit" => '. FABSD %D1, %S1\t\t\t/* FP Absd(%S1) -> %D1 */' +}, + +# other operations + +"fConst" => { + "op_flags" => "c", + "irn_flags" => "R", + "comment" => "represents a FP constant", + "reg_req" => { "out" => [ "floating_point" ] }, + "emit" => '. FMOV %D1, %C\t\t\t/* Mov fConst into register */', + "cmp_attr" => +' if (attr_a->value == NULL || attr_b->value == NULL) + return 1; + + return get_tarval_double(attr_a->value) != get_tarval_double(attr_b->value);' +}, + +"fConvD2S" => { + "irn_flags" => "R", + "comment" => "convert double to single", + "reg_req" => { "in" => [ "floating_point" ], "out" => [ "floating_point" ] }, + "emit" => '. FCVTSD %D1, %S1\t\t\t/* Convert double to single */', +}, + +"fConvS2D" => { + "irn_flags" => "R", + "comment" => "convert single to double", + "reg_req" => { "in" => [ "floating_point" ], "out" => [ "floating_point" ] }, + "emit" => '. FCVTDS %D1, %S1\t\t\t/* Convert single to double */', +}, + + +# Load / Store + +"fLoads" => { + "op_flags" => "L|F", + "irn_flags" => "R", + "state" => "exc_pinned", + "comment" => "construct FP Load: Load(ptr, mem) = LD ptr", + "reg_req" => { "in" => [ "general_purpose", "none" ], "out" => [ "floating_point" ] }, + "emit" => '. FLDS %D1, %S1\t\t\t/* Load((%S1)) -> %D1 */' +# "emit" => '. fmov %O(%S1), %D1\t\t\t/* Load((%S1)) -> %D1 */' +}, + +"fLoadd" => { + "op_flags" => "L|F", + "irn_flags" => "R", + "state" => "exc_pinned", + "comment" => "construct FP Load: Load(ptr, mem) = LD ptr", + "reg_req" => { "in" => [ "general_purpose", "none" ], "out" => [ "floating_point" ] }, + "emit" => '. FLDD %D1, %S1\t\t\t/* Load((%S1)) -> %D1 */' +# "emit" => '. fmov %O(%S1), %D1\t\t\t/* Load((%S1)) -> %D1 */' +}, + +"fStores" => { + "op_flags" => "L|F", + "irn_flags" => "R", + "state" => "exc_pinned", + "comment" => "construct Store: Store(ptr, val, mem) = ST ptr,val", + "reg_req" => { "in" => [ "general_purpose", "floating_point", "none" ] }, + "emit" => '. FSTS %S2, %S1\t\t\t/* Store(%S2) -> (%S1), (%A1, %A2) */' +}, + +"fStored" => { + "op_flags" => "L|F", + "irn_flags" => "R", + "state" => "exc_pinned", + "comment" => "construct Store: Store(ptr, val, mem) = ST ptr,val", + "reg_req" => { "in" => [ "general_purpose", "floating_point", "none" ] }, + "emit" => '. FSTD %S2, %S1\t\t\t/* Store(%S2) -> (%S1), (%A1, %A2) */' +}, + +); # end of %nodes diff --git a/ir/be/arm/arm_transform.c b/ir/be/arm/arm_transform.c new file mode 100644 index 000000000..2775d616a --- /dev/null +++ b/ir/be/arm/arm_transform.c @@ -0,0 +1,1064 @@ +/* The codegenrator (transform FIRM into arm FIRM */ +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "irnode_t.h" +#include "irgraph_t.h" +#include "irmode_t.h" +#include "irgmod.h" +#include "iredges.h" +#include "irvrfy.h" +#include "ircons.h" +#include "dbginfo.h" +#include "iropt_t.h" +#include "debug.h" + +#include "../benode_t.h" +#include "bearch_arm_t.h" + +#include "arm_nodes_attr.h" +#include "../arch/archop.h" /* we need this for Min and Max nodes */ +#include "arm_transform.h" +#include "arm_new_nodes.h" +#include "arm_map_regs.h" + +#include "gen_arm_regalloc_if.h" + +#include + +extern ir_op *get_op_Mulh(void); + + + +/**************************************************************************************************** + * _ _ __ _ _ + * | | | | / _| | | (_) + * _ __ ___ __| | ___ | |_ _ __ __ _ _ __ ___| |_ ___ _ __ _ __ ___ __ _| |_ _ ___ _ __ + * | '_ \ / _ \ / _` |/ _ \ | __| '__/ _` | '_ \/ __| _/ _ \| '__| '_ ` _ \ / _` | __| |/ _ \| '_ \ + * | | | | (_) | (_| | __/ | |_| | | (_| | | | \__ \ || (_) | | | | | | | | (_| | |_| | (_) | | | | + * |_| |_|\___/ \__,_|\___| \__|_| \__,_|_| |_|___/_| \___/|_| |_| |_| |_|\__,_|\__|_|\___/|_| |_| + * + ****************************************************************************************************/ + + + + + +typedef struct vals_ { + int ops; + unsigned char values[4]; + unsigned char shifts[4]; +} vals; + +static vals construct_vals() { + vals result = {0, {0, 0, 0, 0}, {0, 0, 0, 0}}; + return result; +} + +static vals gen_vals_from_word(unsigned int value) +{ + vals result = construct_vals(); + int cur_offset = 0; + while (value != 0) { + if ((value & 3) == 0) { + cur_offset += 2; + value >>= 2; + } else { + result.values[result.ops] = value & 0xff; + result.shifts[result.ops] = cur_offset; + ++result.ops; + value >>= 8; + cur_offset += 8; + } + } + return result; +} + +static ir_node *create_const_node(arm_transform_env_t *env, int value) { + ir_node *result = new_rd_arm_Const(env->dbg, env->irg, env->block, env->mode); + get_arm_attr(result)->value = new_tarval_from_long(value, env->mode); + return result; +} + +#define NEW_BINOP_NODE(opname, env, op1, op2) new_rd_arm_##opname(env->dbg, env->irg, env->block, op1, op2, env->mode) + +unsigned int create_shifter_operand(unsigned int shift, unsigned int immediate) { + return immediate | ((shift>>1)<<8); +} + +static ir_node *create_const_graph_value(arm_transform_env_t *env, unsigned int value) { + ir_node *result; + int negate = 0; + + if (value < 0x100 || ~value < 0x100 ) { + if ( ~value < 0x100 ) { + negate = 1; + value = ~value; + } + result = create_const_node(env, value); + } else { + vals v; + vals v1 = gen_vals_from_word(value); + vals v2 = gen_vals_from_word(~value); + int cnt = 0; + if (v2.ops == 1) { + result = new_rd_arm_Const_Neg(env->dbg, env->irg, env->block, env->mode); + set_arm_value(result, new_tarval_from_long(create_shifter_operand(v2.shifts[0], v2.values[0]), mode_Iu)); + } else { + if ( v2.ops < v1.ops ) { + negate = 1; + v = v2; + } else { + v = v1; + } + if (v.shifts[cnt] == 0) { + result = create_const_node(env, v.values[cnt]); + } else { + result = create_const_node(env, create_shifter_operand(v.shifts[cnt], v.values[cnt])); + } + ++cnt; + while(cnt < v.ops) { + ir_node *const_node = create_const_node(env, v.values[cnt]); + ir_node *orr_i_node = new_rd_arm_Or_i(env->dbg, env->irg, env->block, result, env->mode); + set_arm_value(orr_i_node, new_tarval_from_long(create_shifter_operand(v.shifts[cnt], v.values[cnt]), mode_Iu)); + result = orr_i_node; + ++cnt; + } + } + } + if ( negate ) { + result = new_rd_arm_Not(env->dbg, env->irg, env->block, result, env->mode); + } + return result; +} + +// static ir_node *create_const_graph_value(arm_transform_env_t *env, unsigned int value) { +// ir_node *irn = env->irn; +// ir_node *result; +// int negate = 0; +// +// if ( ~value < 0x100 ) { +// negate = 1; +// value = ~value; +// } +// +// if (value < 0x100) { +// return create_const_node(env, value); +// } else { +// vals v = gen_vals_from_word(value); +// int cnt = 0; +// ir_node *mov_val = create_const_node(env, v.values[cnt]); +// if (v.shifts[cnt] != 0) { +// ir_node *shift_node = new_rd_arm_Shl_i(env->dbg, env->irg, env->block, mov_val, env->mode); +// set_arm_value(shift_node, new_tarval_from_long(v.shifts[cnt], mode_Iu)); +// result = shift_node; +// } else { +// result = mov_val; +// } +// ++cnt; +// while(cnt < v.ops) { +// ir_node *const_node = create_const_node(env, v.values[cnt]); +// ir_node *orr_i_node = new_rd_arm_Or_i(env->dbg, env->irg, env->block, result, env->mode); +// unsigned shift = v.shifts[cnt]; +// unsigned immediate = v.values[cnt]; +// unsigned immediate_with_shift = immediate | ((shift>>1)<<8); +// set_arm_value(orr_i_node, new_tarval_from_long(immediate_with_shift, mode_Iu)); +// result = orr_i_node; +// ++cnt; +// } +// return result; +// } +// } + +static ir_node *create_const_graph(arm_transform_env_t *env) { + int value = get_tarval_long(get_Const_tarval(env->irn)); + return create_const_graph_value(env, value); +} + + + +static ir_node *gen_Const(arm_transform_env_t *env) { + ir_node *result; + assert(env->mode != mode_E && "IEEE Extended FP not supported"); + if (env->mode == mode_F) { + result = new_rd_arm_fConst(env->dbg, env->irg, env->block, env->mode); + get_arm_attr(result)->value = get_Const_tarval(env->irn); + } else if (env->mode == mode_D) { + result = new_rd_arm_fConst(env->dbg, env->irg, env->block, env->mode); + get_arm_attr(result)->value = get_Const_tarval(env->irn); + } else if (env->mode == mode_P) { + return env->irn; + } else { + result = create_const_graph(env); + } + return result; +} + +static ir_node *gen_mask(arm_transform_env_t *env, ir_node *op, int result_bits) { + unsigned mask_bits = (1 << result_bits) - 1; + ir_node *mask_node = create_const_graph_value(env, mask_bits); + return new_rd_arm_And(env->dbg, env->irg, env->block, op, mask_node, get_irn_mode(env->irn)); +} + +static ir_node *gen_sign_extension(arm_transform_env_t *env, ir_node *op, int result_bits) { + int shift_width = 32 - result_bits; + ir_node *shift_const_node = create_const_graph_value(env, shift_width); + ir_node *lshift_node = new_rd_arm_Shl(env->dbg, env->irg, env->block, op, shift_const_node, get_irn_mode(op)); + ir_node *rshift_node = new_rd_arm_Shrs(env->dbg, env->irg, env->block, lshift_node, shift_const_node, get_irn_mode(env->irn)); + return rshift_node; +} + +static ir_node *gen_Conv(arm_transform_env_t *env, ir_node *op) { + ir_mode *in_mode = get_irn_mode(op); + ir_mode *out_mode = env->mode; + + assert( in_mode != mode_E && ""); + assert( in_mode != mode_Ls && ""); + assert( in_mode != mode_Lu && ""); + assert( out_mode != mode_E && ""); + assert( out_mode != mode_Ls && ""); + assert( out_mode != mode_Lu && ""); + + if (in_mode == out_mode) + return op; + + if ((mode_is_int(in_mode) || mode_is_reference(in_mode)) + && (mode_is_reference(out_mode) || mode_is_int(out_mode))) { + int in_bits = get_mode_size_bits(in_mode); + int out_bits = get_mode_size_bits(out_mode); + int in_sign = get_mode_sign(in_mode); + int out_sign = get_mode_sign(out_mode); + + // 32 -> 32 + // NOPpen + if (in_bits == out_bits && in_bits == 32) + return op; + + // 16 -> 16 + // unsigned -> unsigned + // NOP + // unsigned -> signed + // sign extension (31:16)=(15) + // signed -> unsigned + // maskieren (31:16)=0 + // signed -> signed + // NOP + if (in_bits == out_bits && out_bits < 32) { + if (in_sign && !out_sign) { + return gen_mask(env, op, out_bits); + } else { + return gen_sign_extension(env, op, out_bits); + } + } + + // 16 -> 32 + // unsigned -> unsigned + // NOP + // unsigned -> signed + // NOP + // signed -> unsigned + // sign extension (31:16)=(15) + // signed -> signed + // sign extension (31:16)=(15) + if (in_bits < out_bits) { + if (in_sign) { + return gen_sign_extension(env, op, out_bits); + } else { + return op; + } + } + + // 32 -> 16 + // unsigned -> unsigned + // maskieren (31:16)=0 + // unsigned -> signed + // maskieren (31:16)=0 + // signed -> unsigned + // maskieren (31:16)=0 + // signed -> signed + // sign extension (erledigt auch maskieren) (31:16)=(15) + if (in_bits > out_bits) { + if (in_sign && out_sign) { + return gen_sign_extension(env, op, out_bits); + } else { + return gen_mask(env, op, out_bits); + } + } + assert(0 && "recheck integer conversion logic!"); + return env->irn; + } else if (in_mode == mode_D && out_mode == mode_F) { + return new_rd_arm_fConvD2S(env->dbg, env->irg, env->block, op, env->mode); + } else if (in_mode == mode_F && out_mode == mode_D) { + return new_rd_arm_fConvS2D(env->dbg, env->irg, env->block, op, env->mode); + } else if (mode_is_int(in_mode) && mode_is_float(out_mode)) { + return env->irn; /* TODO: implement int->float conversion*/ + } else if (mode_is_float(in_mode) && mode_is_int(out_mode)) { + return env->irn; /* TODO: implement float->int conversion*/ + } else { + assert(0 && "not implemented conversion"); + return env->irn; + } +} + + +/** + * Creates an arm Add. + * + * @param env The transformation environment + * @param op1 first operator + * @param op2 second operator + * @return the created arm Add node + */ +static ir_node *gen_Add(arm_transform_env_t *env, ir_node *op1, ir_node *op2) { + ir_node *result = NULL; + assert(!mode_is_float(env->mode) || (get_irn_mode(op1)->sort == get_irn_mode(op2)->sort)); + assert(env->mode != mode_E && "IEEE Extended FP not supported"); + + if (env->mode == mode_F) { + result = new_rd_arm_fAdds(env->dbg, env->irg, env->block, op1, op2, env->mode); + } else if (env->mode == mode_D) { + result = new_rd_arm_fAddd(env->dbg, env->irg, env->block, op1, op2, env->mode); + } else if (mode_is_numP(env->mode)) { + if (is_arm_Const(op1)) { + result = new_rd_arm_Add_i(env->dbg, env->irg, env->block, op2, env->mode); + set_arm_value(result, get_arm_value(op1)); + } else if (is_arm_Const(op2)) { + result = new_rd_arm_Add_i(env->dbg, env->irg, env->block, op1, env->mode); + set_arm_value(result, get_arm_value(op2)); + } else { + result = new_rd_arm_Add(env->dbg, env->irg, env->block, op1, op2, env->mode); + } + } else { + assert(0 && "unknown mode for add"); + } + return result; +} + + + +/** + * Creates an arm Mul. + * + * @param dbg firm node dbg + * @param block the block the new node should belong to + * @param op1 first operator + * @param op2 second operator + * @param mode node mode + * @return the created arm Mul node + */ +static ir_node *gen_Mul(arm_transform_env_t *env, ir_node *op1, ir_node *op2) { + assert(!mode_is_float(env->mode) || (get_irn_mode(op1)->sort == get_irn_mode(op2)->sort)); + assert(env->mode != mode_E && "IEEE Extended FP not supported"); + + if (env->mode == mode_F) { + return new_rd_arm_fMuls(env->dbg, env->irg, env->block, op1, op2, env->mode); + } + if (env->mode == mode_D) { + return new_rd_arm_fMuld(env->dbg, env->irg, env->block, op1, op2, env->mode); + } + return new_rd_arm_Mul(env->dbg, env->irg, env->block, op1, op2, env->mode); +} + + +/** + * Creates an arm floating Div. + * + * @param dbg firm node dbg + * @param block the block the new node should belong to + * @param op1 first operator + * @param op2 second operator + * @param mode node mode + * @return the created arm fDiv node + */ +static ir_node *gen_Quot(arm_transform_env_t *env, ir_node *op1, ir_node *op2) { + // assert(mode_is_float(env->mode) && "only floating point supported"); + assert(get_irn_mode(op1)->sort == get_irn_mode(op2)->sort); + assert(mode_is_float(get_irn_mode(op1))); + assert(get_irn_mode(op1) != mode_E && "IEEE Extended FP not supported"); + + if (get_irn_mode(op1) == mode_F) { + return new_rd_arm_fDivs(env->dbg, env->irg, env->block, op1, op2, env->mode); + } + return new_rd_arm_fDivd(env->dbg, env->irg, env->block, op1, op2, env->mode); +} + + +/** + * Creates an arm And. + * + * @param dbg firm node dbg + * @param block the block the new node should belong to + * @param op1 first operator + * @param op2 second operator + * @param mode node mode + * @return the created arm And node + */ +static ir_node *gen_And(arm_transform_env_t *env, ir_node *op1, ir_node *op2) { + ir_node *result; + if (is_arm_Const(op1)) { + result = new_rd_arm_And_i(env->dbg, env->irg, env->block, op2, env->mode); + set_arm_value(result, get_arm_value(op1)); + } else if (is_arm_Const(op2)) { + result = new_rd_arm_And_i(env->dbg, env->irg, env->block, op1, env->mode); + set_arm_value(result, get_arm_value(op2)); + } else { + result = new_rd_arm_And(env->dbg, env->irg, env->block, op1, op2, env->mode); + } + return result; +} + + + +/** + * Creates an arm Or. + * + * @param dbg firm node dbg + * @param block the block the new node should belong to + * @param op1 first operator + * @param op2 second operator + * @param mode node mode + * @return the created arm Or node + */ +static ir_node *gen_Or(arm_transform_env_t *env, ir_node *op1, ir_node *op2) { + ir_node *result; + if (is_arm_Const(op1)) { + result = new_rd_arm_Or_i(env->dbg, env->irg, env->block, op2, env->mode); + set_arm_value(result, get_arm_value(op1)); + } else if (is_arm_Const(op2)) { + result = new_rd_arm_Or_i(env->dbg, env->irg, env->block, op1, env->mode); + set_arm_value(result, get_arm_value(op2)); + } else { + result = new_rd_arm_Or(env->dbg, env->irg, env->block, op1, op2, env->mode); + } + return result; +} + + + +/** + * Creates an arm Eor. + * + * @param dbg firm node dbg + * @param block the block the new node should belong to + * @param op1 first operator + * @param op2 second operator + * @param mode node mode + * @return the created arm Eor node + */ +static ir_node *gen_Eor(arm_transform_env_t *env, ir_node *op1, ir_node *op2) { + ir_node *result; + if (is_arm_Const(op1)) { + result = new_rd_arm_Eor_i(env->dbg, env->irg, env->block, op2, env->mode); + set_arm_value(result, get_arm_value(op1)); + } else if (is_arm_Const(op2)) { + result = new_rd_arm_Eor_i(env->dbg, env->irg, env->block, op1, env->mode); + set_arm_value(result, get_arm_value(op2)); + } else { + result = new_rd_arm_Eor(env->dbg, env->irg, env->block, op1, op2, env->mode); + } + return result; +} + + + +/** + * Creates an arm Sub. + * + * @param dbg firm node dbg + * @param block the block the new node should belong to + * @param op1 first operator + * @param op2 second operator + * @param mode node mode + * @return the created arm Sub node + */ +static ir_node *gen_Sub(arm_transform_env_t *env, ir_node *op1, ir_node *op2) { + ir_node *result = NULL; + assert(!mode_is_float(env->mode) || (get_irn_mode(op1)->sort == get_irn_mode(op2)->sort)); + assert(env->mode != mode_E && "IEEE Extended FP not supported"); + + if (env->mode == mode_F) { + result = new_rd_arm_fSubs(env->dbg, env->irg, env->block, op1, op2, env->mode); + } else if (env->mode == mode_D) { + result = new_rd_arm_fSubd(env->dbg, env->irg, env->block, op1, op2, env->mode); + } else if (mode_is_numP(env->mode)) { + if (is_arm_Const(op2)) { + result = new_rd_arm_Sub_i(env->dbg, env->irg, env->block, op1, env->mode); + set_arm_value(result, get_arm_value(op2)); + } else { + result = new_rd_arm_Sub(env->dbg, env->irg, env->block, op1, op2, env->mode); + } + } else { + assert(0 && "unknown mode for sub"); + } + return result; +} + +/** + * Creates an arm Shl. + * + * @param dbg firm node dbg + * @param block the block the new node should belong to + * @param op1 first operator + * @param op2 second operator + * @param mode node mode + * @return the created arm Shl node + */ +static ir_node *gen_Shl(arm_transform_env_t *env, ir_node *op1, ir_node *op2) { + ir_node *result; + if (is_arm_Const(op2)) { + result = new_rd_arm_Shl_i(env->dbg, env->irg, env->block, op1, env->mode); + set_arm_value(result, get_arm_value(op2)); + } else { + result = new_rd_arm_Shl(env->dbg, env->irg, env->block, op1, op2, env->mode); + } + return result; +} + + + +/** + * Creates an arm Shr. + * + * @param dbg firm node dbg + * @param block the block the new node should belong to + * @param op1 first operator + * @param op2 second operator + * @param mode node mode + * @return the created arm Shr node + */ +static ir_node *gen_Shr(arm_transform_env_t *env, ir_node *op1, ir_node *op2) { + ir_node *result; + if (is_arm_Const(op2)) { + result = new_rd_arm_Shr_i(env->dbg, env->irg, env->block, op1, env->mode); + set_arm_value(result, get_arm_value(op2)); + } else { + result = new_rd_arm_Shr(env->dbg, env->irg, env->block, op1, op2, env->mode); + } + return result; +} + +/** + * Creates an arm Shrs. + * + * @param dbg firm node dbg + * @param block the block the new node should belong to + * @param op1 first operator + * @param op2 second operator + * @param mode node mode + * @return the created arm Shrs node + */ +static ir_node *gen_Shrs(arm_transform_env_t *env, ir_node *op1, ir_node *op2) { + ir_node *result; + if (is_arm_Const(op2)) { + result = new_rd_arm_Shrs_i(env->dbg, env->irg, env->block, op1, env->mode); + set_arm_value(result, get_arm_value(op2)); + } else { + result = new_rd_arm_Shrs(env->dbg, env->irg, env->block, op1, op2, env->mode); + } + return result; +} + +/** + * Transforms a Not node. + * + * @param mod the debug module + * @param block the block the new node should belong to + * @param node the ir Not node + * @param op operator + * @param mode node mode + * @return the created arm Not node + */ +static ir_node *gen_Not(arm_transform_env_t *env, ir_node *op) { + return new_rd_arm_Not(env->dbg, env->irg, env->block, op, env->mode); +} + + +static ir_node *gen_Abs(arm_transform_env_t *env, ir_node *op) { + assert(env->mode != mode_E && "IEEE Extended FP not supported"); + + if (env->mode == mode_F) { + return new_rd_arm_fAbss(env->dbg, env->irg, env->block, op, env->mode); + } + if (env->mode == mode_D) { + return new_rd_arm_fAbsd(env->dbg, env->irg, env->block, op, env->mode); + } + + return new_rd_arm_Abs(env->dbg, env->irg, env->block, op, env->mode); +} + + +/** + * Transforms a Minus node. + * + * @param mod the debug module + * @param block the block the new node should belong to + * @param node the ir Minus node + * @param op operator + * @param mode node mode + * @return the created arm Minus node + */ +static ir_node *gen_Minus(arm_transform_env_t *env, ir_node *op) { + if (mode_is_float(env->mode)) { + return new_rd_arm_fMinus(env->dbg, env->irg, env->block, op, env->mode); + } + return new_rd_arm_Minus(env->dbg, env->irg, env->block, op, env->mode); +} + + +/** + * Transforms a Load. + * + * @param mod the debug module + * @param block the block the new node should belong to + * @param node the ir Load node + * @param mode node mode + * @return the created arm Load node + */ +static ir_node *gen_Load(arm_transform_env_t *env) { + ir_node *node = env->irn; + ir_mode *mode = get_Load_mode(node); + +// const ir_edge_t *edge; +// +// foreach_out_edge(node, edge) { +// ir_node* proj = get_edge_src_irn(edge); +// long nr = get_Proj_proj(proj); +// if ( nr == pn_Load_res ) +// mode = proj->mode; +// } +// besser: get_Load_mode() verwenden! + + assert(mode!=NULL && "irgendwie hat das load kein proj fuers result"); + if (mode==mode_F) { + return new_rd_arm_fLoads(env->dbg, env->irg, env->block, get_Load_ptr(node), get_Load_mem(node), env->mode); + } + if (mode==mode_D) { + return new_rd_arm_fLoadd(env->dbg, env->irg, env->block, get_Load_ptr(node), get_Load_mem(node), env->mode); + } + if (mode == mode_Bu) { + return new_rd_arm_Loadb(env->dbg, env->irg, env->block, get_Load_ptr(node), get_Load_mem(node), env->mode); + } + if (mode == mode_Bs) { + return new_rd_arm_Loadbs(env->dbg, env->irg, env->block, get_Load_ptr(node), get_Load_mem(node), env->mode); + } + if (mode == mode_Hu) { + return new_rd_arm_Loadh(env->dbg, env->irg, env->block, get_Load_ptr(node), get_Load_mem(node), env->mode); + } + if (mode == mode_Hs) { + return new_rd_arm_Loadhs(env->dbg, env->irg, env->block, get_Load_ptr(node), get_Load_mem(node), env->mode); + } + if (mode_is_reference(mode)) { + return new_rd_arm_Load(env->dbg, env->irg, env->block, get_Load_ptr(node), get_Load_mem(node), env->mode); + } + return new_rd_arm_Load(env->dbg, env->irg, env->block, get_Load_ptr(node), get_Load_mem(node), env->mode); +} + + + +/** + * Transforms a Store. + * + * @param mod the debug module + * @param block the block the new node should belong to + * @param node the ir Store node + * @param mode node mode + * @return the created arm Store node + */ +static ir_node *gen_Store(arm_transform_env_t *env) { + ir_node *node = env->irn; + ir_mode *mode = get_irn_mode(get_Store_value(node)); + assert(env->mode != mode_E && "IEEE Extended FP not supported"); + + if (mode == mode_F) { + return new_rd_arm_fStores(env->dbg, env->irg, env->block, get_Store_ptr(node), get_Store_value(node), get_Store_mem(node), env->mode); + } + if (mode == mode_D) { + return new_rd_arm_fStored(env->dbg, env->irg, env->block, get_Store_ptr(node), get_Store_value(node), get_Store_mem(node), env->mode); + } + if (mode == mode_Bu) { + return new_rd_arm_Storeb(env->dbg, env->irg, env->block, get_Store_ptr(node), get_Store_value(node), get_Store_mem(node), env->mode); + } + if (mode == mode_Bs) { + return new_rd_arm_Storebs(env->dbg, env->irg, env->block, get_Store_ptr(node), get_Store_value(node), get_Store_mem(node), env->mode); + } + if (mode == mode_Hu) { + return new_rd_arm_Storeh(env->dbg, env->irg, env->block, get_Store_ptr(node), get_Store_value(node), get_Store_mem(node), env->mode); + } + if (mode == mode_Hs) { + return new_rd_arm_Storehs(env->dbg, env->irg, env->block, get_Store_ptr(node), get_Store_value(node), get_Store_mem(node), env->mode); + } + return new_rd_arm_Store(env->dbg, env->irg, env->block, get_Store_ptr(node), get_Store_value(node), get_Store_mem(node), env->mode); +} + + +static ir_node *gen_Cond(arm_transform_env_t *env) { + ir_node *result = NULL; + ir_node *selector = get_Cond_selector(env->irn); + ir_node *irn = env->irn; + if ( get_irn_mode(selector) == mode_b ) { + //CondJmp + ir_node *proj_node = get_Cond_selector(irn); + ir_mode *proj_mode = get_irn_mode(proj_node); + ir_node *cmp_node = get_Proj_pred(proj_node); + ir_node *op1 = get_Cmp_left(cmp_node); + ir_node *op2 = get_Cmp_right(cmp_node); + result = new_rd_arm_CondJmp(env->dbg, env->irg, env->block, op1, op2, mode_T); + set_arm_proj_num(result, get_Proj_proj(proj_node)); + } else { + //SwitchJmp + ir_node *op = get_irn_n(env->irn, 0); + ir_node *const_graph; + ir_node *sub; + ir_node *const_node; + + ir_node *proj; + const ir_edge_t *edge; + int min = INT_MAX; + int max = INT_MIN; + int translation; + int norm_max; + int norm_min; + int pn; + arm_transform_env_t const_env; + int n_projs; + ir_node **projs; + + foreach_out_edge(irn, edge) { + proj = get_edge_src_irn(edge); + assert(is_Proj(proj) && "Only proj allowed at SwitchJmp"); + + pn = get_Proj_proj(proj); + + min = pnmax ? pn : max; + } + translation = min; + norm_max = max - translation; + norm_min = min - translation; + + n_projs = norm_max + 1; + projs = xcalloc(n_projs , sizeof(ir_node*)); + + + foreach_out_edge(irn, edge) { + proj = get_edge_src_irn(edge); + assert(is_Proj(proj) && "Only proj allowed at SwitchJmp"); + + pn = get_Proj_proj(proj) - translation; + set_Proj_proj(proj, pn); + } + + + const_node = new_rd_Const(env->dbg, env->irg, env->block, mode_Iu, new_tarval_from_long(translation, mode_Iu)); + const_env = *env; + const_env.mode = mode_Is; + const_env.irn = const_node; + const_graph = gen_Const(&const_env); + sub = new_rd_arm_Sub(env->dbg, env->irg, env->block, op, const_graph, get_irn_mode(op)); + result = new_rd_arm_SwitchJmp(env->dbg, env->irg, env->block, sub, mode_T); + set_arm_n_projs(result, n_projs); + set_arm_default_proj_num(result, get_Cond_defaultProj(irn)-translation); + } + return result; +} + +/** + * Returns the name of a SymConst. + * @param symc the SymConst + * @return name of the SymConst + */ +const char *get_sc_name(ir_node *symc) { + if (get_irn_opcode(symc) != iro_SymConst) + return "NONE"; + + switch (get_SymConst_kind(symc)) { + case symconst_addr_name: + return get_id_str(get_SymConst_name(symc)); + + case symconst_addr_ent: + return get_entity_ld_name(get_SymConst_entity(symc)); + + default: + assert(0 && "Unsupported SymConst"); + } + + return NULL; +} + +static ir_node *gen_SymConst(arm_transform_env_t *env) { + ir_node *result; + const char *str = get_sc_name(env->irn); + result = new_rd_arm_SymConst(env->dbg, env->irg, env->block, env->mode); + set_arm_symconst_label(result, str); + return result; +} + + + + + +/** + * Transforms a CopyB node. + * + * @param env The transformation environment + * @return The transformed node. + */ +static ir_node *gen_CopyB(arm_transform_env_t *env) { + ir_node *res = NULL; + dbg_info *dbg = env->dbg; + ir_graph *irg = env->irg; + ir_mode *mode = env->mode; + ir_node *block = env->block; + ir_node *node = env->irn; + ir_node *src = get_CopyB_src(node); + ir_node *dst = get_CopyB_dst(node); + ir_node *mem = get_CopyB_mem(node); + int size = get_type_size_bytes(get_CopyB_type(node)); + ir_node *src_copy; + ir_node *dst_copy; + + arm_transform_env_t const_env; + const_env.block = block; + const_env.dbg = dbg; + const_env.irg = irg; + const_env.irn = node; + const_env.mod = env->mod; + const_env.mode = mode_Iu; + + src_copy = be_new_Copy(&arm_reg_classes[CLASS_arm_general_purpose], irg, block, src); + dst_copy = be_new_Copy(&arm_reg_classes[CLASS_arm_general_purpose], irg, block, dst); + + res = new_rd_arm_CopyB( dbg, irg, block, dst_copy, src_copy, new_rd_arm_EmptyReg(dbg, irg, block, mode_Iu), new_rd_arm_EmptyReg(dbg, irg, block, mode_Iu), new_rd_arm_EmptyReg(dbg, irg, block, mode_Iu), mem, mode); + set_arm_value(res, new_tarval_from_long(size, mode_Iu)); + + return res; +} + + + + + +// /************************************************************************/ +// /* be transforms */ +// /************************************************************************/ +// +// static ir_node *gen_be_Copy(arm_transform_env_t *env, ir_node *op) { +// return new_rd_arm_Copy(env->dbg, env->irg, env->block, op, env->mode); +// } + +/********************************************************* + * _ _ _ + * (_) | | (_) + * _ __ ___ __ _ _ _ __ __| |_ __ ___ _____ _ __ + * | '_ ` _ \ / _` | | '_ \ / _` | '__| \ \ / / _ \ '__| + * | | | | | | (_| | | | | | | (_| | | | |\ V / __/ | + * |_| |_| |_|\__,_|_|_| |_| \__,_|_| |_| \_/ \___|_| + * + *********************************************************/ + +/************************************************************************/ +/* move constants out of startblock */ +/************************************************************************/ +void arm_move_consts(ir_node *node, void *env) { + arm_code_gen_t *cgenv = (arm_code_gen_t *)env; + int i; + if (is_Block(node)) + return; + if (is_Phi(node)) { + for (i=0; imod; + tenv.mode = get_irn_mode(pred); + const_graph = create_const_graph(&tenv); + set_irn_n(node, i, const_graph); + } else if (pred_code == iro_SymConst) { + const char *str = get_sc_name(pred); + ir_node *symconst_node; + symconst_node = new_rd_arm_SymConst(get_irn_dbg_info(pred), + current_ir_graph, get_nodes_block(get_irn_n(get_nodes_block(node),i)), get_irn_mode(pred)); + set_arm_symconst_label(symconst_node, str); + set_irn_n(node, i, symconst_node); + } + } + return; + } + for (i=0; imod; + tenv.mode = get_irn_mode(pred); + const_graph = create_const_graph(&tenv); + set_irn_n(node, i, const_graph); + } else if (pred_code == iro_SymConst) { + const char *str = get_sc_name(pred); + ir_node *symconst_node; + symconst_node = new_rd_arm_SymConst(get_irn_dbg_info(pred), + current_ir_graph, get_nodes_block(node), get_irn_mode(pred)); + set_arm_symconst_label(symconst_node, str); + set_irn_n(node, i, symconst_node); + } + } +} + + +/************************************************************************/ +/* move symbolic constants out of startblock */ +/************************************************************************/ +void arm_move_symconsts(ir_node *node, void *env) { + arm_code_gen_t *cgenv = (arm_code_gen_t *)env; + int i; + if (is_Block(node)) + return; + for (i=0; imod; + tenv.mode = get_irn_mode(node); + +#define UNOP(a) case iro_##a: asm_node = gen_##a(&tenv, get_##a##_op(node)); break +#define BINOP(a) case iro_##a: asm_node = gen_##a(&tenv, get_##a##_left(node), get_##a##_right(node)); break +#define GEN(a) case iro_##a: asm_node = gen_##a(&tenv); break +#define IGN(a) case iro_##a: break +#define BAD(a) case iro_##a: goto bad + + DBG((tenv.mod, LEVEL_1, "check %+F ... ", node)); + + switch (code) { + BINOP(Add); // done + BINOP(Mul); // done + BINOP(Quot); // done + BINOP(And); // done + BINOP(Or); // done + BINOP(Eor); // done + + BINOP(Sub); // done + BINOP(Shl); // done + BINOP(Shr); // done + BINOP(Shrs); // done + + UNOP(Minus); // done + UNOP(Not); // done + UNOP(Abs); // done + + GEN(CopyB); // done + GEN(Const); // TODO: floating point consts + UNOP(Conv); // TODO: floating point conversions + + GEN(Load); // done + GEN(Store); // done + + GEN(SymConst); + GEN(Cond); // integer done + + /* TODO: implement these nodes */ + + IGN(Div); // intrinsic lowering + IGN(Mod); // intrinsic lowering + IGN(DivMod); // TODO: implement DivMod + + IGN(Mux); + IGN(Unknown); + IGN(Cmp); // done, implemented in cond + + /* You probably don't need to handle the following nodes */ + + IGN(Call); + IGN(Proj); + IGN(Alloc); + + IGN(Block); + IGN(Start); + IGN(End); + IGN(NoMem); + IGN(Phi); + IGN(IJmp); + IGN(Jmp); // emitter done + IGN(Break); + IGN(Sync); + + BAD(Raise); + BAD(Sel); + BAD(InstOf); + BAD(Cast); + BAD(Free); + BAD(Tuple); + BAD(Id); + BAD(Bad); + BAD(Confirm); + BAD(Filter); + BAD(CallBegin); + BAD(EndReg); + BAD(EndExcept); + + default: + if (get_irn_op(node) == get_op_Max() || + get_irn_op(node) == get_op_Min() || + get_irn_op(node) == get_op_Mulh()) + { + /* TODO: implement */ + /* ignore for now */ + } + break; +bad: + fprintf(stderr, "Not implemented: %s\n", get_irn_opname(node)); + assert(0); + } + + if (asm_node) { + exchange(node, asm_node); + DB((tenv.mod, LEVEL_1, "created node %+F[%p]\n", asm_node, asm_node)); + } + else { + DB((tenv.mod, LEVEL_1, "ignored\n")); + } +} diff --git a/ir/be/arm/arm_transform.h b/ir/be/arm/arm_transform.h new file mode 100644 index 000000000..b61ac20c6 --- /dev/null +++ b/ir/be/arm/arm_transform.h @@ -0,0 +1,8 @@ +#ifndef _ARM_TRANSFORM_H_ +#define _ARM_TRANSFORM_H_ + +void arm_move_consts(ir_node *node, void *env); +void arm_move_symconsts(ir_node *node, void *env); +void arm_transform_node(ir_node *node, void *env); + +#endif /* _ARM_TRANSFORM_H_ */ diff --git a/ir/be/arm/bearch_arm.c b/ir/be/arm/bearch_arm.c new file mode 100644 index 000000000..189190c5f --- /dev/null +++ b/ir/be/arm/bearch_arm.c @@ -0,0 +1,976 @@ +/* The main arm backend driver file. */ +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pseudo_irg.h" +#include "irgwalk.h" +#include "irprog.h" +#include "irprintf.h" +#include "ircons.h" +#include "irgmod.h" +#include "lower_intrinsics.h" + +#include "bitset.h" +#include "debug.h" + +#include "../bearch.h" /* the general register allocator interface */ +#include "../benode_t.h" +#include "../belower.h" +#include "../besched_t.h" +#include "../be.h" +#include "../beabi.h" + +#include "bearch_arm_t.h" + +#include "arm_new_nodes.h" /* arm nodes interface */ +#include "gen_arm_regalloc_if.h" /* the generated interface (register type and class defenitions) */ +#include "arm_gen_decls.h" /* interface declaration emitter */ +#include "arm_transform.h" +#include "arm_emitter.h" +#include "arm_map_regs.h" + +#define DEBUG_MODULE "firm.be.arm.isa" + +/* TODO: ugly, but we need it to get access to the registers assigned to Phi nodes */ +static set *cur_reg_set = NULL; + +/************************************************** + * _ _ _ __ + * | | | (_)/ _| + * _ __ ___ __ _ __ _| | | ___ ___ _| |_ + * | '__/ _ \/ _` | / _` | | |/ _ \ / __| | | _| + * | | | __/ (_| | | (_| | | | (_) | (__ | | | + * |_| \___|\__, | \__,_|_|_|\___/ \___| |_|_| + * __/ | + * |___/ + **************************************************/ + +static ir_node *my_skip_proj(const ir_node *n) { + while (is_Proj(n)) + n = get_Proj_pred(n); + return (ir_node *)n; +} + +/** + * Return register requirements for a arm node. + * If the node returns a tuple (mode_T) then the proj's + * will be asked for this information. + */ +static const arch_register_req_t *arm_get_irn_reg_req(const void *self, arch_register_req_t *req, const ir_node *irn, int pos) { + const arm_register_req_t *irn_req; + long node_pos = pos == -1 ? 0 : pos; + ir_mode *mode = get_irn_mode(irn); + firm_dbg_module_t *mod = firm_dbg_register(DEBUG_MODULE); + + if (is_Block(irn) || mode == mode_X || mode == mode_M) { + DBG((mod, LEVEL_1, "ignoring mode_T, mode_M node %+F\n", irn)); + return NULL; + } + + if (mode == mode_T && pos < 0) { + DBG((mod, LEVEL_1, "ignoring request for OUT requirements at %+F\n", irn)); + return NULL; + } + + DBG((mod, LEVEL_1, "get requirements at pos %d for %+F ... ", pos, irn)); + + if (is_Proj(irn)) { + /* in case of a proj, we need to get the correct OUT slot */ + /* of the node corresponding to the proj number */ + if (pos == -1) { + node_pos = arm_translate_proj_pos(irn); + } + else { + node_pos = pos; + } + + irn = my_skip_proj(irn); + + DB((mod, LEVEL_1, "skipping Proj, going to %+F at pos %d ... ", irn, node_pos)); + } + + /* get requirements for our own nodes */ + if (is_arm_irn(irn)) { + if (pos >= 0) { + irn_req = get_arm_in_req(irn, pos); + } + else { + irn_req = get_arm_out_req(irn, node_pos); + } + + DB((mod, LEVEL_1, "returning reqs for %+F at pos %d\n", irn, pos)); + + memcpy(req, &(irn_req->req), sizeof(*req)); + + if (arch_register_req_is(&(irn_req->req), should_be_same)) { + assert(irn_req->same_pos >= 0 && "should be same constraint for in -> out NYI"); + req->other_same = get_irn_n(irn, irn_req->same_pos); + } + + if (arch_register_req_is(&(irn_req->req), should_be_different)) { + assert(irn_req->different_pos >= 0 && "should be different constraint for in -> out NYI"); + req->other_different = get_irn_n(irn, irn_req->different_pos); + } + } + /* get requirements for FIRM nodes */ + else { + /* treat Phi like Const with default requirements */ + if (is_Phi(irn)) { + DB((mod, LEVEL_1, "returning standard reqs for %+F\n", irn)); + + if (mode_is_float(mode)) { + memcpy(req, &(arm_default_req_arm_floating_point.req), sizeof(*req)); + } + else if (mode_is_int(mode) || mode_is_reference(mode)) { + memcpy(req, &(arm_default_req_arm_general_purpose.req), sizeof(*req)); + } + else if (mode == mode_T || mode == mode_M) { + DBG((mod, LEVEL_1, "ignoring Phi node %+F\n", irn)); + return NULL; + } + else { + assert(0 && "unsupported Phi-Mode"); + } + } + else { + DB((mod, LEVEL_1, "returning NULL for %+F (node not supported)\n", irn)); + req = NULL; + } + } + + return req; +} + +static void arm_set_irn_reg(const void *self, ir_node *irn, const arch_register_t *reg) { + int pos = 0; + + if (is_Proj(irn)) { + + if (get_irn_mode(irn) == mode_X) { + return; + } + + pos = arm_translate_proj_pos(irn); + irn = my_skip_proj(irn); + } + + if (is_arm_irn(irn)) { + const arch_register_t **slots; + + slots = get_arm_slots(irn); + slots[pos] = reg; + } + else { + /* here we set the registers for the Phi nodes */ + arm_set_firm_reg(irn, reg, cur_reg_set); + } +} + +static const arch_register_t *arm_get_irn_reg(const void *self, const ir_node *irn) { + int pos = 0; + const arch_register_t *reg = NULL; + + if (is_Proj(irn)) { + + if (get_irn_mode(irn) == mode_X) { + return NULL; + } + + pos = arm_translate_proj_pos(irn); + irn = my_skip_proj(irn); + } + + if (is_arm_irn(irn)) { + const arch_register_t **slots; + slots = get_arm_slots(irn); + reg = slots[pos]; + } + else { + reg = arm_get_firm_reg(irn, cur_reg_set); + } + + return reg; +} + +static arch_irn_class_t arm_classify(const void *self, const ir_node *irn) { + irn = my_skip_proj(irn); + + if (is_cfop(irn)) { + return arch_irn_class_branch; + } + else if (is_arm_irn(irn)) { + return arch_irn_class_normal; + } + + return 0; +} + +static arch_irn_flags_t arm_get_flags(const void *self, const ir_node *irn) { + irn = my_skip_proj(irn); + + if (is_arm_irn(irn)) { + return get_arm_flags(irn); + } + else if (is_Unknown(irn)) { + return arch_irn_flags_ignore; + } + + return 0; +} + +static entity *arm_get_frame_entity(const void *self, const ir_node *irn) { + /* TODO: return the entity assigned to the frame */ + return NULL; +} + +/** + * This function is called by the generic backend to correct offsets for + * nodes accessing the stack. + */ +static void arm_set_stack_bias(const void *self, ir_node *irn, int bias) { + /* TODO: correct offset if irn accesses the stack */ +} + +/* fill register allocator interface */ + +static const arch_irn_ops_if_t arm_irn_ops_if = { + arm_get_irn_reg_req, + arm_set_irn_reg, + arm_get_irn_reg, + arm_classify, + arm_get_flags, + arm_get_frame_entity, + arm_set_stack_bias +}; + +arm_irn_ops_t arm_irn_ops = { + &arm_irn_ops_if, + NULL +}; + + + +/************************************************** + * _ _ __ + * | | (_)/ _| + * ___ ___ __| | ___ __ _ ___ _ __ _| |_ + * / __/ _ \ / _` |/ _ \/ _` |/ _ \ '_ \ | | _| + * | (_| (_) | (_| | __/ (_| | __/ | | | | | | + * \___\___/ \__,_|\___|\__, |\___|_| |_| |_|_| + * __/ | + * |___/ + **************************************************/ + +/** + * Transforms the standard firm graph into + * a ARM firm graph + */ +static void arm_prepare_graph(void *self) { + arm_code_gen_t *cg = self; + + irg_walk_blkwise_graph(cg->irg, arm_move_consts, arm_transform_node, cg); +} + + + +/** + * Called immediately before emit phase. + */ +static void arm_finish_irg(ir_graph *irg, arm_code_gen_t *cg) { + /* TODO: - fix offsets for nodes accessing stack + - ... + */ +} + + +/** + * These are some hooks which must be filled but are probably not needed. + */ +static void arm_before_sched(void *self) { + /* Some stuff you need to do after scheduling but before register allocation */ +} + +static void arm_before_ra(void *self) { + /* Some stuff you need to do immediately after register allocation */ +} + + +/** + * Emits the code, closes the output file and frees + * the code generator interface. + */ +static void arm_emit_and_done(void *self) { + arm_code_gen_t *cg = self; + ir_graph *irg = cg->irg; + FILE *out = cg->out; + + if (cg->emit_decls) { + arm_gen_decls(cg->out); + cg->emit_decls = 0; + } + + arm_finish_irg(irg, cg); + dump_ir_block_graph_sched(irg, "-arm-finished"); + arm_gen_routine(out, irg, cg); + + cur_reg_set = NULL; + + /* de-allocate code generator */ + del_set(cg->reg_set); + free(self); +} + +enum convert_which { low, high }; + +/** + * Move an floating point value to a integer register. + * Place the move operation into block bl. + */ +static ir_node *convert_to_int(ir_node *bl, ir_node *arg, enum convert_which which) { + return NULL; +} + +/** + * Convert the arguments of a call to support the + * ARM calling convention of general purpose AND floating + * point arguments + */ +static void handle_calls(ir_node *call, void *env) +{ + arm_code_gen_t *cg = env; + int i, j, n, size, idx, flag, n_param, n_res; + ir_type *mtp, *new_mtd, *new_tp[5]; + ir_node *new_in[5], **in; + ir_node *bl; + + if (! is_Call(call)) + return; + + /* check, if we need conversions */ + n = get_Call_n_params(call); + mtp = get_Call_type(call); + assert(get_method_n_params(mtp) == n); + + /* it's always enough to handle the first 4 parameters */ + if (n > 4) + n = 4; + flag = size = idx = 0; + bl = get_nodes_block(call); + for (i = 0; i < n; ++i) { + ir_type *param_tp = get_method_param_type(mtp, i); + + if (is_compound_type(param_tp)) { + /* an aggregate parameter: bad case */ + assert(0); + } + else { + /* a primitive parameter */ + ir_mode *mode = get_type_mode(param_tp); + + if (mode_is_float(mode)) { + if (get_mode_size_bits(mode) > 32) { + size += 2 * 4; + new_tp[idx] = cg->int_tp; + new_in[idx] = convert_to_int(bl, get_Call_param(call, i), low); + ++idx; + new_tp[idx] = cg->int_tp; + new_in[idx] = convert_to_int(bl, get_Call_param(call, i), high); + ++idx; + } + else { + size += 4; + new_tp[idx] = cg->int_tp; + new_in[idx] = convert_to_int(bl, get_Call_param(call, i), low); + ++idx; + } + flag = 1; + } + else { + size += 4; + new_tp[idx] = param_tp; + new_in[idx] = get_Call_param(call, i); + ++idx; + } + } + + if (size >= 16) + break; + } + + /* if flag is NOT set, no need to translate the method type */ + if (! flag) + return; + + /* construct a new method type */ + n = i; + n_param = get_method_n_params(mtp) - n + idx; + n_res = get_method_n_ress(mtp); + new_mtd = new_d_type_method(get_type_ident(mtp), n_param, n_res, get_type_dbg_info(mtp)); + + for (i = 0; i < idx; ++i) + set_method_param_type(new_mtd, i, new_tp[i]); + for (i = n, j = idx; i < get_method_n_params(mtp); ++i) + set_method_param_type(new_mtd, j++, get_method_param_type(mtp, i)); + for (i = 0; i < n_res; ++i) + set_method_res_type(new_mtd, i, get_method_res_type(mtp, i)); + + set_method_calling_convention(new_mtd, get_method_calling_convention(mtp)); + set_method_first_variadic_param_index(new_mtd, get_method_first_variadic_param_index(mtp)); + + if (is_lowered_type(mtp)) { + mtp = get_associated_type(mtp); + } + set_lowered_type(mtp, new_mtd); + + set_Call_type(call, new_mtd); + + /* calculate new in array of the Call */ + NEW_ARR_A(ir_node *, in, n_param + 2); + for (i = 0; i < idx; ++i) + in[2 + i] = new_in[i]; + for (i = n, j = idx; i < get_method_n_params(mtp); ++i) + in[2 + j++] = get_Call_param(call, i); + + in[0] = get_Call_mem(call); + in[1] = get_Call_ptr(call); + + /* finally, change the call inputs */ + set_irn_in(call, n_param + 2, in); +} + +/** + * Handle graph transformations before the abi converter does it's work + */ +static void arm_before_abi(void *self) { + arm_code_gen_t *cg = self; + + irg_walk_graph(cg->irg, NULL, handle_calls, cg); +} + +static void *arm_cg_init(FILE *F, const be_irg_t *birg); + +static const arch_code_generator_if_t arm_code_gen_if = { + arm_cg_init, + arm_before_abi, /* before abi introduce */ + arm_prepare_graph, + arm_before_sched, /* before scheduling hook */ + arm_before_ra, /* before register allocation hook */ + NULL, /* after register allocation */ + arm_emit_and_done, +}; + +/** + * Initializes the code generator. + */ +static void *arm_cg_init(FILE *F, const be_irg_t *birg) { + static ir_type *int_tp = NULL; + arm_isa_t *isa = (arm_isa_t *)birg->main_env->arch_env->isa; + arm_code_gen_t *cg; + + if (! int_tp) { + /* create an integer type with machine size */ + int_tp = new_type_primitive(new_id_from_chars("int", 3), mode_Is); + } + + cg = xmalloc(sizeof(*cg)); + cg->impl = &arm_code_gen_if; + cg->irg = birg->irg; + cg->reg_set = new_set(arm_cmp_irn_reg_assoc, 1024); + cg->mod = firm_dbg_register("firm.be.arm.cg"); + cg->out = F; + cg->arch_env = birg->main_env->arch_env; + cg->birg = birg; + cg->int_tp = int_tp; + + isa->num_codegens++; + + if (isa->num_codegens > 1) + cg->emit_decls = 0; + else + cg->emit_decls = 1; + + cur_reg_set = cg->reg_set; + + arm_irn_ops.cg = cg; + + return (arch_code_generator_t *)cg; +} + + +/** + * Maps all intrinsic calls that the backend support + * and map all instructions the backend did not support + * to runtime calls. + */ +void arm_global_init(void) { + ir_type *tp, *int_tp, *uint_tp; + i_record records[8]; + int n_records = 0; + +#define ID(x) new_id_from_chars(x, sizeof(x)-1) + + int_tp = new_type_primitive(ID("int"), mode_Is); + uint_tp = new_type_primitive(ID("uint"), mode_Iu); + + { + runtime_rt rt_Div; + i_instr_record *map_Div = &records[n_records++].i_instr; + + tp = new_type_method(ID("rt_iDiv"), 2, 1); + set_method_param_type(tp, 0, int_tp); + set_method_param_type(tp, 1, int_tp); + set_method_res_type(tp, 0, int_tp); + + rt_Div.ent = new_entity(get_glob_type(), ID("__divsi3"), tp); + rt_Div.mode = mode_T; + rt_Div.mem_proj_nr = pn_Div_M; + rt_Div.exc_proj_nr = pn_Div_X_except; + rt_Div.exc_mem_proj_nr = pn_Div_M; + rt_Div.res_proj_nr = pn_Div_res; + + set_entity_visibility(rt_Div.ent, visibility_external_allocated); + + map_Div->kind = INTRINSIC_INSTR; + map_Div->op = op_Div; + map_Div->i_mapper = i_mapper_RuntimeCall; + map_Div->ctx = &rt_Div; + } + { + runtime_rt rt_Div; + i_instr_record *map_Div = &records[n_records++].i_instr; + + tp = new_type_method(ID("rt_uDiv"), 2, 1); + set_method_param_type(tp, 0, uint_tp); + set_method_param_type(tp, 1, uint_tp); + set_method_res_type(tp, 0, uint_tp); + + rt_Div.ent = new_entity(get_glob_type(), ID("__udivsi3"), tp); + rt_Div.mode = mode_T; + rt_Div.mem_proj_nr = pn_Div_M; + rt_Div.exc_proj_nr = pn_Div_X_except; + rt_Div.exc_mem_proj_nr = pn_Div_M; + rt_Div.res_proj_nr = pn_Div_res; + + set_entity_visibility(rt_Div.ent, visibility_external_allocated); + + map_Div->kind = INTRINSIC_INSTR; + map_Div->op = op_Div; + map_Div->i_mapper = i_mapper_RuntimeCall; + map_Div->ctx = &rt_Div; + } + { + runtime_rt rt_Mod; + i_instr_record *map_Mod = &records[n_records++].i_instr; + + tp = new_type_method(ID("rt_iMod"), 2, 1); + set_method_param_type(tp, 0, int_tp); + set_method_param_type(tp, 1, int_tp); + set_method_res_type(tp, 0, int_tp); + + rt_Mod.ent = new_entity(get_glob_type(), ID("__modsi3"), tp); + rt_Mod.mode = mode_T; + rt_Mod.mem_proj_nr = pn_Mod_M; + rt_Mod.exc_proj_nr = pn_Mod_X_except; + rt_Mod.exc_mem_proj_nr = pn_Mod_M; + rt_Mod.res_proj_nr = pn_Mod_res; + + set_entity_visibility(rt_Mod.ent, visibility_external_allocated); + + map_Mod->kind = INTRINSIC_INSTR; + map_Mod->op = op_Mod; + map_Mod->i_mapper = i_mapper_RuntimeCall; + map_Mod->ctx = &rt_Mod; + } + { + runtime_rt rt_Mod; + i_instr_record *map_Mod = &records[n_records++].i_instr; + + tp = new_type_method(ID("rt_uMod"), 2, 1); + set_method_param_type(tp, 0, uint_tp); + set_method_param_type(tp, 1, uint_tp); + set_method_res_type(tp, 0, uint_tp); + + rt_Mod.ent = new_entity(get_glob_type(), ID("__umodsi3"), tp); + rt_Mod.mode = mode_T; + rt_Mod.mem_proj_nr = pn_Mod_M; + rt_Mod.exc_proj_nr = pn_Mod_X_except; + rt_Mod.exc_mem_proj_nr = pn_Mod_M; + rt_Mod.res_proj_nr = pn_Mod_res; + + set_entity_visibility(rt_Mod.ent, visibility_external_allocated); + + map_Mod->kind = INTRINSIC_INSTR; + map_Mod->op = op_Mod; + map_Mod->i_mapper = i_mapper_RuntimeCall; + map_Mod->ctx = &rt_Mod; + } + + if (n_records > 0) + lower_intrinsics(records, n_records); +} + +/***************************************************************** + * ____ _ _ _____ _____ + * | _ \ | | | | |_ _|/ ____| /\ + * | |_) | __ _ ___| | _____ _ __ __| | | | | (___ / \ + * | _ < / _` |/ __| |/ / _ \ '_ \ / _` | | | \___ \ / /\ \ + * | |_) | (_| | (__| < __/ | | | (_| | _| |_ ____) / ____ \ + * |____/ \__,_|\___|_|\_\___|_| |_|\__,_| |_____|_____/_/ \_\ + * + *****************************************************************/ + +static arm_isa_t arm_isa_template = { + &arm_isa_if, /* isa interface */ + &arm_general_purpose_regs[REG_R13], /* stack pointer */ + &arm_general_purpose_regs[REG_R11], /* base pointer */ + -1, /* stack direction */ + 0 /* number of codegenerator objects */ +}; + +/** + * Initializes the backend ISA and opens the output file. + */ +static void *arm_init(void) { + static int inited = 0; + arm_isa_t *isa; + + if(inited) + return NULL; + + isa = xcalloc(1, sizeof(*isa)); + memcpy(isa, &arm_isa_template, sizeof(*isa)); + + arm_register_init(isa); + arm_create_opcodes(); + + inited = 1; + + return isa; +} + + + +/** + * Closes the output file and frees the ISA structure. + */ +static void arm_done(void *self) { + free(self); +} + + + +static int arm_get_n_reg_class(const void *self) { + return N_CLASSES; +} + +static const arch_register_class_t *arm_get_reg_class(const void *self, int i) { + assert(i >= 0 && i < N_CLASSES && "Invalid arm register class requested."); + return &arm_reg_classes[i]; +} + + + +/** + * Get the register class which shall be used to store a value of a given mode. + * @param self The this pointer. + * @param mode The mode in question. + * @return A register class which can hold values of the given mode. + */ +const arch_register_class_t *arm_get_reg_class_for_mode(const void *self, const ir_mode *mode) { + if (mode_is_float(mode)) + return &arm_reg_classes[CLASS_arm_floating_point]; + else + return &arm_reg_classes[CLASS_arm_general_purpose]; +} + + + +/** + * Produces the type which sits between the stack args and the locals on the stack. + * it will contain the return address and space to store the old base pointer. + * @return The Firm type modelling the ABI between type. + */ +static ir_type *arm_get_between_type(void *self) { + static ir_type *between_type = NULL; + static entity *old_bp_ent = NULL; + + if(!between_type) { + entity *ret_addr_ent; + ir_type *ret_addr_type = new_type_primitive(new_id_from_str("return_addr"), mode_P); + ir_type *old_bp_type = new_type_primitive(new_id_from_str("bp"), mode_P); + + between_type = new_type_class(new_id_from_str("arm_between_type")); + old_bp_ent = new_entity(between_type, new_id_from_str("old_bp"), old_bp_type); + ret_addr_ent = new_entity(between_type, new_id_from_str("old_bp"), ret_addr_type); + + set_entity_offset_bytes(old_bp_ent, 0); + set_entity_offset_bytes(ret_addr_ent, get_type_size_bytes(old_bp_type)); + set_type_size_bytes(between_type, get_type_size_bytes(old_bp_type) + get_type_size_bytes(ret_addr_type)); + } + + return between_type; +} + + + + + + + + +typedef struct { + be_abi_call_flags_bits_t flags; + const arch_env_t *arch_env; + const arch_isa_t *isa; + ir_graph *irg; +} arm_abi_env_t; + +static void *arm_abi_init(const be_abi_call_t *call, const arch_env_t *arch_env, ir_graph *irg) +{ + arm_abi_env_t *env = xmalloc(sizeof(env[0])); + be_abi_call_flags_t fl = be_abi_call_get_flags(call); + env->flags = fl.bits; + env->irg = irg; + env->arch_env = arch_env; + env->isa = arch_env->isa; + return env; +} + +static void arm_abi_dont_save_regs(void *self, pset *s) +{ + arm_abi_env_t *env = self; + if(env->flags.try_omit_fp) + pset_insert_ptr(s, env->isa->bp); +} + + + +/** + * Build the ARM prolog + */ +static const arch_register_t *arm_abi_prologue(void *self, ir_node **mem, pmap *reg_map) { + ir_node *keep, *store; + arm_abi_env_t *env = self; + ir_graph *irg = env->irg; + ir_node *block = get_irg_start_block(irg); +// ir_node *regs[16]; +// int n_regs = 0; + arch_register_class_t *gp = &arm_reg_classes[CLASS_arm_general_purpose]; + static const arm_register_req_t *fp_req[] = { + &arm_default_req_arm_general_purpose_r11 + }; + + ir_node *fp = be_abi_reg_map_get(reg_map, env->isa->bp); + ir_node *ip = be_abi_reg_map_get(reg_map, &arm_general_purpose_regs[REG_R12]); + ir_node *sp = be_abi_reg_map_get(reg_map, env->isa->sp); + ir_node *lr = be_abi_reg_map_get(reg_map, &arm_general_purpose_regs[REG_R14]); + ir_node *pc = be_abi_reg_map_get(reg_map, &arm_general_purpose_regs[REG_R15]); +// ir_node *r0 = be_abi_reg_map_get(reg_map, &arm_general_purpose_regs[REG_R0]); +// ir_node *r1 = be_abi_reg_map_get(reg_map, &arm_general_purpose_regs[REG_R1]); +// ir_node *r2 = be_abi_reg_map_get(reg_map, &arm_general_purpose_regs[REG_R2]); +// ir_node *r3 = be_abi_reg_map_get(reg_map, &arm_general_purpose_regs[REG_R3]); + + if(env->flags.try_omit_fp) + return env->isa->sp; + + ip = be_new_Copy(gp, irg, block, sp ); + arch_set_irn_register(env->arch_env, ip, &arm_general_purpose_regs[REG_R12]); + be_set_constr_single_reg(ip, BE_OUT_POS(0), &arm_general_purpose_regs[REG_R12] ); + +// if (r0) regs[n_regs++] = r0; +// if (r1) regs[n_regs++] = r1; +// if (r2) regs[n_regs++] = r2; +// if (r3) regs[n_regs++] = r3; +// sp = new_r_arm_StoreStackMInc(irg, block, *mem, sp, n_regs, regs, get_irn_mode(sp)); +// set_arm_req_out(sp, &arm_default_req_arm_general_purpose_r13, 0); +// arch_set_irn_register(env->arch_env, sp, env->isa->sp); + store = new_rd_arm_StoreStackM4Inc(NULL, irg, block, sp, fp, ip, lr, pc, *mem, mode_T); + set_arm_req_out(store, &arm_default_req_arm_general_purpose_r13, 0); +// arch_set_irn_register(env->arch_env, store, env->isa->sp); + + sp = new_r_Proj(irg, block, store, env->isa->sp->reg_class->mode, 0); + arch_set_irn_register(env->arch_env, sp, env->isa->sp); + *mem = new_r_Proj(irg, block, store, mode_M, 1); + + keep = be_new_CopyKeep_single(gp, irg, block, ip, sp, get_irn_mode(ip)); + be_node_set_reg_class(keep, 1, gp); + arch_set_irn_register(env->arch_env, keep, &arm_general_purpose_regs[REG_R12]); + be_set_constr_single_reg(keep, BE_OUT_POS(0), &arm_general_purpose_regs[REG_R12] ); + + fp = new_rd_arm_Sub_i(NULL, irg, block, keep, get_irn_mode(fp) ); + set_arm_value(fp, new_tarval_from_long(4, mode_Iu)); + set_arm_req_out_all(fp, fp_req); + //set_arm_req_out(fp, &arm_default_req_arm_general_purpose_r11, 0); + arch_set_irn_register(env->arch_env, fp, env->isa->bp); + +// be_abi_reg_map_set(reg_map, &arm_general_purpose_regs[REG_R0], r0); +// be_abi_reg_map_set(reg_map, &arm_general_purpose_regs[REG_R1], r1); +// be_abi_reg_map_set(reg_map, &arm_general_purpose_regs[REG_R2], r2); +// be_abi_reg_map_set(reg_map, &arm_general_purpose_regs[REG_R3], r3); + be_abi_reg_map_set(reg_map, env->isa->bp, fp); + be_abi_reg_map_set(reg_map, &arm_general_purpose_regs[REG_R12], keep); + be_abi_reg_map_set(reg_map, env->isa->sp, sp); + be_abi_reg_map_set(reg_map, &arm_general_purpose_regs[REG_R14], lr); + be_abi_reg_map_set(reg_map, &arm_general_purpose_regs[REG_R15], pc); + + return env->isa->bp; +} + +static void arm_abi_epilogue(void *self, ir_node *bl, ir_node **mem, pmap *reg_map) { + arm_abi_env_t *env = self; + ir_node *curr_sp = be_abi_reg_map_get(reg_map, env->isa->sp); + ir_node *curr_bp = be_abi_reg_map_get(reg_map, env->isa->bp); + ir_node *curr_pc = be_abi_reg_map_get(reg_map, &arm_general_purpose_regs[REG_R15]); + ir_node *curr_lr = be_abi_reg_map_get(reg_map, &arm_general_purpose_regs[REG_R14]); + static const arm_register_req_t *sub12_req[] = { + &arm_default_req_arm_general_purpose_r13 + }; + +// TODO: Activate Omit fp in epilogue + if(env->flags.try_omit_fp) { + curr_sp = be_new_IncSP(env->isa->sp, env->irg, bl, curr_sp, *mem, BE_STACK_FRAME_SIZE, be_stack_dir_shrink); + + curr_lr = be_new_CopyKeep_single(&arm_reg_classes[CLASS_arm_general_purpose], env->irg, bl, curr_lr, curr_sp, get_irn_mode(curr_lr)); + be_node_set_reg_class(curr_lr, 1, &arm_reg_classes[CLASS_arm_general_purpose]); + arch_set_irn_register(env->arch_env, curr_lr, &arm_general_purpose_regs[REG_R14]); + be_set_constr_single_reg(curr_lr, BE_OUT_POS(0), &arm_general_purpose_regs[REG_R14] ); + + curr_pc = be_new_Copy(&arm_reg_classes[CLASS_arm_general_purpose], env->irg, bl, curr_lr ); + arch_set_irn_register(env->arch_env, curr_pc, &arm_general_purpose_regs[REG_R15]); + be_set_constr_single_reg(curr_pc, BE_OUT_POS(0), &arm_general_purpose_regs[REG_R15] ); + } else { + ir_node *sub12_node; + ir_node *load_node; + sub12_node = new_rd_arm_Sub_i(NULL, env->irg, bl, curr_bp, mode_Iu ); + set_arm_value(sub12_node, new_tarval_from_long(12,mode_Iu)); + set_arm_req_out_all(sub12_node, sub12_req); + arch_set_irn_register(env->arch_env, sub12_node, env->isa->sp); + load_node = new_rd_arm_LoadStackM3( NULL, env->irg, bl, sub12_node, *mem, mode_T ); + set_arm_req_out(load_node, &arm_default_req_arm_general_purpose_r11, 0); + set_arm_req_out(load_node, &arm_default_req_arm_general_purpose_r13, 1); + set_arm_req_out(load_node, &arm_default_req_arm_general_purpose_r15, 2); + curr_bp = new_r_Proj(env->irg, bl, load_node, env->isa->bp->reg_class->mode, 0); + curr_sp = new_r_Proj(env->irg, bl, load_node, env->isa->sp->reg_class->mode, 1); + curr_pc = new_r_Proj(env->irg, bl, load_node, mode_Iu, 2); + *mem = new_r_Proj(env->irg, bl, load_node, mode_M, 3); + arch_set_irn_register(env->arch_env, curr_bp, env->isa->bp); + arch_set_irn_register(env->arch_env, curr_sp, env->isa->sp); + arch_set_irn_register(env->arch_env, curr_pc, &arm_general_purpose_regs[REG_R15]); + } + be_abi_reg_map_set(reg_map, env->isa->sp, curr_sp); + be_abi_reg_map_set(reg_map, env->isa->bp, curr_bp); + be_abi_reg_map_set(reg_map, &arm_general_purpose_regs[REG_R14], curr_lr); + be_abi_reg_map_set(reg_map, &arm_general_purpose_regs[REG_R15], curr_pc); +} + +static const be_abi_callbacks_t arm_abi_callbacks = { + arm_abi_init, + free, + arm_get_between_type, + arm_abi_dont_save_regs, + arm_abi_prologue, + arm_abi_epilogue, +}; + + +/** + * Get the ABI restrictions for procedure calls. + * @param self The this pointer. + * @param method_type The type of the method (procedure) in question. + * @param abi The abi object to be modified + */ +void arm_get_call_abi(const void *self, ir_type *method_type, be_abi_call_t *abi) { + ir_type *tp; + ir_mode *mode; + int i; + int n = get_method_n_params(method_type); +// const arch_register_t *reg; + be_abi_call_flags_t flags = { + 0, /* store from left to right */ + 0, /* store arguments sequential */ + 1, /* try to omit the frame pointer */ + 1, /* the function can use any register as frame pointer */ + 1 /* a call can take the callee's address as an immediate */ + }; + + /* set stack parameter passing style */ + be_abi_call_set_flags(abi, flags, &arm_abi_callbacks); + + for (i = 0; i < n; i++) { + /* reg = get reg for param i; */ + /* be_abi_call_param_reg(abi, i, reg); */ + if (i < 4) + + be_abi_call_param_reg(abi, i, arm_get_RegParam_reg(i)); + else + be_abi_call_param_stack(abi, i, 4, 0, 0); + } + + /* default: return value is in R0 resp. F0 */ + assert(get_method_n_ress(method_type) < 2); + if (get_method_n_ress(method_type) > 0) { + tp = get_method_res_type(method_type, 0); + mode = get_type_mode(tp); + + be_abi_call_res_reg(abi, 0, + mode_is_float(mode) ? &arm_floating_point_regs[REG_F0] : &arm_general_purpose_regs[REG_R0]); + } +} + +static const void *arm_get_irn_ops(const arch_irn_handler_t *self, const ir_node *irn) { + return &arm_irn_ops; +} + +const arch_irn_handler_t arm_irn_handler = { + arm_get_irn_ops +}; + +const arch_irn_handler_t *arm_get_irn_handler(const void *self) { + return &arm_irn_handler; +} + +int arm_to_appear_in_schedule(void *block_env, const ir_node *irn) { + return is_arm_irn(irn); +} + +/** + * Initializes the code generator interface. + */ +static const arch_code_generator_if_t *arm_get_code_generator_if(void *self) { + return &arm_code_gen_if; +} + +list_sched_selector_t arm_sched_selector; + +/** + * Returns the reg_pressure scheduler with to_appear_in_schedule() over\loaded + */ +static const list_sched_selector_t *arm_get_list_sched_selector(const void *self) { + memcpy(&arm_sched_selector, reg_pressure_selector, sizeof(list_sched_selector_t)); + arm_sched_selector.to_appear_in_schedule = arm_to_appear_in_schedule; + return &arm_sched_selector; +} + +#ifdef WITH_LIBCORE +static void arm_register_options(lc_opt_entry_t *ent) +{ +} +#endif /* WITH_LIBCORE */ + +const arch_isa_if_t arm_isa_if = { +#ifdef WITH_LIBCORE + arm_register_options, +#endif + arm_init, + arm_done, + arm_get_n_reg_class, + arm_get_reg_class, + arm_get_reg_class_for_mode, + arm_get_call_abi, + arm_get_irn_handler, + arm_get_code_generator_if, + arm_get_list_sched_selector, +}; diff --git a/ir/be/arm/bearch_arm.h b/ir/be/arm/bearch_arm.h new file mode 100644 index 000000000..077733ba9 --- /dev/null +++ b/ir/be/arm/bearch_arm.h @@ -0,0 +1,10 @@ +#ifndef _BEARCH_ARM_H_ +#define _BEARCH_ARM_H_ + +#include "../bearch.h" + +extern const arch_isa_if_t arm_isa_if; +void arm_global_init(void); + + +#endif /* _BEARCH_ARM_H_ */ diff --git a/ir/be/arm/bearch_arm_t.h b/ir/be/arm/bearch_arm_t.h new file mode 100644 index 000000000..e24d2bb14 --- /dev/null +++ b/ir/be/arm/bearch_arm_t.h @@ -0,0 +1,52 @@ +#ifndef _BEARCH_ARM_T_H_ +#define _BEARCH_ARM_T_H_ + +#include "debug.h" +#include "bearch_arm.h" +#include "arm_nodes_attr.h" +#include "../be.h" +#include "set.h" +#include + +typedef struct _arm_code_gen_t { + const arch_code_generator_if_t *impl; /**< implementation */ + ir_graph *irg; /**< current irg */ + FILE *out; /**< output file */ + const arch_env_t *arch_env; /**< the arch env */ + set *reg_set; /**< set to memorize registers for FIRM nodes (e.g. phi) */ + firm_dbg_module_t *mod; /**< debugging module */ + int emit_decls; /**< flag indicating if decls were already emitted */ + const be_irg_t *birg; /**< The be-irg (contains additional information about the irg) */ + ir_type *int_tp; /**< the int type, needed for Call conversion */ +} arm_code_gen_t; + + +typedef struct _arm_isa_t { + const arch_isa_if_t *impl; + const arch_register_t *sp; /**< The stack pointer register. */ + const arch_register_t *bp; /**< The base pointer register. */ + const int stack_dir; /**< -1 for decreasing, 1 for increasing. */ + int num_codegens; +} arm_isa_t; + + +typedef struct _arm_irn_ops_t { + const arch_irn_ops_if_t *impl; + arm_code_gen_t *cg; +} arm_irn_ops_t; + + +/* this is a struct to minimize the number of parameters + for transformation walker */ +typedef struct _arm_transform_env_t { + firm_dbg_module_t *mod; /**< The firm debugger */ + dbg_info *dbg; /**< The node debug info */ + ir_graph *irg; /**< The irg, the node should be created in */ + ir_node *block; /**< The block, the node should belong to */ + ir_node *irn; /**< The irn, to be transformed */ + ir_mode *mode; /**< The mode of the irn */ +// arm_code_gen_t *cg; /**< The code generator */ +} arm_transform_env_t; + + +#endif /* _BEARCH_ARM_T_H_ */