static void ix86_adjust_counter PARAMS ((rtx, HOST_WIDE_INT));
static rtx ix86_expand_aligntest PARAMS ((rtx, int));
static void ix86_expand_strlensi_unroll_1 PARAMS ((rtx, rtx));
+static void ix86_output_main_function_alignment_hack PARAMS ((FILE *f, int));
struct ix86_address
{
HOST_WIDE_INT));
# undef TARGET_ASM_FUNCTION_PROLOGUE
# define TARGET_ASM_FUNCTION_PROLOGUE ix86_osf_output_function_prologue
+#else
+# undef TARGET_ASM_FUNCTION_PROLOGUE
+# define TARGET_ASM_FUNCTION_PROLOGUE \
+ ix86_output_main_function_alignment_hack
#endif
#undef TARGET_ASM_OPEN_PAREN
* (int) GET_MODE_SIZE (mode) / 4);
}
}
+
+/* Most of current runtimes (Jul 2001) do not align stack properly when
+ entering main, so emit an wrapper to align stack before the real main
+ code is called.
+
+ This can eventually go if we manage to fix the runtimes or teach gcc
+ to dynamically align stack in main automatically.
+
+ Adding check to configure is probably not good idea, as binarry can move
+ from one shared library to older. */
+
+static void
+ix86_output_main_function_alignment_hack (file, size)
+ FILE *file;
+ int size ATTRIBUTE_UNUSED;
+{
+ rtx label;
+ char buf[256];
+ /* Check that we see main function with maximally 8 bytes of arguments.
+ if so, emit the hack to align stack for runtimes, where this constraint
+ is broken. */
+ if (strcmp (cfun->name, "main"))
+ return;
+ if (cfun->pops_args || cfun->args_size > 12)
+ return;
+ if (PREFERRED_STACK_BOUNDARY <= 2)
+ return;
+ label = gen_label_rtx ();
+ fprintf (file, "\tpushl\t%%ebp\n");
+ fprintf (file, "\tmovl\t%%esp, %%ebp\n");
+ fprintf (file, "\tandl\t$0xfffffff0, %%esp\n");
+ fprintf (file, "\tpushl\t%%ebp\n");
+ fprintf (file, "\tpushl\t16(%%ebp)\n");
+ fprintf (file, "\tpushl\t12(%%ebp)\n");
+ fprintf (file, "\tpushl\t8(%%ebp)\n");
+ fprintf (file, "\tcall\t");
+ ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (label));
+ assemble_name (file, buf);
+ fprintf (file, "\n\tleave\n");
+ fprintf (file, "\tret\n");
+ ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (label));
+}
}
}
\f
+/* Mark the back edges in DFS traversal.
+ Return non-zero if a loop (natural or otherwise) is present.
+ Inspired by Depth_First_Search_PP described in:
+
+ Advanced Compiler Design and Implementation
+ Steven Muchnick
+ Morgan Kaufmann, 1997
+
+ and heavily borrowed from flow_depth_first_order_compute. */
+
+bool
+mark_dfs_back_edges ()
+{
+ edge *stack;
+ int *pre;
+ int *post;
+ int sp;
+ int prenum = 1;
+ int postnum = 1;
+ sbitmap visited;
+ bool found = false;
+
+ /* Allocate the preorder and postorder number arrays. */
+ pre = (int *) xcalloc (n_basic_blocks, sizeof (int));
+ post = (int *) xcalloc (n_basic_blocks, sizeof (int));
+
+ /* Allocate stack for back-tracking up CFG. */
+ stack = (edge *) xmalloc ((n_basic_blocks + 1) * sizeof (edge));
+ sp = 0;
+
+ /* Allocate bitmap to track nodes that have been visited. */
+ visited = sbitmap_alloc (n_basic_blocks);
+
+ /* None of the nodes in the CFG have been visited yet. */
+ sbitmap_zero (visited);
+
+ /* Push the first edge on to the stack. */
+ stack[sp++] = ENTRY_BLOCK_PTR->succ;
+
+ while (sp)
+ {
+ edge e;
+ basic_block src;
+ basic_block dest;
+
+ /* Look at the edge on the top of the stack. */
+ e = stack[sp - 1];
+ src = e->src;
+ dest = e->dest;
+ e->flags &= ~EDGE_DFS_BACK;
+
+ /* Check if the edge destination has been visited yet. */
+ if (dest != EXIT_BLOCK_PTR && ! TEST_BIT (visited, dest->index))
+ {
+ /* Mark that we have visited the destination. */
+ SET_BIT (visited, dest->index);
+
+ pre[dest->index] = prenum++;
+
+ if (dest->succ)
+ {
+ /* Since the DEST node has been visited for the first
+ time, check its successors. */
+ stack[sp++] = dest->succ;
+ }
+ else
+ post[dest->index] = postnum++;
+ }
+ else
+ {
+ if (dest != EXIT_BLOCK_PTR && src != ENTRY_BLOCK_PTR
+ && pre[src->index] >= pre[dest->index]
+ && post[dest->index] == 0)
+ e->flags |= EDGE_DFS_BACK, found = true;
+
+ if (! e->succ_next && src != ENTRY_BLOCK_PTR)
+ post[src->index] = postnum++;
+
+ if (e->succ_next)
+ stack[sp - 1] = e->succ_next;
+ else
+ sp--;
+ }
+ }
+
+ free (pre);
+ free (post);
+ free (stack);
+ sbitmap_free (visited);
+
+ return found;
+}
+\f
/* Split a block BB after insn INSN creating a new fallthru edge.
Return the new edge. Note that to keep other parts of the compiler happy,
this function renumbers all the basic blocks so that the new
if (e->flags)
{
static const char * const bitnames[] = {
- "fallthru", "crit", "ab", "abcall", "eh", "fake"
+ "fallthru", "crit", "ab", "abcall", "eh", "fake", "dfs_back"
};
int comma = 0;
int i, flags = e->flags;
typedef struct block_info_def
{
struct stack_def stack_in; /* Input stack configuration. */
+ struct stack_def stack_out; /* Output stack configuration. */
HARD_REG_SET out_reg_set; /* Stack regs live on output. */
int done; /* True if block already converted. */
+ int predecesors; /* Number of predecesors that needs
+ to be visited. */
} *block_info;
#define BLOCK_INFO(B) ((block_info) (B)->aux)
static void print_stack PARAMS ((FILE *, stack));
static rtx next_flags_user PARAMS ((rtx));
static void record_label_references PARAMS ((rtx, rtx));
+static bool compensate_edge PARAMS ((edge, FILE *));
\f
/* Return non-zero if any stack register is mentioned somewhere within PAT. */
find_basic_blocks (first, max_reg_num (), file);
count_or_remove_death_notes (NULL, 1);
life_analysis (first, file, PROP_DEATH_NOTES);
+ mark_dfs_back_edges ();
/* Set up block info for each basic block. */
bi = (block_info) xcalloc ((n_basic_blocks + 1), sizeof (*bi));
for (i = n_basic_blocks - 1; i >= 0; --i)
- BASIC_BLOCK (i)->aux = bi + i;
+ {
+ edge e;
+ basic_block bb = BASIC_BLOCK (i);
+ bb->aux = bi + i;
+ for (e = bb->pred; e; e=e->pred_next)
+ if (!(e->flags & EDGE_DFS_BACK)
+ && e->src != ENTRY_BLOCK_PTR)
+ BLOCK_INFO (bb)->predecesors++;
+ }
EXIT_BLOCK_PTR->aux = bi + n_basic_blocks;
/* Create the replacement registers up front. */
}
}
+/* Adjust the stack of this block on exit to match the stack of the
+ target block, or copy stack info into the stack of the successor
+ of the successor hasn't been processed yet. */
+static bool
+compensate_edge (e, file)
+ edge e;
+ FILE *file;
+{
+ basic_block block = e->src, target = e->dest;
+ block_info bi = BLOCK_INFO (block);
+ struct stack_def regstack, tmpstack;
+ stack target_stack = &BLOCK_INFO (target)->stack_in;
+ int reg;
+
+ current_block = block;
+ regstack = bi->stack_out;
+ if (file)
+ fprintf (file, "Edge %d->%d: ", block->index, target->index);
+
+ if (target_stack->top == -2)
+ {
+ /* The target block hasn't had a stack order selected.
+ We need merely ensure that no pops are needed. */
+ for (reg = regstack.top; reg >= 0; --reg)
+ if (!TEST_HARD_REG_BIT (target_stack->reg_set, regstack.reg[reg]))
+ break;
+
+ if (reg == -1)
+ {
+ if (file)
+ fprintf (file, "new block; copying stack position\n");
+
+ /* change_stack kills values in regstack. */
+ tmpstack = regstack;
+
+ change_stack (block->end, &tmpstack, target_stack, EMIT_AFTER);
+ return false;
+ }
+
+ if (file)
+ fprintf (file, "new block; pops needed\n");
+ }
+ else
+ {
+ if (target_stack->top == regstack.top)
+ {
+ for (reg = target_stack->top; reg >= 0; --reg)
+ if (target_stack->reg[reg] != regstack.reg[reg])
+ break;
+
+ if (reg == -1)
+ {
+ if (file)
+ fprintf (file, "no changes needed\n");
+ return false;
+ }
+ }
+
+ if (file)
+ {
+ fprintf (file, "correcting stack to ");
+ print_stack (file, target_stack);
+ }
+ }
+
+ /* Care for non-call EH edges specially. The normal return path have
+ values in registers. These will be popped en masse by the unwind
+ library. */
+ if ((e->flags & (EDGE_EH | EDGE_ABNORMAL_CALL)) == EDGE_EH)
+ target_stack->top = -1;
+
+ /* Other calls may appear to have values live in st(0), but the
+ abnormal return path will not have actually loaded the values. */
+ else if (e->flags & EDGE_ABNORMAL_CALL)
+ {
+ /* Assert that the lifetimes are as we expect -- one value
+ live at st(0) on the end of the source block, and no
+ values live at the beginning of the destination block. */
+ HARD_REG_SET tmp;
+
+ CLEAR_HARD_REG_SET (tmp);
+ GO_IF_HARD_REG_EQUAL (target_stack->reg_set, tmp, eh1);
+ abort ();
+ eh1:
+
+ SET_HARD_REG_BIT (tmp, FIRST_STACK_REG);
+ GO_IF_HARD_REG_EQUAL (regstack.reg_set, tmp, eh2);
+ abort ();
+ eh2:
+
+ target_stack->top = -1;
+ }
+
+ /* It is better to output directly to the end of the block
+ instead of to the edge, because emit_swap can do minimal
+ insn scheduling. We can do this when there is only one
+ edge out, and it is not abnormal. */
+ else if (block->succ->succ_next == NULL && !(e->flags & EDGE_ABNORMAL))
+ {
+ /* change_stack kills values in regstack. */
+ tmpstack = regstack;
+
+ change_stack (block->end, &tmpstack, target_stack,
+ (GET_CODE (block->end) == JUMP_INSN
+ ? EMIT_BEFORE : EMIT_AFTER));
+ }
+ else
+ {
+ rtx seq, after;
+
+ /* We don't support abnormal edges. Global takes care to
+ avoid any live register across them, so we should never
+ have to insert instructions on such edges. */
+ if (e->flags & EDGE_ABNORMAL)
+ abort ();
+
+ current_block = NULL;
+ start_sequence ();
+
+ /* ??? change_stack needs some point to emit insns after.
+ Also needed to keep gen_sequence from returning a
+ pattern as opposed to a sequence, which would lose
+ REG_DEAD notes. */
+ after = emit_note (NULL, NOTE_INSN_DELETED);
+
+ tmpstack = regstack;
+ change_stack (after, &tmpstack, target_stack, EMIT_BEFORE);
+
+ seq = gen_sequence ();
+ end_sequence ();
+
+ insert_insn_on_edge (seq, e);
+ return true;
+ }
+ return false;
+}
+
/* Convert stack register references in one block. */
static int
FILE *file;
basic_block block;
{
- struct stack_def regstack, tmpstack;
+ struct stack_def regstack;
block_info bi = BLOCK_INFO (block);
int inserted, reg;
rtx insn, next;
- edge e;
+ edge e, beste = NULL;
- current_block = block;
+ inserted = 0;
+
+ /* Find the edge we will copy stack from. It should be the most frequent
+ one as it will get cheapest after compensation code is generated,
+ if multiple such exists, take one with largest count, preffer critical
+ one (as splitting critical edges is more expensive), or one with lowest
+ index, to avoid random changes with different orders of the edges. */
+ for (e = block->pred; e ; e = e->pred_next)
+ {
+ if (e->flags & EDGE_DFS_BACK)
+ ;
+ else if (! beste)
+ beste = e;
+ else if (EDGE_FREQUENCY (beste) < EDGE_FREQUENCY (e))
+ beste = e;
+ else if (beste->count < e->count)
+ beste = e;
+ else if (beste->count > e->count)
+ ;
+ else if ((e->flags & EDGE_CRITICAL) != (beste->flags & EDGE_CRITICAL))
+ {
+ if (e->flags & EDGE_CRITICAL)
+ beste = e;
+ }
+ else if (e->src->index < beste->src->index)
+ beste = e;
+ }
+
+ /* Entry block does have stack already initialized. */
+ if (bi->stack_in.top == -2)
+ inserted |= compensate_edge (beste, file);
+ else
+ beste = NULL;
+ current_block = block;
+
if (file)
{
fprintf (file, "\nBasic block %d\nInput stack: ", block->index);
GO_IF_HARD_REG_EQUAL (regstack.reg_set, bi->out_reg_set, win);
abort ();
win:
+ bi->stack_out = regstack;
- /* Adjust the stack of this block on exit to match the stack of the
- target block, or copy stack info into the stack of the successor
- of the successor hasn't been processed yet. */
- inserted = 0;
+ /* Compensate the back edges, as those wasn't visited yet. */
for (e = block->succ; e ; e = e->succ_next)
{
- basic_block target = e->dest;
- stack target_stack = &BLOCK_INFO (target)->stack_in;
-
- if (file)
- fprintf (file, "Edge to block %d: ", target->index);
-
- if (target_stack->top == -2)
- {
- /* The target block hasn't had a stack order selected.
- We need merely ensure that no pops are needed. */
- for (reg = regstack.top; reg >= 0; --reg)
- if (! TEST_HARD_REG_BIT (target_stack->reg_set,
- regstack.reg[reg]))
- break;
-
- if (reg == -1)
- {
- if (file)
- fprintf (file, "new block; copying stack position\n");
-
- /* change_stack kills values in regstack. */
- tmpstack = regstack;
-
- change_stack (block->end, &tmpstack,
- target_stack, EMIT_AFTER);
- continue;
- }
-
- if (file)
- fprintf (file, "new block; pops needed\n");
- }
- else
+ if (e->flags & EDGE_DFS_BACK
+ || (e->dest == EXIT_BLOCK_PTR))
{
- if (target_stack->top == regstack.top)
- {
- for (reg = target_stack->top; reg >= 0; --reg)
- if (target_stack->reg[reg] != regstack.reg[reg])
- break;
-
- if (reg == -1)
- {
- if (file)
- fprintf (file, "no changes needed\n");
- continue;
- }
- }
-
- if (file)
- {
- fprintf (file, "correcting stack to ");
- print_stack (file, target_stack);
- }
- }
-
- /* Care for non-call EH edges specially. The normal return path have
- values in registers. These will be popped en masse by the unwind
- library. */
- if ((e->flags & (EDGE_EH | EDGE_ABNORMAL_CALL)) == EDGE_EH)
- target_stack->top = -1;
-
- /* Other calls may appear to have values live in st(0), but the
- abnormal return path will not have actually loaded the values. */
- else if (e->flags & EDGE_ABNORMAL_CALL)
- {
- /* Assert that the lifetimes are as we expect -- one value
- live at st(0) on the end of the source block, and no
- values live at the beginning of the destination block. */
- HARD_REG_SET tmp;
-
- CLEAR_HARD_REG_SET (tmp);
- GO_IF_HARD_REG_EQUAL (target_stack->reg_set, tmp, eh1);
- abort();
- eh1:
-
- SET_HARD_REG_BIT (tmp, FIRST_STACK_REG);
- GO_IF_HARD_REG_EQUAL (regstack.reg_set, tmp, eh2);
- abort();
- eh2:
-
- target_stack->top = -1;
- }
-
- /* It is better to output directly to the end of the block
- instead of to the edge, because emit_swap can do minimal
- insn scheduling. We can do this when there is only one
- edge out, and it is not abnormal. */
- else if (block->succ->succ_next == NULL
- && ! (e->flags & EDGE_ABNORMAL))
- {
- /* change_stack kills values in regstack. */
- tmpstack = regstack;
-
- change_stack (block->end, &tmpstack, target_stack,
- (GET_CODE (block->end) == JUMP_INSN
- ? EMIT_BEFORE : EMIT_AFTER));
+ if (!BLOCK_INFO (e->dest)->done
+ && e->dest != block)
+ abort ();
+ inserted |= compensate_edge (e, file);
}
- else
+ }
+ for (e = block->pred; e ; e = e->pred_next)
+ {
+ if (e != beste && !(e->flags & EDGE_DFS_BACK)
+ && e->src != ENTRY_BLOCK_PTR)
{
- rtx seq, after;
-
- /* We don't support abnormal edges. Global takes care to
- avoid any live register across them, so we should never
- have to insert instructions on such edges. */
- if (e->flags & EDGE_ABNORMAL)
+ if (!BLOCK_INFO (e->src)->done)
abort ();
-
- current_block = NULL;
- start_sequence ();
-
- /* ??? change_stack needs some point to emit insns after.
- Also needed to keep gen_sequence from returning a
- pattern as opposed to a sequence, which would lose
- REG_DEAD notes. */
- after = emit_note (NULL, NOTE_INSN_DELETED);
-
- tmpstack = regstack;
- change_stack (after, &tmpstack, target_stack, EMIT_BEFORE);
-
- seq = gen_sequence ();
- end_sequence ();
-
- insert_insn_on_edge (seq, e);
- inserted = 1;
- current_block = block;
+ inserted |= compensate_edge (e, file);
}
}
sp = stack;
*sp++ = block;
- BLOCK_INFO (block)->done = 1;
inserted = 0;
do
block = *--sp;
inserted |= convert_regs_1 (file, block);
+ BLOCK_INFO (block)->done = 1;
for (e = block->succ; e ; e = e->succ_next)
- if (! BLOCK_INFO (e->dest)->done)
+ if (! (e->flags & EDGE_DFS_BACK))
{
- *sp++ = e->dest;
- BLOCK_INFO (e->dest)->done = 1;
+ BLOCK_INFO (e->dest)->predecesors--;
+ if (!BLOCK_INFO (e->dest)->predecesors)
+ *sp++ = e->dest;
}
}
while (sp != stack);