Remove handling for 32bit as smaller mode in emit_ia32_Conv_I2I(), because it is...
[libfirm] / ir / be / ia32 / ia32_emitter.c
index 1f13a05..53d7296 100644 (file)
@@ -73,6 +73,46 @@ static const ia32_isa_t *isa;
 static ia32_code_gen_t  *cg;
 static int               do_pic;
 static char              pic_base_label[128];
+static ir_label_t        exc_label_id;
+
+/** Return the next block in Block schedule */
+static ir_node *get_prev_block_sched(const ir_node *block)
+{
+       return get_irn_link(block);
+}
+
+static bool is_fallthrough(const ir_node *cfgpred)
+{
+       ir_node *pred;
+
+       if(!is_Proj(cfgpred))
+               return true;
+       pred = get_Proj_pred(cfgpred);
+       if(is_ia32_SwitchJmp(pred))
+               return false;
+
+       return true;
+}
+
+static bool block_needs_label(const ir_node *block)
+{
+       bool need_label = true;
+       int  n_cfgpreds = get_Block_n_cfgpreds(block);
+
+       if (n_cfgpreds == 0) {
+               need_label = false;
+       } else if (n_cfgpreds == 1) {
+               ir_node *cfgpred            = get_Block_cfgpred(block, 0);
+               ir_node *cfgpred_block      = get_nodes_block(cfgpred);
+
+               if (get_prev_block_sched(block) == cfgpred_block
+                               && is_fallthrough(cfgpred)) {
+                       need_label = false;
+               }
+       }
+
+       return need_label;
+}
 
 /**
  * Returns the register at in position pos.
@@ -290,11 +330,12 @@ void ia32_emit_mode_suffix(const ir_node *node)
 
 void ia32_emit_x87_mode_suffix(const ir_node *node)
 {
-       ir_mode *mode = get_ia32_ls_mode(node);
-       assert(mode != NULL);
        /* we only need to emit the mode on address mode */
-       if(get_ia32_op_type(node) != ia32_Normal)
+       if(get_ia32_op_type(node) != ia32_Normal) {
+               ir_mode *mode = get_ia32_ls_mode(node);
+               assert(mode != NULL);
                ia32_emit_mode_suffix_mode(mode);
+       }
 }
 
 static char get_xmm_mode_suffix(ir_mode *mode)
@@ -599,11 +640,6 @@ static const struct cmp2conditon_t cmp2condition_u[] = {
        { NULL,              pn_Cmp_Leg },   /* always true  */
 };
 
-enum {
-       ia32_pn_Cmp_unsigned = 0x1000,
-       ia32_pn_Cmp_float    = 0x2000,
-};
-
 /**
  * walks up a tree of copies/perms/spills/reloads to find the original value
  * that is moved around
@@ -730,6 +766,7 @@ void ia32_emit_cmp_suffix_node(const ir_node *node,
  * Returns the target block for a control flow node.
  */
 static ir_node *get_cfop_target_block(const ir_node *irn) {
+       assert(get_irn_mode(irn) == mode_X);
        return get_irn_link(irn);
 }
 
@@ -739,14 +776,23 @@ static ir_node *get_cfop_target_block(const ir_node *irn) {
 static void ia32_emit_block_name(const ir_node *block)
 {
        if (has_Block_label(block)) {
-               be_emit_string(be_gas_label_prefix());
-               be_emit_irprintf("%u", (unsigned)get_Block_label(block));
+               be_emit_string(be_gas_block_label_prefix());
+               be_emit_irprintf("%lu", get_Block_label(block));
        } else {
                be_emit_cstring(BLOCK_PREFIX);
-               be_emit_irprintf("%d", get_irn_node_nr(block));
+               be_emit_irprintf("%ld", get_irn_node_nr(block));
        }
 }
 
+/**
+ * Emits an exception label for a given node.
+ */
+static void ia32_emit_exc_label(const ir_node *node)
+{
+       be_emit_string(be_gas_insn_label_prefix());
+       be_emit_irprintf("%lu", get_ia32_exc_label_id(node));
+}
+
 /**
  * Emits the target label for a control flow node.
  */
@@ -757,12 +803,6 @@ static void ia32_emit_cfop_target(const ir_node *node)
        ia32_emit_block_name(block);
 }
 
-/** Return the next block in Block schedule */
-static ir_node *next_blk_sched(const ir_node *block)
-{
-       return get_irn_link(block);
-}
-
 /**
  * Returns the Proj with projection number proj and NOT mode_M
  */
@@ -785,6 +825,13 @@ static ir_node *get_proj(const ir_node *node, long proj) {
        return NULL;
 }
 
