/*
- * Copyright (C) 1995-2007 University of Karlsruhe. All right reserved.
+ * Copyright (C) 1995-2008 University of Karlsruhe. All right reserved.
*
* This file is part of libFirm.
*
#include "execfreq.h"
#include "error.h"
#include "raw_bitset.h"
+#include "dbginfo.h"
#include "../besched_t.h"
#include "../benode_t.h"
#include "../beemitter.h"
#include "../begnuas.h"
#include "../beirg_t.h"
+#include "../be_dbgout.h"
#include "ia32_emitter.h"
#include "gen_ia32_emitter.h"
#include "ia32_nodes_attr.h"
#include "ia32_new_nodes.h"
#include "ia32_map_regs.h"
+#include "ia32_architecture.h"
#include "bearch_ia32_t.h"
DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
static const arch_env_t *arch_env;
static const ia32_isa_t *isa;
static ia32_code_gen_t *cg;
+static int do_pic;
+static char pic_base_label[128];
/**
* Returns the register at in position pos.
switch(get_mode_size_bits(mode)) {
case 32: be_emit_char('s'); return;
case 64: be_emit_char('l'); return;
- case 80: be_emit_char('t'); return;
+ case 80:
+ case 96: be_emit_char('t'); return;
}
} else {
assert(mode_is_int(mode) || mode_is_reference(mode));
void ia32_emit_x87_mode_suffix(const ir_node *node)
{
ir_mode *mode = get_ia32_ls_mode(node);
- if(mode != NULL)
+ assert(mode != NULL);
+ /* we only need to emit the mode on address mode */
+ if(get_ia32_op_type(node) != ia32_Normal)
ia32_emit_mode_suffix_mode(mode);
}
-static
-char get_xmm_mode_suffix(ir_mode *mode)
+static char get_xmm_mode_suffix(ir_mode *mode)
{
assert(mode_is_float(mode));
switch(get_mode_size_bits(mode)) {
}
}
-static
-void ia32_emit_function_object(const char *name)
-{
- switch (be_gas_flavour) {
- case GAS_FLAVOUR_NORMAL:
- be_emit_cstring("\t.type\t");
- be_emit_string(name);
- be_emit_cstring(", @function\n");
- be_emit_write_line();
- break;
- case GAS_FLAVOUR_MINGW:
- be_emit_cstring("\t.def\t");
- be_emit_string(name);
- be_emit_cstring(";\t.scl\t2;\t.type\t32;\t.endef\n");
- be_emit_write_line();
- break;
- default:
- break;
- }
-}
-
-static
-void ia32_emit_function_size(const char *name)
-{
- switch (be_gas_flavour) {
- case GAS_FLAVOUR_NORMAL:
- be_emit_cstring("\t.size\t");
- be_emit_string(name);
- be_emit_cstring(", .-");
- be_emit_string(name);
- be_emit_char('\n');
- be_emit_write_line();
- break;
- default:
- break;
- }
-}
-
-
void ia32_emit_source_register_or_immediate(const ir_node *node, int pos)
{
ir_node *in = get_irn_n(node, pos);
}
}
-void ia32_emit_am_or_dest_register(const ir_node *node,
- int pos) {
- if(get_ia32_op_type(node) == ia32_Normal) {
- ia32_emit_dest_register(node, pos);
- } else {
- assert(get_ia32_op_type(node) == ia32_AddrModeD);
- ia32_emit_am(node);
- }
-}
-
/**
* Emits registers and/or address mode of a unary operation.
*/
}
}
+static void ia32_emit_entity(ir_entity *entity, int no_pic_adjust)
+{
+ ident *id;
+
+ set_entity_backend_marked(entity, 1);
+ id = get_entity_ld_ident(entity);
+ be_emit_ident(id);
+
+ if (get_entity_owner(entity) == get_tls_type()) {
+ if (get_entity_visibility(entity) == visibility_external_allocated) {
+ be_emit_cstring("@INDNTPOFF");
+ } else {
+ be_emit_cstring("@NTPOFF");
+ }
+ }
+
+ if (!no_pic_adjust && do_pic) {
+ /* TODO: only do this when necessary */
+ be_emit_char('-');
+ be_emit_string(pic_base_label);
+ }
+}
+
/**
* Emits address mode.
*/
/* emit offset */
if (ent != NULL) {
- ident *id;
-
- set_entity_backend_marked(ent, 1);
- id = get_entity_ld_ident(ent);
if (is_ia32_am_sc_sign(node))
be_emit_char('-');
- be_emit_ident(id);
-
- if(get_entity_owner(ent) == get_tls_type()) {
- if (get_entity_visibility(ent) == visibility_external_allocated) {
- be_emit_cstring("@INDNTPOFF");
- } else {
- be_emit_cstring("@NTPOFF");
- }
- }
+ ia32_emit_entity(ent, 0);
}
if(offs != 0) {
}
}
+static void emit_ia32_IMul(const ir_node *node)
+{
+ ir_node *left = get_irn_n(node, n_ia32_IMul_left);
+ const arch_register_t *out_reg = get_out_reg(node, pn_ia32_IMul_res);
+
+ be_emit_cstring("\timul");
+ ia32_emit_mode_suffix(node);
+ be_emit_char(' ');
+
+ ia32_emit_binop(node);
+
+ /* do we need the 3-address form? */
+ if(is_ia32_NoReg_GP(left) ||
+ get_in_reg(node, n_ia32_IMul_left) != out_reg) {
+ be_emit_cstring(", ");
+ emit_register(out_reg, get_ia32_ls_mode(node));
+ }
+ be_emit_finish_line_gas(node);
+}
+
/*************************************************
* _ _ _
* (_) | | |
}
flags_attr = get_ia32_attr_const(cmp);
- if(flags_attr->data.cmp_flipped)
+ if(flags_attr->data.ins_permuted)
pnc = get_mirrored_pnc(pnc);
pnc |= ia32_pn_Cmp_float;
} else if(is_ia32_Ucomi(flags) || is_ia32_Fucomi(flags)
|| is_ia32_Fucompi(flags)) {
flags_attr = get_ia32_attr_const(flags);
- if(flags_attr->data.cmp_flipped)
+ if(flags_attr->data.ins_permuted)
pnc = get_mirrored_pnc(pnc);
pnc |= ia32_pn_Cmp_float;
} else {
+#if 0
assert(is_ia32_Cmp(flags) || is_ia32_Test(flags)
|| is_ia32_Cmp8Bit(flags) || is_ia32_Test8Bit(flags));
+#endif
flags_attr = get_ia32_attr_const(flags);
- if(flags_attr->data.cmp_flipped)
+ if(flags_attr->data.ins_permuted)
pnc = get_mirrored_pnc(pnc);
if(flags_attr->data.cmp_unsigned)
pnc |= ia32_pn_Cmp_unsigned;
void ia32_emit_cmp_suffix_node(const ir_node *node,
int flags_pos)
{
- pn_Cmp pnc = get_ia32_pncode(node);
+ const ia32_attr_t *attr = get_ia32_attr_const(node);
+
+ pn_Cmp pnc = get_ia32_condcode(node);
pnc = determine_final_pnc(node, flags_pos, pnc);
+ if(attr->data.ins_permuted) {
+ if(pnc & ia32_pn_Cmp_float) {
+ pnc = get_negated_pnc(pnc, mode_F);
+ } else {
+ pnc = get_negated_pnc(pnc, mode_Iu);
+ }
+ }
+
ia32_emit_cmp_suffix(pnc);
}
/**
* Returns the target block for a control flow node.
*/
-static
-ir_node *get_cfop_target_block(const ir_node *irn) {
+static ir_node *get_cfop_target_block(const ir_node *irn) {
return get_irn_link(irn);
}
/**
* Emits a block label for the given block.
*/
-static
-void ia32_emit_block_name(const ir_node *block)
+static void ia32_emit_block_name(const ir_node *block)
{
if (has_Block_label(block)) {
be_emit_string(be_gas_label_prefix());
*/
static void emit_ia32_Jcc(const ir_node *node)
{
+ int need_parity_label = 0;
const ir_node *proj_true;
const ir_node *proj_false;
const ir_node *block;
const ir_node *next_block;
- pn_Cmp pnc = get_ia32_pncode(node);
+ pn_Cmp pnc = get_ia32_condcode(node);
pnc = determine_final_pnc(node, 0, pnc);
/* Some floating point comparisons require a test of the parity flag,
* which indicates that the result is unordered */
switch (pnc & 15) {
- case pn_Cmp_Uo:
+ case pn_Cmp_Uo: {
be_emit_cstring("\tjp ");
ia32_emit_cfop_target(proj_true);
be_emit_finish_line_gas(proj_true);
break;
+ }
case pn_Cmp_Leg:
be_emit_cstring("\tjnp ");
case pn_Cmp_Eq:
case pn_Cmp_Lt:
case pn_Cmp_Le:
- be_emit_cstring("\tjp ");
- ia32_emit_cfop_target(proj_false);
+ /* we need a local label if the false proj is a fallthrough
+ * as the falseblock might have no label emitted then */
+ if (get_cfop_target_block(proj_false) == next_block) {
+ need_parity_label = 1;
+ be_emit_cstring("\tjp 1f");
+ } else {
+ be_emit_cstring("\tjp ");
+ ia32_emit_cfop_target(proj_false);
+ }
be_emit_finish_line_gas(proj_false);
goto emit_jcc;
be_emit_finish_line_gas(proj_true);
}
+ if(need_parity_label) {
+ be_emit_cstring("1:");
+ be_emit_write_line();
+ }
+
/* the second Proj might be a fallthrough */
if (get_cfop_target_block(proj_false) != next_block) {
be_emit_cstring("\tjmp ");
static void emit_ia32_CMov(const ir_node *node)
{
- const ia32_attr_t *attr = get_ia32_attr_const(node);
- const arch_register_t *out = arch_get_irn_register(arch_env, node);
- pn_Cmp pnc = get_ia32_pncode(node);
- int flipped = attr->data.cmp_flipped;
+ const ia32_attr_t *attr = get_ia32_attr_const(node);
+ int ins_permuted = attr->data.ins_permuted;
+ const arch_register_t *out = arch_get_irn_register(arch_env, node);
+ pn_Cmp pnc = get_ia32_condcode(node);
const arch_register_t *in_true;
const arch_register_t *in_false;
} else if(out == in_true) {
const arch_register_t *tmp;
- flipped = !flipped;
+ assert(get_ia32_op_type(node) == ia32_Normal);
+
+ ins_permuted = !ins_permuted;
tmp = in_true;
in_true = in_false;
be_emit_finish_line_gas(node);
}
- if(flipped) {
+ if(ins_permuted) {
if(pnc & ia32_pn_Cmp_float) {
pnc = get_negated_pnc(pnc, mode_F);
} else {
/**
* Compare two variables of type branch_t. Used to sort all switch cases
*/
-static
-int ia32_cmp_branch_t(const void *a, const void *b) {
+static int ia32_cmp_branch_t(const void *a, const void *b) {
branch_t *b1 = (branch_t *)a;
branch_t *b2 = (branch_t *)b;
* possible otherwise a cmp-jmp cascade). Port from
* cggg ia32 backend
*/
-static
-void emit_ia32_SwitchJmp(const ir_node *node) {
+static void emit_ia32_SwitchJmp(const ir_node *node)
+{
unsigned long interval;
int last_value, i;
long pnc;
+ long default_pn;
jmp_tbl_t tbl;
ir_node *proj;
const ir_edge_t *edge;
tbl.label = xmalloc(SNPRINTF_BUF_LEN);
tbl.label = get_unique_label(tbl.label, SNPRINTF_BUF_LEN, ".TBL_");
tbl.defProj = NULL;
- tbl.num_branches = get_irn_n_edges(node);
+ tbl.num_branches = get_irn_n_edges(node) - 1;
tbl.branches = xcalloc(tbl.num_branches, sizeof(tbl.branches[0]));
tbl.min_value = INT_MAX;
tbl.max_value = INT_MIN;
+ default_pn = get_ia32_condcode(node);
i = 0;
/* go over all proj's and collect them */
foreach_out_edge(node, edge) {
pnc = get_Proj_proj(proj);
- /* create branch entry */
- tbl.branches[i].target = proj;
- tbl.branches[i].value = pnc;
-
- tbl.min_value = pnc < tbl.min_value ? pnc : tbl.min_value;
- tbl.max_value = pnc > tbl.max_value ? pnc : tbl.max_value;
-
/* check for default proj */
- if (pnc == get_ia32_pncode(node)) {
- assert(tbl.defProj == NULL && "found two defProjs at SwitchJmp");
+ if (pnc == default_pn) {
+ assert(tbl.defProj == NULL && "found two default Projs at SwitchJmp");
tbl.defProj = proj;
+ } else {
+ tbl.min_value = pnc < tbl.min_value ? pnc : tbl.min_value;
+ tbl.max_value = pnc > tbl.max_value ? pnc : tbl.max_value;
+
+ /* create branch entry */
+ tbl.branches[i].target = proj;
+ tbl.branches[i].value = pnc;
+ ++i;
}
- i++;
}
+ assert(i == tbl.num_branches);
/* sort the branches by their number */
qsort(tbl.branches, tbl.num_branches, sizeof(tbl.branches[0]), ia32_cmp_branch_t);
be_emit_char('$');
if(attr->symconst != NULL) {
- ident *id = get_entity_ld_ident(attr->symconst);
-
- if(attr->attr.data.am_sc_sign)
+ if(attr->sc_sign)
be_emit_char('-');
- be_emit_ident(id);
+ ia32_emit_entity(attr->symconst, 0);
}
if(attr->symconst == NULL || attr->offset != 0) {
if(attr->symconst != NULL) {
}
}
+/**
+ * Emit an inline assembler operand.
+ *
+ * @param node the ia32_ASM node
+ * @param s points to the operand (a %c)
+ *
+ * @return pointer to the first char in s NOT in the current operand
+ */
static const char* emit_asm_operand(const ir_node *node, const char *s)
{
+ const ia32_attr_t *ia32_attr = get_ia32_attr_const(node);
+ const ia32_asm_attr_t *attr = CONST_CAST_IA32_ATTR(ia32_asm_attr_t,
+ ia32_attr);
const arch_register_t *reg;
+ const ia32_asm_reg_t *asm_regs = attr->register_map;
+ const ia32_asm_reg_t *asm_reg;
const char *reg_name;
char c;
char modifier = 0;
int num = -1;
- const ia32_attr_t *attr;
- int n_outs;
int p;
assert(*s == '%');
s += p;
}
+ if(num < 0 || num >= ARR_LEN(asm_regs)) {
+ ir_fprintf(stderr, "Error: Custom assembler references invalid "
+ "input/output (%+F)\n", node);
+ return s;
+ }
+ asm_reg = & asm_regs[num];
+ assert(asm_reg->valid);
+
/* get register */
- attr = get_ia32_attr_const(node);
- n_outs = ARR_LEN(attr->slots);
- if(num < n_outs) {
- reg = get_out_reg(node, num);
+ if(asm_reg->use_input == 0) {
+ reg = get_out_reg(node, asm_reg->inout_pos);
} else {
- ir_node *pred;
- int in = num - n_outs;
- if(in >= get_irn_arity(node)) {
- ir_fprintf(stderr, "Warning: Invalid input %d specified in asm "
- "op (%+F)\n", num, node);
- return s;
- }
- pred = get_irn_n(node, in);
+ ir_node *pred = get_irn_n(node, asm_reg->inout_pos);
+
/* might be an immediate value */
if(is_ia32_Immediate(pred)) {
emit_ia32_Immediate(pred);
return s;
}
- reg = get_in_reg(node, in);
+ reg = get_in_reg(node, asm_reg->inout_pos);
}
if(reg == NULL) {
ir_fprintf(stderr, "Warning: no register assigned for %d asm op "
return s;
}
+ if(asm_reg->memory) {
+ be_emit_char('(');
+ }
+
/* emit it */
- be_emit_char('%');
- switch(modifier) {
- case 0:
- reg_name = arch_register_get_name(reg);
- break;
- case 'b':
- reg_name = ia32_get_mapped_reg_name(isa->regs_8bit, reg);
- break;
- case 'h':
- reg_name = ia32_get_mapped_reg_name(isa->regs_8bit_high, reg);
- break;
- case 'w':
- reg_name = ia32_get_mapped_reg_name(isa->regs_16bit, reg);
- break;
- default:
- panic("Invalid asm op modifier");
+ if(modifier != 0) {
+ be_emit_char('%');
+ switch(modifier) {
+ case 'b':
+ reg_name = ia32_get_mapped_reg_name(isa->regs_8bit, reg);
+ break;
+ case 'h':
+ reg_name = ia32_get_mapped_reg_name(isa->regs_8bit_high, reg);
+ break;
+ case 'w':
+ reg_name = ia32_get_mapped_reg_name(isa->regs_16bit, reg);
+ break;
+ default:
+ panic("Invalid asm op modifier");
+ }
+ be_emit_string(reg_name);
+ } else {
+ emit_register(reg, asm_reg->mode);
+ }
+
+ if(asm_reg->memory) {
+ be_emit_char(')');
}
- be_emit_string(reg_name);
return s;
}
/**
* Emit movsb/w instructions to make mov count divideable by 4
*/
-static void emit_CopyB_prolog(int rem) {
+static void emit_CopyB_prolog(unsigned size) {
be_emit_cstring("\tcld");
be_emit_finish_line_gas(NULL);
- switch(rem) {
+ switch (size) {
case 1:
be_emit_cstring("\tmovsb");
be_emit_finish_line_gas(NULL);
*/
static void emit_ia32_CopyB(const ir_node *node)
{
- int rem = get_ia32_pncode(node);
+ unsigned size = get_ia32_copyb_size(node);
- emit_CopyB_prolog(rem);
+ emit_CopyB_prolog(size);
be_emit_cstring("\trep movsd");
be_emit_finish_line_gas(node);
*/
static void emit_ia32_CopyB_i(const ir_node *node)
{
- int size = get_ia32_pncode(node);
+ unsigned size = get_ia32_copyb_size(node);
emit_CopyB_prolog(size & 0x3);
be_emit_finish_line_gas(node);
}
-/**
- * Emits code for an 8Bit Int conversion.
- */
-static void emit_ia32_Conv_I2I8Bit(const ir_node *node)
-{
- emit_ia32_Conv_I2I(node);
-}
-
/*******************************************
* _ _
be_emit_cstring("\tcall ");
if (ent) {
- set_entity_backend_marked(ent, 1);
- be_emit_string(get_entity_ld_name(ent));
+ ia32_emit_entity(ent, 1);
} else {
const arch_register_t *reg = get_in_reg(node, be_pos_Call_ptr);
be_emit_char('*');
emit_sbb( node, in_hi, out_hi);
}
+static void emit_ia32_GetEIP(const ir_node *node)
+{
+ be_emit_cstring("\tcall ");
+ be_emit_string(pic_base_label);
+ be_emit_finish_line_gas(node);
+
+ be_emit_string(pic_base_label);
+ be_emit_cstring(":\n");
+ be_emit_write_line();
+
+ be_emit_cstring("\tpopl ");
+ ia32_emit_dest_register(node, 0);
+ be_emit_char('\n');
+ be_emit_write_line();
+}
+
static void emit_be_Return(const ir_node *node)
{
unsigned pop;
be_emit_cstring("\tret");
pop = be_Return_get_pop(node);
- if(pop > 0) {
+ if (pop > 0 || be_Return_get_emit_pop(node)) {
be_emit_irprintf(" $%d", pop);
}
be_emit_finish_line_gas(node);
* Enters the emitter functions for handled nodes into the generic
* pointer of an opcode.
*/
-static
-void ia32_register_emitters(void) {
+static void ia32_register_emitters(void) {
#define IA32_EMIT2(a,b) op_ia32_##a->ops.generic = (op_func)emit_ia32_##b
#define IA32_EMIT(a) IA32_EMIT2(a,a)
/* other ia32 emitter functions */
IA32_EMIT(Asm);
IA32_EMIT(CMov);
+ IA32_EMIT(IMul);
IA32_EMIT(SwitchJmp);
IA32_EMIT(CopyB);
IA32_EMIT(CopyB_i);
IA32_EMIT(Conv_FP2I);
IA32_EMIT(Conv_FP2FP);
IA32_EMIT(Conv_I2I);
- IA32_EMIT(Conv_I2I8Bit);
+ IA32_EMIT2(Conv_I2I8Bit, Conv_I2I);
IA32_EMIT(Const);
IA32_EMIT(LdTls);
IA32_EMIT(Minus64Bit);
IA32_EMIT(Jcc);
+ IA32_EMIT(GetEIP);
/* benode emitter */
BE_EMIT(Call);
#undef IA32_EMIT
}
-static const char *last_name = NULL;
-static unsigned last_line = -1;
-static unsigned num = -1;
-
-/**
- * Emit the debug support for node node.
- */
-static void ia32_emit_dbg(const ir_node *node)
-{
- dbg_info *db = get_irn_dbg_info(node);
- unsigned lineno;
- const char *fname = be_retrieve_dbg_info(db, &lineno);
-
- if (! cg->birg->main_env->options->stabs_debug_support)
- return;
-
- if (fname) {
- if (last_name != fname) {
- last_line = -1;
- be_dbg_include_begin(cg->birg->main_env->db_handle, fname);
- last_name = fname;
- }
- if (last_line != lineno) {
- char name[64];
-
- snprintf(name, sizeof(name), ".LM%u", ++num);
- last_line = lineno;
- be_dbg_line(cg->birg->main_env->db_handle, lineno, name);
- be_emit_string(name);
- be_emit_cstring(":\n");
- be_emit_write_line();
- }
- }
-}
-
typedef void (*emit_func_ptr) (const ir_node *);
/**
if (op->ops.generic) {
emit_func_ptr func = (emit_func_ptr) op->ops.generic;
- ia32_emit_dbg(node);
+
+ be_dbg_set_dbg_info(get_irn_dbg_info(node));
+
(*func) (node);
} else {
emit_Nothing(node);
be_emit_write_line();
}
-/**
- * Emits gas alignment directives for Functions depended on cpu architecture.
- */
-static void ia32_emit_align_func(cpu_support cpu)
-{
- unsigned align;
- unsigned maximum_skip;
-
- switch (cpu) {
- case arch_i386:
- align = 2;
- break;
- case arch_i486:
- align = 4;
- break;
- case arch_k6:
- align = 5;
- break;
- default:
- align = 4;
- }
- maximum_skip = (1 << align) - 1;
- ia32_emit_alignment(align, maximum_skip);
-}
-
/**
* Emits gas alignment directives for Labels depended on cpu architecture.
*/
-static void ia32_emit_align_label(cpu_support cpu)
+static void ia32_emit_align_label(void)
{
- unsigned align; unsigned maximum_skip;
-
- switch (cpu) {
- case arch_i386:
- align = 2;
- break;
- case arch_i486:
- align = 4;
- break;
- case arch_k6:
- align = 5;
- break;
- default:
- align = 4;
- }
- maximum_skip = (1 << align) - 1;
+ unsigned align = ia32_cg_config.label_alignment;
+ unsigned maximum_skip = ia32_cg_config.label_alignment_max_skip;
ia32_emit_alignment(align, maximum_skip);
}
/**
- * Test wether a block should be aligned.
- * For cpus in the P4/Athlon class it is usefull to align jump labels to
+ * 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.
*/
double block_freq;
double prev_freq = 0; /**< execfreq of the fallthrough block */
double jmp_freq = 0; /**< execfreq of all non-fallthrough blocks */
- cpu_support cpu = isa->opt_arch;
int i, n_cfgpreds;
if(exec_freq == NULL)
return 0;
- if(cpu == arch_i386 || cpu == arch_i486)
+ if(ia32_cg_config.label_alignment_factor <= 0)
return 0;
block_freq = get_block_execfreq(exec_freq, block);
jmp_freq /= prev_freq;
- switch (cpu) {
- case arch_athlon:
- case arch_athlon_64:
- case arch_k6:
- return jmp_freq > 3;
- default:
- return jmp_freq > 2;
- }
+ return jmp_freq > ia32_cg_config.label_alignment_factor;
}
-static void ia32_emit_block_header(ir_node *block, ir_node *prev)
+/**
+ * Return non-zero, if a instruction in a fall-through.
+ */
+static int is_fallthrough(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;
+}
+
+/**
+ * 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_node *prev_block)
{
+ ir_graph *irg = current_ir_graph;
int n_cfgpreds;
- int need_label;
+ int need_label = 1;
int i, arity;
- ir_exec_freq *exec_freq = cg->birg->exec_freq;
+ ir_exec_freq *exec_freq = cg->birg->exec_freq;
+
+ if(block == get_irg_end_block(irg) || block == get_irg_start_block(irg))
+ return;
n_cfgpreds = get_Block_n_cfgpreds(block);
- need_label = (n_cfgpreds != 0);
- if (should_align_block(block, prev)) {
- assert(need_label);
- ia32_emit_align_label(isa->opt_arch);
+ 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_block && is_fallthrough(cfgpred)) {
+ need_label = 0;
+ }
}
- if(need_label) {
+ 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, prev_block)) {
+ ia32_emit_align_label();
+ } else {
+ /* if the predecessor block has no fall-through,
+ we can always align the label. */
+ int i;
+ ir_node *check_node = NULL;
+
+ for (i = n_cfgpreds - 1; i >= 0; --i) {
+ ir_node *cfg_pred = get_Block_cfgpred(block, i);
+
+ if (get_nodes_block(skip_Proj(cfg_pred)) == prev_block) {
+ check_node = cfg_pred;
+ break;
+ }
+ }
+ if (check_node == NULL || !is_fallthrough(check_node))
+ ia32_emit_align_label();
+ }
+ }
+
+ if (need_label) {
ia32_emit_block_name(block);
be_emit_char(':');
be_emit_pad_comment();
- be_emit_cstring(" /* preds:");
-
- /* emit list of pred blocks in comment */
- arity = get_irn_arity(block);
- for (i = 0; i < arity; ++i) {
- ir_node *predblock = get_Block_cfgpred_block(block, i);
- be_emit_irprintf(" %d", get_irn_node_nr(predblock));
- }
+ be_emit_cstring(" /* ");
} else {
be_emit_cstring("\t/* ");
ia32_emit_block_name(block);
be_emit_cstring(": ");
}
+
+ be_emit_cstring("preds:");
+
+ /* emit list of pred blocks in comment */
+ arity = get_irn_arity(block);
+ for (i = 0; i < arity; ++i) {
+ ir_node *predblock = get_Block_cfgpred_block(block, i);
+ be_emit_irprintf(" %d", get_irn_node_nr(predblock));
+ }
if (exec_freq != NULL) {
be_emit_irprintf(" freq: %f",
get_block_execfreq(exec_freq, block));
ia32_emit_block_header(block, last_block);
/* emit the contents of the block */
- ia32_emit_dbg(block);
+ be_dbg_set_dbg_info(get_irn_dbg_info(block));
sched_foreach(block, node) {
ia32_emit_node(node);
}
}
-/**
- * Emits code for function start.
- */
-static void ia32_emit_func_prolog(ir_graph *irg)
-{
- ir_entity *irg_ent = get_irg_entity(irg);
- const char *irg_name = get_entity_ld_name(irg_ent);
- cpu_support cpu = isa->opt_arch;
- const be_irg_t *birg = cg->birg;
-
- be_emit_write_line();
- be_gas_emit_switch_section(GAS_SECTION_TEXT);
- be_dbg_method_begin(birg->main_env->db_handle, irg_ent, be_abi_get_stack_layout(birg->abi));
- ia32_emit_align_func(cpu);
- if (get_entity_visibility(irg_ent) == visibility_external_visible) {
- be_emit_cstring(".global ");
- be_emit_string(irg_name);
- be_emit_char('\n');
- be_emit_write_line();
- }
- ia32_emit_function_object(irg_name);
- be_emit_string(irg_name);
- be_emit_cstring(":\n");
- be_emit_write_line();
-}
-
-/**
- * Emits code for function end
- */
-static void ia32_emit_func_epilog(ir_graph *irg)
-{
- const char *irg_name = get_entity_ld_name(get_irg_entity(irg));
- const be_irg_t *birg = cg->birg;
-
- ia32_emit_function_size(irg_name);
- be_dbg_method_end(birg->main_env->db_handle);
- be_emit_char('\n');
- be_emit_write_line();
-}
-
/**
* Block-walker:
* Sets labels for control flow nodes (jump target)
*/
void ia32_gen_routine(ia32_code_gen_t *ia32_cg, ir_graph *irg)
{
- ir_node *block;
- ir_node *last_block = NULL;
+ ir_node *block;
+ ir_node *last_block = NULL;
+ ir_entity *entity = get_irg_entity(irg);
int i, n;
cg = ia32_cg;
isa = (const ia32_isa_t*) cg->arch_env->isa;
arch_env = cg->arch_env;
+ do_pic = cg->birg->main_env->options->pic;
ia32_register_emitters();
- ia32_emit_func_prolog(irg);
+ get_unique_label(pic_base_label, sizeof(pic_base_label), ".PIC_BASE");
+
+ 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);
n = ARR_LEN(cg->blk_sched);
last_block = block;
}
- ia32_emit_func_epilog(irg);
+ be_gas_emit_function_epilog(entity);
+ be_dbg_method_end();
+ be_emit_char('\n');
+ be_emit_write_line();
}
void ia32_init_emitter(void)