/* global arch_env for lc_printf functions */
static const arch_env_t *arch_env = NULL;
-/* indicates whether blocks are scheduled or not
- (this variable is set automatically) */
-static int have_block_sched = 0;
-
/*************************************************************
* _ _ __ _ _
* (_) | | / _| | | | |
* |_| |_|
*************************************************************/
+/**
+ * returns true if a node has x87 registers
+ */
+static int has_x87_register(const ir_node *n)
+{
+ return get_irn_op(n)->flags & (irop_flag_machine << 1);
+}
+
/* We always pass the ir_node which is a pointer. */
static int ia32_get_arg_type(const lc_arg_occ_t *occ) {
return lc_arg_type_ptr;
*/
static const char *get_ia32_reg_name(ir_node *irn, int pos, enum io_direction in_out) {
const arch_register_t *reg;
- const char *name;
- static char *buf = NULL;
- int len;
if (in_out == IN_REG) {
reg = get_in_reg(irn, pos);
+
+ if (reg->reg_class == &ia32_reg_classes[CLASS_ia32_vfp]) {
+ /* FIXME: works for binop only */
+ assert(2 <= pos && pos <= 3);
+ reg = get_ia32_attr(irn)->x87[pos - 2];
+ }
}
else {
/* destination address mode nodes don't have outputs */
}
reg = get_out_reg(irn, pos);
+ if (reg->reg_class == &ia32_reg_classes[CLASS_ia32_vfp])
+ reg = get_ia32_attr(irn)->x87[pos + 2];
}
-
- name = arch_register_get_name(reg);
-
- if (buf) {
- free(buf);
- }
-
- len = strlen(name) + 2;
- buf = xcalloc(1, len);
-
- snprintf(buf, len, "%%%s", name);
-
- return buf;
+ return arch_register_get_name(reg);
}
/**
buf = get_ia32_reg_name(X, nr, occ->conversion == 'S' ? IN_REG : OUT_REG);
+ /* append the stupid % to register names */
+ lc_appendable_chadd(app, '%');
+ return lc_appendable_snadd(app, buf, strlen(buf));
+}
+
+/**
+ * Get the x87 register name for a node.
+ */
+static int ia32_get_x87_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;
+ ia32_attr_t *attr;
+
+ if (!X)
+ return lc_appendable_snadd(app, "(null)", 6);
+
+ attr = get_ia32_attr(X);
+ buf = attr->x87[nr]->name;
+ lc_appendable_chadd(app, '%');
return lc_appendable_snadd(app, buf, strlen(buf));
}
static const lc_arg_handler_t ia32_reg_handler = { ia32_get_arg_type, ia32_get_reg_name };
static const lc_arg_handler_t ia32_const_handler = { ia32_get_arg_type, ia32_const_to_str };
static const lc_arg_handler_t ia32_mode_handler = { ia32_get_arg_type, ia32_get_mode_suffix };
+ static const lc_arg_handler_t ia32_x87_handler = { ia32_get_arg_type, ia32_get_x87_name };
if(env == NULL) {
/* extend the firm printer */
lc_arg_register(env, "ia32:cnst", 'C', &ia32_const_handler);
lc_arg_register(env, "ia32:offs", 'O', &ia32_const_handler);
lc_arg_register(env, "ia32:mode", 'M', &ia32_mode_handler);
+ lc_arg_register(env, "ia32:x87", 'X', &ia32_x87_handler);
}
return env;
const arch_register_t *in2 = get_in_reg(n, 3);
const arch_register_t *out = PRODUCES_RESULT(n) ? get_out_reg(n, 0) : NULL;
const arch_register_t *in;
+ const char *in_name;
- in = out ? (REGS_ARE_EQUAL(out, in2) ? in1 : in2) : in2;
- out = out ? out : in1;
+ in = out ? (REGS_ARE_EQUAL(out, in2) ? in1 : in2) : in2;
+ out = out ? out : in1;
+ in_name = arch_register_get_name(in);
- snprintf(buf, SNPRINTF_BUF_LEN, "%%%s, %%%s", \
- arch_register_get_name(out), arch_register_get_name(in));
+ if (is_ia32_emit_cl(n)) {
+ assert(REGS_ARE_EQUAL(&ia32_gp_regs[REG_ECX], in) && "shift operation needs ecx");
+ in_name = "cl";
+ }
+
+ snprintf(buf, SNPRINTF_BUF_LEN, "%%%s, %%%s", arch_register_get_name(out), in_name);
}
break;
case ia32_AddrModeS:
- lc_esnprintf(ia32_get_arg_env(), buf, SNPRINTF_BUF_LEN, "%4S, %s", n, ia32_emit_am(n, env));
+ if (is_ia32_ImmConst(n) || is_ia32_ImmSymConst(n)) {
+ assert(! PRODUCES_RESULT(n) && "Source AM with Const must not produce result");
+ snprintf(buf, SNPRINTF_BUF_LEN, "%s, %s", get_ia32_cnst(n), ia32_emit_am(n, env));
+ }
+ else {
+ lc_esnprintf(ia32_get_arg_env(), buf, SNPRINTF_BUF_LEN, "%4S, %s", n, ia32_emit_am(n, env));
+ }
break;
case ia32_AddrModeD:
if (is_ia32_ImmConst(n) || is_ia32_ImmSymConst(n)) {
else {
const arch_register_t *in1 = get_in_reg(n, 2);
ir_mode *mode = get_ia32_res_mode(n);
+ const char *in_name;
+
+ mode = mode ? mode : get_ia32_ls_mode(n);
+ in_name = ia32_get_reg_name_for_mode(env, mode, in1);
+
+ if (is_ia32_emit_cl(n)) {
+ assert(REGS_ARE_EQUAL(&ia32_gp_regs[REG_ECX], in1) && "shift operation needs ecx");
+ in_name = "cl";
+ }
- mode = mode ? mode : get_ia32_ls_mode(n);
- lc_esnprintf(ia32_get_arg_env(), buf, SNPRINTF_BUF_LEN, "%s, %%%s",
- ia32_emit_am(n, env), ia32_get_reg_name_for_mode(env, mode, in1));
+ lc_esnprintf(ia32_get_arg_env(), buf, SNPRINTF_BUF_LEN, "%s, %%%s", ia32_emit_am(n, env), in_name);
}
break;
default:
return buf;
}
+/**
+ * Emits registers and/or address mode of a binary operation.
+ */
+char *ia32_emit_x87_binop(const ir_node *n, ia32_emit_env_t *env) {
+ static char *buf = NULL;
+
+ /* verify that this function is never called on non-AM supporting operations */
+ //assert(get_ia32_am_support(n) != ia32_am_None && "emit binop expects addressmode support");
+
+ if (! buf) {
+ buf = xcalloc(1, SNPRINTF_BUF_LEN);
+ }
+ else {
+ memset(buf, 0, SNPRINTF_BUF_LEN);
+ }
+
+ switch(get_ia32_op_type(n)) {
+ case ia32_Normal:
+ if (is_ia32_ImmConst(n) || is_ia32_ImmSymConst(n)) {
+ lc_esnprintf(ia32_get_arg_env(), buf, SNPRINTF_BUF_LEN, "%3S, %s", n, get_ia32_cnst(n));
+ }
+ else {
+ ia32_attr_t *attr = get_ia32_attr(n);
+ const arch_register_t *in1 = attr->x87[0];
+ const arch_register_t *in2 = attr->x87[1];
+ const arch_register_t *out = attr->x87[2];
+ const arch_register_t *in;
+ const char *in_name;
+
+ in = out ? (REGS_ARE_EQUAL(out, in2) ? in1 : in2) : in2;
+ out = out ? out : in1;
+ in_name = arch_register_get_name(in);
+
+ snprintf(buf, SNPRINTF_BUF_LEN, "%%%s, %%%s", arch_register_get_name(out), in_name);
+ }
+ break;
+ case ia32_AddrModeS:
+ case ia32_AddrModeD:
+ lc_esnprintf(ia32_get_arg_env(), buf, SNPRINTF_BUF_LEN, "%s", ia32_emit_am(n, env));
+ break;
+ default:
+ assert(0 && "unsupported op type");
+ }
+
+#undef PRODUCES_RESULT
+
+ return buf;
+}
+
/**
* Emits registers and/or address mode of a unary operation.
*/
switch(get_ia32_op_type(n)) {
case ia32_Normal:
- lc_esnprintf(ia32_get_arg_env(), buf, SNPRINTF_BUF_LEN, "%1D", n);
+ if (is_ia32_ImmConst(n) || is_ia32_ImmSymConst(n)) {
+ lc_esnprintf(ia32_get_arg_env(), buf, SNPRINTF_BUF_LEN, "%C", n);
+ }
+ else {
+ lc_esnprintf(ia32_get_arg_env(), buf, SNPRINTF_BUF_LEN, "%1D", n);
+ }
break;
case ia32_am_Dest:
snprintf(buf, SNPRINTF_BUF_LEN, ia32_emit_am(n, env));
case 32:
obstack_printf(obst, "DWORD PTR ");
break;
+ case 64:
+ if (has_x87_register(n))
+ /* ARGHHH: stupid gas x87 wants QWORD PTR but SSE must be WITHOUT */
+ obstack_printf(obst, "QWORD PTR ");
+ break;
+ case 80:
+ case 96:
+ obstack_printf(obst, "XWORD PTR ");
+ break;
default:
break;
}
}
- obstack_printf(obst, "[");
+ /* emit address mode symconst */
+ if (get_ia32_am_sc(n)) {
+ if (is_ia32_am_sc_sign(n))
+ obstack_printf(obst, "-");
+ obstack_printf(obst, "%s", get_id_str(get_ia32_am_sc(n)));
+ }
if (am_flav & ia32_B) {
+ obstack_printf(obst, "[");
lc_eoprintf(ia32_get_arg_env(), obst, "%1S", n);
had_output = 1;
}
if (had_output) {
obstack_printf(obst, "+");
}
+ else {
+ obstack_printf(obst, "[");
+ }
lc_eoprintf(ia32_get_arg_env(), obst, "%2S", n);
}
if (am_flav & ia32_O) {
- obstack_printf(obst, get_ia32_am_offs(n));
+ s = get_ia32_am_offs(n);
+
+ if (s) {
+ /* omit explicit + if there was no base or index */
+ if (! had_output) {
+ obstack_printf(obst, "[");
+ if (s[0] == '+')
+ s++;
+ }
+
+ obstack_printf(obst, s);
+ had_output = 1;
+ }
}
- obstack_printf(obst, "] ");
+ if (had_output)
+ obstack_printf(obst, "] ");
size = obstack_object_size(obst);
s = obstack_finish(obst);
{ NULL, pn_Cmp_False }, /* always false */
{ "e", pn_Cmp_Eq }, /* == */
{ "b", pn_Cmp_Lt }, /* < */
- { "be", pn_Cmp_Le }, /* <= */
+ { "be", pn_Cmp_Le }, /* <= */
{ "a", pn_Cmp_Gt }, /* > */
{ "ae", pn_Cmp_Ge }, /* >= */
{ "ne", pn_Cmp_Lg }, /* != */
/** Return the next block in Block schedule */
static ir_node *next_blk_sched(const ir_node *block) {
- return have_block_sched ? get_irn_link(block) : NULL;
+ return get_irn_link(block);
}
/**
snprintf(cmd_buf, SNPRINTF_BUF_LEN, "cmp %s", ia32_emit_binop(irn, env));
lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* %+F */", irn);
IA32_DO_EMIT(irn);
- finish_CondJmp(F, irn, get_irn_mode(get_irn_n(irn, 2)));
+ finish_CondJmp(F, irn, get_ia32_res_mode(irn));
}
/**
CondJmp_emitter(irn, env);
}
-/**
- * Emits code for conditional jump with immediate.
- */
-void emit_ia32_CondJmp_i(const ir_node *irn, ia32_emit_env_t *env) {
- CondJmp_emitter(irn, env);
-}
-
/**
* Emits code for conditional test and jump.
*/
lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* %+F */", irn);
IA32_DO_EMIT(irn);
- finish_CondJmp(F, irn, get_irn_mode(get_irn_n(irn, 0)));
+ finish_CondJmp(F, irn, get_ia32_res_mode(irn));
#undef IA32_IS_IMMOP
}
char cmnt_buf[SNPRINTF_BUF_LEN];
snprintf(cmd_buf, SNPRINTF_BUF_LEN, " ");
- lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* %+F omitted redundant test/cmp */", irn);
+ lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* %+F omitted redundant test */", irn);
IA32_DO_EMIT(irn);
- finish_CondJmp(F, irn, get_irn_mode(get_irn_n(irn, 0)));
+ finish_CondJmp(F, irn, get_ia32_res_mode(irn));
}
static void emit_ia32_CJmpAM(const ir_node *irn, ia32_emit_env_t *env) {
snprintf(cmd_buf, SNPRINTF_BUF_LEN, " ");
lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* %+F omitted redundant test/cmp */", irn);
IA32_DO_EMIT(irn);
- finish_CondJmp(F, irn, get_irn_mode(get_irn_n(irn, 2)));
+ finish_CondJmp(F, irn, get_ia32_res_mode(irn));
}
/*********************************************************
* possible otherwise a cmp-jmp cascade). Port from
* cggg ia32 backend
*/
-void emit_ia32_SwitchJmp(const ir_node *irn, ia32_emit_env_t *emit_env) {
+static void emit_ia32_SwitchJmp(const ir_node *irn, ia32_emit_env_t *emit_env) {
unsigned long interval;
char buf[SNPRINTF_BUF_LEN];
int last_value, i, pn, do_jmp_tbl = 1;
/**
* Emits code for a unconditional jump.
*/
-void emit_Jmp(const ir_node *irn, ia32_emit_env_t *env) {
+static void emit_Jmp(const ir_node *irn, ia32_emit_env_t *env) {
ir_node *block, *next_bl;
FILE *F = env->out;
char buf[SNPRINTF_BUF_LEN], cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN];
/**
* Emits code for a proj -> node
*/
-void emit_Proj(const ir_node *irn, ia32_emit_env_t *env) {
+static void emit_Proj(const ir_node *irn, ia32_emit_env_t *env) {
ir_node *pred = get_Proj_pred(irn);
if (get_irn_op(pred) == op_Start) {
/**
* Emit rep movsd instruction for memcopy.
*/
-void emit_ia32_CopyB(const ir_node *irn, ia32_emit_env_t *emit_env) {
+static void emit_ia32_CopyB(const ir_node *irn, ia32_emit_env_t *emit_env) {
FILE *F = emit_env->out;
tarval *tv = get_ia32_Immop_tarval(irn);
int rem = get_tarval_long(tv);
/**
* Emits unrolled memcopy.
*/
-void emit_ia32_CopyB_i(const ir_node *irn, ia32_emit_env_t *emit_env) {
+static void emit_ia32_CopyB_i(const ir_node *irn, ia32_emit_env_t *emit_env) {
tarval *tv = get_ia32_Immop_tarval(irn);
int size = get_tarval_long(tv);
FILE *F = emit_env->out;
IA32_DO_EMIT(irn);
}
-void emit_ia32_Conv_I2FP(const ir_node *irn, ia32_emit_env_t *emit_env) {
+static void emit_ia32_Conv_I2FP(const ir_node *irn, ia32_emit_env_t *emit_env) {
emit_ia32_Conv_with_FP(irn, emit_env);
}
-void emit_ia32_Conv_FP2I(const ir_node *irn, ia32_emit_env_t *emit_env) {
+static void emit_ia32_Conv_FP2I(const ir_node *irn, ia32_emit_env_t *emit_env) {
emit_ia32_Conv_with_FP(irn, emit_env);
}
-void emit_ia32_Conv_FP2FP(const ir_node *irn, ia32_emit_env_t *emit_env) {
+static void emit_ia32_Conv_FP2FP(const ir_node *irn, ia32_emit_env_t *emit_env) {
emit_ia32_Conv_with_FP(irn, emit_env);
}
/**
* Emits code for an Int conversion.
*/
-void emit_ia32_Conv_I2I(const ir_node *irn, ia32_emit_env_t *emit_env) {
+static void emit_ia32_Conv_I2I(const ir_node *irn, ia32_emit_env_t *emit_env) {
FILE *F = emit_env->out;
const lc_arg_env_t *env = ia32_get_arg_env();
char *move_cmd, *conv_cmd;
IA32_DO_EMIT(irn);
}
+/**
+ * Emits code for an 8Bit Int conversion.
+ */
+void emit_ia32_Conv_I2I8Bit(const ir_node *irn, ia32_emit_env_t *emit_env) {
+ emit_ia32_Conv_I2I(irn, emit_env);
+}
+
+
/*******************************************
* _ _
* | | | |
/**
* Emits a backend call
*/
-void emit_be_Call(const ir_node *irn, ia32_emit_env_t *emit_env) {
+static void emit_be_Call(const ir_node *irn, ia32_emit_env_t *emit_env) {
FILE *F = emit_env->out;
entity *ent = be_Call_get_entity(irn);
char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN];
/**
* Emits code to increase stack pointer.
*/
-void emit_be_IncSP(const ir_node *irn, ia32_emit_env_t *emit_env) {
+static void emit_be_IncSP(const ir_node *irn, ia32_emit_env_t *emit_env) {
FILE *F = emit_env->out;
unsigned offs = be_get_IncSP_offset(irn);
be_stack_dir_t dir = be_get_IncSP_direction(irn);
if (offs) {
lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "add %1S,%s%u", irn,
- (dir == be_stack_dir_along) ? " -" : " ", offs);
+ (dir == be_stack_dir_expand) ? " -" : " ", offs);
lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* %+F (IncSP) */", irn);
}
else {
/**
* Emits code to set stack pointer.
*/
-void emit_be_SetSP(const ir_node *irn, ia32_emit_env_t *emit_env) {
+static void emit_be_SetSP(const ir_node *irn, ia32_emit_env_t *emit_env) {
FILE *F = emit_env->out;
char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN];
/**
* Emits code for Copy.
*/
-void emit_be_Copy(const ir_node *irn, ia32_emit_env_t *emit_env) {
+static void emit_be_Copy(const ir_node *irn, ia32_emit_env_t *emit_env) {
FILE *F = emit_env->out;
char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN];
- lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "mov %1D, %1S", irn, irn);
+ if (mode_is_float(get_irn_mode(irn)))
+ lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "movs%M %1D, %1S", irn, irn, irn);
+ else
+ lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "mov %1D, %1S", irn, irn);
lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* %+F */", irn);
IA32_DO_EMIT(irn);
}
/**
* Emits code for exchange.
*/
-void emit_be_Perm(const ir_node *irn, ia32_emit_env_t *emit_env) {
+static void emit_be_Perm(const ir_node *irn, ia32_emit_env_t *emit_env) {
FILE *F = emit_env->out;
char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN];
IA32_DO_EMIT(irn);
}
+
+
/***********************************************************************************
* _ __ _
* (_) / _| | |
IA32_EMIT(Conv_FP2I);
IA32_EMIT(Conv_FP2FP);
IA32_EMIT(Conv_I2I);
+ IA32_EMIT(Conv_I2I8Bit);
/* benode emitter */
BE_EMIT(Call);
(*emit)(irn, env);
}
else {
- ir_fprintf(F, "\t%35s /* %+F */\n", " ", irn);
+ ir_fprintf(F, "\t%35s /* %+F (%+G) */\n", " ", irn, irn);
}
}
entity *irg_ent = get_irg_entity(irg);
const char *irg_name = get_entity_name(irg_ent);
- fprintf(F, "\t.text\n");
+ fprintf(F, "\t.section\t.text\n");
if (get_entity_visibility(irg_ent) == visibility_external_visible) {
fprintf(F, ".globl %s\n", irg_name);
}
}
}
-typedef struct {
- ir_node *start;
- ir_node *end;
-} anchor;
-
-/**
- * Ext-Block walker: create a block schedule
- */
-static void create_block_list(ir_extblk *blk, void *env) {
- anchor *list = env;
- int i, n;
-
- for (i = 0, n = get_extbb_n_blocks(blk); i < n; ++i) {
- ir_node *block = get_extbb_block(blk, i);
-
- set_irn_link(block, NULL);
- if (list->start)
- set_irn_link(list->end, block);
- else
- list->start = block;
-
- list->end = block;
- }
-}
-
/**
* Main driver. Emits the code for one routine.
*/
void ia32_gen_routine(FILE *F, ir_graph *irg, const ia32_code_gen_t *cg) {
ia32_emit_env_t emit_env;
- anchor list;
ir_node *block;
- emit_env.mod = firm_dbg_register("firm.be.ia32.emitter");
emit_env.out = F;
emit_env.arch_env = cg->arch_env;
emit_env.cg = cg;
emit_env.isa = (ia32_isa_t *)cg->arch_env->isa;
+ FIRM_DBG_REGISTER(emit_env.mod, "firm.be.ia32.emitter");
/* set the global arch_env (needed by print hooks) */
arch_env = cg->arch_env;
ia32_emit_func_prolog(F, irg);
irg_block_walk_graph(irg, ia32_gen_labels, NULL, &emit_env);
- if (cg->opt.extbb) {
- /* schedule extended basic blocks */
+ if (cg->opt.extbb && cg->blk_sched) {
+ int i, n = ARR_LEN(cg->blk_sched);
- compute_extbb(irg);
+ for (i = 0; i < n;) {
+ ir_node *next_bl;
- list.start = NULL;
- list.end = NULL;
- irg_extblock_walk_graph(irg, NULL, create_block_list, &list);
+ block = cg->blk_sched[i];
+ ++i;
+ next_bl = i < n ? cg->blk_sched[i] : NULL;
- have_block_sched = 1;
- for (block = list.start; block; block = get_irn_link(block))
+ /* set here the link. the emitter expects to find the next block here */
+ set_irn_link(block, next_bl);
ia32_gen_block(block, &emit_env);
+ }
}
else {
- /* "normal" block schedule */
-
- have_block_sched = 0;
+ /* "normal" block schedule: Note the get_next_block() returns the NUMBER of the block
+ in the block schedule. As this number should NEVER be equal the next block,
+ we does not need a clear block link here. */
irg_walk_blkwise_graph(irg, NULL, ia32_gen_block, &emit_env);
}