+/*
+ * returns the condition code
+ */
+static const char *get_cmp_suffix(int cmp_code, int unsigned_cmp)
+{
+ assert(cmp2condition_s[cmp_code].num == cmp_code);
+ assert(cmp2condition_u[cmp_code].num == cmp_code);
+
+ return unsigned_cmp ? cmp2condition_u[cmp_code & 7].name : cmp2condition_s[cmp_code & 7].name;
+}
+
+/**
+ * Returns the target block for a control flow node.
+ */
+static ir_node *get_cfop_target_block(const ir_node *irn) {
+ return get_irn_link(irn);
+}
+
+/**
+ * Returns the target label for a control flow node.
+ */
+static char *get_cfop_target(const ir_node *irn, char *buf) {
+ ir_node *bl = get_cfop_target_block(irn);
+
+ snprintf(buf, SNPRINTF_BUF_LEN, BLOCK_PREFIX("%ld"), get_irn_node_nr(bl));
+ return buf;
+}
+
+/** 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
+ */
+static ir_node *get_proj(const ir_node *irn, long proj) {
+ const ir_edge_t *edge;
+ ir_node *src;
+
+ assert(get_irn_mode(irn) == mode_T && "expected mode_T node");
+
+ foreach_out_edge(irn, edge) {
+ src = get_edge_src_irn(edge);
+
+ assert(is_Proj(src) && "Proj expected");
+ if (get_irn_mode(src) == mode_M)
+ continue;
+
+ if (get_Proj_proj(src) == proj)
+ return src;
+ }
+ return NULL;
+}
+
+/**
+ * Emits the jump sequence for a conditional jump (cmp + jmp_true + jmp_false)
+ */
+static void finish_CondJmp(FILE *F, const ir_node *irn, ir_mode *mode) {
+ const ir_node *proj1, *proj2 = NULL;
+ const ir_node *block, *next_bl = NULL;
+ char buf[SNPRINTF_BUF_LEN];
+ char cmd_buf[SNPRINTF_BUF_LEN];
+ char cmnt_buf[SNPRINTF_BUF_LEN];
+ int is_unsigned;
+
+ /* get both Proj's */
+ proj1 = get_proj(irn, pn_Cond_true);
+ assert(proj1 && "CondJmp without true Proj");
+
+ proj2 = get_proj(irn, pn_Cond_false);
+ assert(proj2 && "CondJmp without false Proj");
+
+ /* for now, the code works for scheduled and non-schedules blocks */
+ block = get_nodes_block(irn);
+
+ /* we have a block schedule */
+ next_bl = next_blk_sched(block);
+
+ if (get_cfop_target_block(proj1) == next_bl) {
+ /* exchange both proj's so the second one can be omitted */
+ const ir_node *t = proj1;
+ proj1 = proj2;
+ proj2 = t;
+ }
+
+ /* the first Proj must always be created */
+ is_unsigned = mode_is_float(mode) || ! mode_is_signed(mode);
+ if (get_Proj_proj(proj1) == pn_Cond_true) {
+ snprintf(cmd_buf, SNPRINTF_BUF_LEN, "j%s %s",
+ get_cmp_suffix(get_ia32_pncode(irn), is_unsigned),
+ get_cfop_target(proj1, buf));
+ snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "/* cmp(a, b) == TRUE */");
+ }
+ else {
+ snprintf(cmd_buf, SNPRINTF_BUF_LEN, "j%s %s",
+ get_cmp_suffix(get_negated_pnc(get_ia32_pncode(irn), mode), is_unsigned),
+ get_cfop_target(proj1, buf));
+ snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "/* cmp(a, b) == FALSE */");
+ }
+ IA32_DO_EMIT(irn);
+
+ /* the second Proj might be a fallthrough */
+ if (get_cfop_target_block(proj2) != next_bl) {
+ snprintf(cmd_buf, SNPRINTF_BUF_LEN, "jmp %s", get_cfop_target(proj2, buf));
+ snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "/* otherwise */");
+ }
+ else {
+ cmd_buf[0] = '\0';
+ snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "/* fallthrough %s */", get_cfop_target(proj2, buf));
+ }
+ IA32_DO_EMIT(irn);
+}
+
+/**
+ * Emits code for conditional jump.
+ */
+static void CondJmp_emitter(const ir_node *irn, ia32_emit_env_t *env) {
+ FILE *F = env->out;
+ char cmd_buf[SNPRINTF_BUF_LEN];
+ char cmnt_buf[SNPRINTF_BUF_LEN];
+
+ 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_ia32_res_mode(irn));
+}
+
+/**
+ * Emits code for conditional jump with two variables.
+ */
+static void emit_ia32_CondJmp(const ir_node *irn, ia32_emit_env_t *env) {
+ CondJmp_emitter(irn, env);
+}
+
+/**
+ * Emits code for conditional test and jump.
+ */
+static void TestJmp_emitter(const ir_node *irn, ia32_emit_env_t *env) {
+
+#define IA32_IS_IMMOP (is_ia32_ImmConst(irn) || is_ia32_ImmSymConst(irn))
+
+ FILE *F = env->out;
+ const char *op1 = arch_register_get_name(get_in_reg(irn, 0));
+ const char *op2 = IA32_IS_IMMOP ? get_ia32_cnst(irn) : NULL;
+ char cmd_buf[SNPRINTF_BUF_LEN];
+ char cmnt_buf[SNPRINTF_BUF_LEN];
+
+ if (! op2)
+ op2 = arch_register_get_name(get_in_reg(irn, 1));
+
+ snprintf(cmd_buf, SNPRINTF_BUF_LEN, "test %%%s,%s%s ", op1, IA32_IS_IMMOP ? " " : " %", op2);
+ lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* %+F */", irn);
+
+ IA32_DO_EMIT(irn);
+ finish_CondJmp(F, irn, get_ia32_res_mode(irn));
+
+#undef IA32_IS_IMMOP
+}
+
+/**
+ * Emits code for conditional test and jump with two variables.
+ */
+static void emit_ia32_TestJmp(const ir_node *irn, ia32_emit_env_t *env) {
+ TestJmp_emitter(irn, env);
+}
+
+static void emit_ia32_CJmp(const ir_node *irn, ia32_emit_env_t *env) {
+ FILE *F = env->out;
+ char cmd_buf[SNPRINTF_BUF_LEN];
+ 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 */", irn);
+ IA32_DO_EMIT(irn);
+ finish_CondJmp(F, irn, get_ia32_res_mode(irn));
+}
+
+static void emit_ia32_CJmpAM(const ir_node *irn, ia32_emit_env_t *env) {
+ FILE *F = env->out;
+ char cmd_buf[SNPRINTF_BUF_LEN];
+ 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);
+ IA32_DO_EMIT(irn);
+ finish_CondJmp(F, irn, get_ia32_res_mode(irn));
+}
+
+/**
+ * Emits code for conditional SSE floating point jump with two variables.
+ */
+static void emit_ia32_xCondJmp(ir_node *irn, ia32_emit_env_t *env) {
+ FILE *F = env->out;
+ char cmd_buf[SNPRINTF_BUF_LEN];
+ char cmnt_buf[SNPRINTF_BUF_LEN];
+
+ lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "ucomis%M %s", irn, 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, mode_F);
+
+}
+
+/**
+ * Emits code for conditional x87 floating point jump with two variables.
+ */
+static void emit_ia32_x87CondJmp(ir_node *irn, ia32_emit_env_t *env) {
+ FILE *F = env->out;
+ char cmd_buf[SNPRINTF_BUF_LEN];
+ char cmnt_buf[SNPRINTF_BUF_LEN];
+ ia32_attr_t *attr = get_ia32_attr(irn);
+ const char *reg = attr->x87[1]->name;
+ const char *instr = "fcom";
+ int reverse = 0;
+
+ switch (get_ia32_pncode(irn)) {
+ case iro_ia32_fcomrJmp:
+ reverse = 1;
+ case iro_ia32_fcomJmp:
+ default:
+ instr = "fucom";
+ break;
+ case iro_ia32_fcomrpJmp:
+ reverse = 1;
+ case iro_ia32_fcompJmp:
+ instr = "fucomp";
+ break;
+ case iro_ia32_fcomrppJmp:
+ reverse = 1;
+ case iro_ia32_fcomppJmp:
+ instr = "fucompp";
+ reg = "";
+ break;
+ }
+
+ if (reverse)
+ set_ia32_pncode(irn, (long)get_negated_pnc(get_ia32_pncode(irn), mode_Is));
+
+ snprintf(cmd_buf, SNPRINTF_BUF_LEN, "%s %%%s", instr, reg);
+ lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* %+F */", irn);
+ IA32_DO_EMIT(irn);
+ lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "fnstsw %%ax", irn);
+ snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "/* Store x87 FPU Control Word */");
+ IA32_DO_EMIT(irn);
+ snprintf(cmd_buf, SNPRINTF_BUF_LEN, "sahf");
+ snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "/* Store ah into flags */");
+ IA32_DO_EMIT(irn);
+
+ finish_CondJmp(F, irn, mode_Is);
+}
+
+static void CMov_emitter(ir_node *irn, ia32_emit_env_t *env) {
+ FILE *F = env->out;
+ const lc_arg_env_t *arg_env = ia32_get_arg_env();
+ ir_mode *mode = get_irn_mode(get_irn_n(irn, 0));
+ int is_unsigned = mode_is_float(mode) || ! mode_is_signed(mode);
+ const char *cmp_suffix = get_cmp_suffix(get_ia32_pncode(irn), is_unsigned);
+ int is_PsiCondCMov = is_ia32_PsiCondCMov(irn);
+
+ char cmd_buf[SNPRINTF_BUF_LEN];
+ char cmnt_buf[SNPRINTF_BUF_LEN];
+ const arch_register_t *in1, *in2, *out;
+
+ out = arch_get_irn_register(env->arch_env, irn);
+ in1 = arch_get_irn_register(env->arch_env, get_irn_n(irn, 2 - is_PsiCondCMov));
+ in2 = arch_get_irn_register(env->arch_env, get_irn_n(irn, 3 - is_PsiCondCMov));
+
+ /* we have to emit the cmp first, because the destination register */
+ /* could be one of the compare registers */
+ if (is_ia32_CmpCMov(irn)) {
+ lc_esnprintf(arg_env, cmd_buf, SNPRINTF_BUF_LEN, "cmp %1S, %2S", irn, irn);
+ }
+ else if (is_ia32_xCmpCMov(irn)) {
+ lc_esnprintf(arg_env, cmd_buf, SNPRINTF_BUF_LEN, "ucomis%M %1S, %2S", get_irn_n(irn, 0), irn, irn);
+ }
+ else if (is_PsiCondCMov) {
+ /* omit compare because flags are already set by And/Or */
+ snprintf(cmd_buf, SNPRINTF_BUF_LEN, " ");
+ }
+ else {
+ assert(0 && "unsupported CMov");
+ }
+ snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "/* Psi condition */" );
+ IA32_DO_EMIT(irn);
+
+ if (REGS_ARE_EQUAL(out, in2)) {
+ /* best case: default in == out -> do nothing */
+ }
+ else if (REGS_ARE_EQUAL(out, in1)) {
+ /* true in == out -> need complement compare and exchange true and default in */
+ ir_node *t = get_irn_n(irn, 2);
+ set_irn_n(irn, 2, get_irn_n(irn, 3));
+ set_irn_n(irn, 3, t);
+
+ cmp_suffix = get_cmp_suffix(get_inversed_pnc(get_ia32_pncode(irn)), is_unsigned);
+
+ }
+ else {
+ /* out is different from in: need copy default -> out */
+ lc_esnprintf(arg_env, cmd_buf, SNPRINTF_BUF_LEN, "mov %1D, %4S", irn, irn);
+ lc_esnprintf(arg_env, cmnt_buf, SNPRINTF_BUF_LEN, "/* copy default -> out */" );
+ IA32_DO_EMIT(irn);
+ }