+static bool can_be_fallthrough(const ir_node *node)
+{
+       ir_node *target_block = get_cfop_target_block(node);
+       ir_node *block        = get_nodes_block(node);
+       return get_prev_block_sched(target_block) == block;
+}
+
 /**
  * Emits the jump sequence for a conditional jump (cmp + jmp_true + jmp_false)
  */
@@ -807,9 +854,8 @@ static void emit_ia32_Jcc(const ir_node *node)
        assert(proj_false && "Jcc without false Proj");
 
        block      = get_nodes_block(node);
-       next_block = next_blk_sched(block);
 
-       if (get_cfop_target_block(proj_true) == next_block) {
+       if (can_be_fallthrough(proj_true)) {
                /* exchange both proj's so the second one can be omitted */
                const ir_node *t = proj_true;
 
@@ -1110,20 +1156,19 @@ static void emit_ia32_SwitchJmp(const ir_node *node)
  */
 static void emit_Jmp(const ir_node *node)
 {
-       ir_node *block, *next_block;
+       ir_node *block;
 
        /* for now, the code works for scheduled and non-schedules blocks */
        block = get_nodes_block(node);
 
        /* we have a block schedule */
-       next_block = next_blk_sched(block);
-       if (get_cfop_target_block(node) != next_block) {
-               be_emit_cstring("\tjmp ");
-               ia32_emit_cfop_target(node);
-       } else {
+       if (can_be_fallthrough(node)) {
                be_emit_cstring("\t/* fallthrough to ");
                ia32_emit_cfop_target(node);
                be_emit_cstring(" */");
+       } else {
+               be_emit_cstring("\tjmp ");
+               ia32_emit_cfop_target(node);
        }
        be_emit_finish_line_gas(node);
 }
@@ -1459,16 +1504,10 @@ static void emit_ia32_Conv_I2I(const ir_node *node)
        const arch_register_t *in_reg, *out_reg;
 
        assert(!mode_is_float(smaller_mode));
-       assert(smaller_bits == 8 || smaller_bits == 16 || smaller_bits == 32);
+       assert(smaller_bits == 8 || smaller_bits == 16);
 
        signed_mode = mode_is_signed(smaller_mode);
-       if(smaller_bits == 32) {
-               // this should not happen as it's no convert
-               assert(0);
-               sign_suffix = "";
-       } else {
-               sign_suffix = signed_mode ? "s" : "z";
-       }
+       sign_suffix = signed_mode ? "s" : "z";
 
        out_reg = get_out_reg(node, 0);
 
@@ -1809,8 +1848,13 @@ static void emit_be_Return(const ir_node *node)
        be_emit_cstring("\tret");
 
        pop = be_Return_get_pop(node);
-       if(pop > 0) {
+       if (pop > 0) {
                be_emit_irprintf(" $%d", pop);
+       } else if (be_Return_get_emit_pop(node)) {
+               ir_node *block = get_nodes_block(node);
+               if (block_needs_label(block)) {
+                       be_emit_cstring(" $0");
+               }
        }
        be_emit_finish_line_gas(node);
 }
@@ -1898,12 +1942,16 @@ typedef void (*emit_func_ptr) (const ir_node *);
 /**
  * Emits code for a node.
  */
-static void ia32_emit_node(const ir_node *node)
+static void ia32_emit_node(ir_node *node)
 {
        ir_op *op = get_irn_op(node);
 
        DBG((dbg, LEVEL_1, "emitting code for %+F\n", node));
 
+       if (is_ia32_irn(node) && get_ia32_exc_label(node)) {
+               /* emit the exception label of this instruction */
+               ia32_assign_exc_label(node);
+       }
        if (op->ops.generic) {
                emit_func_ptr func = (emit_func_ptr) op->ops.generic;
 
@@ -1933,20 +1981,21 @@ static void ia32_emit_alignment(unsigned align, unsigned skip)
 static void ia32_emit_align_label(void)
 {
        unsigned align        = ia32_cg_config.label_alignment;
-       unsigned maximum_skip = (1 << align) - 1;
+       unsigned maximum_skip = ia32_cg_config.label_alignment_max_skip;
        ia32_emit_alignment(align, maximum_skip);
 }
 
 /**
- * Test wether a block should be aligned.
+ * Test whether a block should be aligned.
  * For cpus in the P4/Athlon class it is useful to align jump labels to
  * 16 bytes. However we should only do that if the alignment nops before the
  * label aren't executed more often than we have jumps to the label.
  */
-static int should_align_block(ir_node *block, ir_node *prev)
+static int should_align_block(const ir_node *block)
 {
        static const double DELTA = .0001;
        ir_exec_freq *exec_freq   = cg->birg->exec_freq;
+       ir_node      *prev        = get_prev_block_sched(block);
        double        block_freq;
        double        prev_freq = 0;  /**< execfreq of the fallthrough block */
        double        jmp_freq  = 0;  /**< execfreq of all non-fallthrough blocks */
@@ -1981,46 +2030,50 @@ static int should_align_block(ir_node *block, ir_node *prev)
        return jmp_freq > ia32_cg_config.label_alignment_factor;
 }
 
-static int can_omit_block_label(ir_node *cfgpred)
-{
-       ir_node *pred;
-
-       if(!is_Proj(cfgpred))
-               return 1;
-       pred = get_Proj_pred(cfgpred);
-       if(is_ia32_SwitchJmp(pred))
-               return 0;
-
-       return 1;
-}
-
-static void ia32_emit_block_header(ir_node *block, ir_node *prev)
+/**
+ * Emit the block header for a block.
+ *
+ * @param block       the block
+ * @param prev_block  the previous block
+ */
+static void ia32_emit_block_header(ir_node *block)
 {
        ir_graph     *irg = current_ir_graph;
        int           n_cfgpreds;
-       int           need_label = 1;
+       bool          need_label = block_needs_label(block);
        int           i, arity;
        ir_exec_freq *exec_freq = cg->birg->exec_freq;
 
-       if(block == get_irg_end_block(irg) || block == get_irg_start_block(irg))
+       if (block == get_irg_end_block(irg) || block == get_irg_start_block(irg))
                return;
 
-       n_cfgpreds = get_Block_n_cfgpreds(block);
+       if (ia32_cg_config.label_alignment > 0) {
+               /* align the current block if:
+                * a) if should be aligned due to its execution frequency
+                * b) there is no fall-through here
+                */
+               if (should_align_block(block)) {
+                       ia32_emit_align_label();
+               } else {
+                       /* if the predecessor block has no fall-through,
+                          we can always align the label. */
+                       int      i;
+                       bool     has_fallthrough = false;;
+
+                       for (i = n_cfgpreds - 1; i >= 0; --i) {
+                               ir_node *cfg_pred = get_Block_cfgpred(block, i);
+                               if (can_be_fallthrough(cfg_pred)) {
+                                       has_fallthrough = true;
+                                       break;
+                               }
+                       }
 
-       if(n_cfgpreds == 0) {
-               need_label = 0;
-       } else if(n_cfgpreds == 1) {
-               ir_node *cfgpred = get_Block_cfgpred(block, 0);
-               if(get_nodes_block(cfgpred) == prev && can_omit_block_label(cfgpred)) {
-                       need_label = 0;
+                       if (!has_fallthrough)
+                               ia32_emit_align_label();
                }
        }
 
-       if (should_align_block(block, prev)) {
-               ia32_emit_align_label();
-       }
-
-       if(need_label) {
+       if (need_label || has_Block_label(block)) {
                ia32_emit_block_name(block);
                be_emit_char(':');
 
@@ -2052,11 +2105,11 @@ static void ia32_emit_block_header(ir_node *block, ir_node *prev)
  * Walks over the nodes in a block connected by scheduling edges
  * and emits code for each node.
  */
-static void ia32_gen_block(ir_node *block, ir_node *last_block)
+static void ia32_gen_block(ir_node *block)
 {
-       const ir_node *node;
+       ir_node *node;
 
-       ia32_emit_block_header(block, last_block);
+       ia32_emit_block_header(block);
 
        /* emit the contents of the block */
        be_dbg_set_dbg_info(get_irn_dbg_info(block));
@@ -2065,45 +2118,80 @@ static void ia32_gen_block(ir_node *block, ir_node *last_block)
        }
 }
 
+typedef struct exc_entry {
+       ir_node *exc_instr;  /** The instruction that can issue an exception. */
+       ir_node *block;      /** The block to call then. */
+} exc_entry;
+
 /**
  * Block-walker:
- * Sets labels for control flow nodes (jump target)
+ * Sets labels for control flow nodes (jump target).
+ * Links control predecessors to there destination blocks.
  */
 static void ia32_gen_labels(ir_node *block, void *data)
 {
+       exc_entry **exc_list = data;
        ir_node *pred;
-       int n = get_Block_n_cfgpreds(block);
-       (void) data;
+       int     n;
 
-       for (n--; n >= 0; n--) {
+       for (n = get_Block_n_cfgpreds(block) - 1; n >= 0; --n) {
                pred = get_Block_cfgpred(block, n);
                set_irn_link(pred, block);
+
+               pred = skip_Proj(pred);
+               if (is_ia32_irn(pred) && get_ia32_exc_label(pred)) {
+                       exc_entry e;
+
+                       e.exc_instr = pred;
+                       e.block     = block;
+                       ARR_APP1(exc_entry, *exc_list, e);
+                       set_irn_link(pred, block);
+               }
        }
 }
 
 /**
- * Emit an exception label if the current instruction can fail.
+ * Assign and emit an exception label if the current instruction can fail.
  */
-void ia32_emit_exc_label(const ir_node *node)
+void ia32_assign_exc_label(ir_node *node)
 {
        if (get_ia32_exc_label(node)) {
-               be_emit_irprintf(".EXL%u\n", 0);
+               /* assign a new ID to the instruction */
+               set_ia32_exc_label_id(node, ++exc_label_id);
+               /* print it */
+               ia32_emit_exc_label(node);
+               be_emit_char(':');
+               be_emit_pad_comment();
+               be_emit_cstring("/* exception to Block ");
+               ia32_emit_cfop_target(node);
+               be_emit_cstring(" */\n");
                be_emit_write_line();
        }
 }
 
+/**
+ * Compare two exception_entries.
+ */
+static int cmp_exc_entry(const void *a, const void *b) {
+       const exc_entry *ea = a;
+       const exc_entry *eb = b;
+
+       if (get_ia32_exc_label_id(ea->exc_instr) < get_ia32_exc_label_id(eb->exc_instr))
+               return -1;
+       return +1;
+}
+
 /**
  * Main driver. Emits the code for one routine.
  */
 void ia32_gen_routine(ia32_code_gen_t *ia32_cg, ir_graph *irg)
 {
-       ir_node   *block;
-       ir_node   *last_block = NULL;
        ir_entity *entity     = get_irg_entity(irg);
+       exc_entry *exc_list   = NEW_ARR_F(exc_entry, 0);
        int i, n;
 
        cg       = ia32_cg;
-       isa      = (const ia32_isa_t*) cg->arch_env->isa;
+       isa      = (const ia32_isa_t*) cg->arch_env;
        arch_env = cg->arch_env;
        do_pic   = cg->birg->main_env->options->pic;
 
@@ -2114,26 +2202,48 @@ void ia32_gen_routine(ia32_code_gen_t *ia32_cg, ir_graph *irg)
        be_dbg_method_begin(entity, be_abi_get_stack_layout(cg->birg->abi));
        be_gas_emit_function_prolog(entity, ia32_cg_config.function_alignment);
 
-       irg_block_walk_graph(irg, ia32_gen_labels, NULL, NULL);
+       /* we use links to point to target blocks */
+       set_using_irn_link(irg);
+       irg_block_walk_graph(irg, ia32_gen_labels, NULL, &exc_list);
 
+       /* initialize next block links */
        n = ARR_LEN(cg->blk_sched);
-       for (i = 0; i < n;) {
-               ir_node *next_bl;
+       for (i = 0; i < n; ++i) {
+               ir_node *block = cg->blk_sched[i];
+               ir_node *prev  = i > 0 ? cg->blk_sched[i-1] : NULL;
 
-               block   = cg->blk_sched[i];
-               ++i;
-               next_bl = i < n ? cg->blk_sched[i] : NULL;
+               set_irn_link(block, prev);
+       }
+
+       for (i = 0; i < n; ++i) {
+               ir_node *block = cg->blk_sched[i];
 
-               /* set here the link. the emitter expects to find the next block here */
-               set_irn_link(block, next_bl);
-               ia32_gen_block(block, last_block);
-               last_block = block;
+               ia32_gen_block(block);
        }
 
        be_gas_emit_function_epilog(entity);
        be_dbg_method_end();
        be_emit_char('\n');
        be_emit_write_line();
+
+       clear_using_irn_link(irg);
+
+       /* Sort the exception table using the exception label id's.
+          Those are ascending with ascending addresses. */
+       qsort(exc_list, ARR_LEN(exc_list), sizeof(exc_list[0]), cmp_exc_entry);
+       {
+               int i;
+
+               for (i = 0; i < ARR_LEN(exc_list); ++i) {
+                       be_emit_cstring("\t.long ");
+                       ia32_emit_exc_label(exc_list[i].exc_instr);
+                       be_emit_char('\n');
+                       be_emit_cstring("\t.long ");
+                       ia32_emit_block_name(exc_list[i].block);
+                       be_emit_char('\n');
+               }
+       }
+       DEL_ARR_F(exc_list);
 }
 
 void ia32_init_emitter(void)