+/**********************************
+ * _____ ____
+ * / ____| | _ \
+ * | | ___ _ __ _ _| |_) |
+ * | | / _ \| '_ \| | | | _ <
+ * | |___| (_) | |_) | |_| | |_) |
+ * \_____\___/| .__/ \__, |____/
+ * | | __/ |
+ * |_| |___/
+ **********************************/
+
+/**
+ * Emit movsb/w instructions to make mov count divideable by 4
+ */
+static void emit_CopyB_prolog(FILE *F, const ir_node *irn, int rem) {
+ char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN];
+
+ ir_fprintf(F, "\t/* memcopy prolog %+F */\n", irn);
+
+ snprintf(cmd_buf, SNPRINTF_BUF_LEN, "cld");
+ snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "/* copy direction forward */");
+
+ switch(rem) {
+ case 1:
+ IA32_DO_EMIT(NULL);
+ snprintf(cmd_buf, SNPRINTF_BUF_LEN, "movsb");
+ snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "/* memcopy remainder 1 */");
+ break;
+ case 2:
+ IA32_DO_EMIT(NULL);
+ snprintf(cmd_buf, SNPRINTF_BUF_LEN, "movsw");
+ snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "/* memcopy remainder 2 */");
+ break;
+ case 3:
+ IA32_DO_EMIT(NULL);
+ snprintf(cmd_buf, SNPRINTF_BUF_LEN, "movsb");
+ snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "/* memcopy remainder 3 */");
+ IA32_DO_EMIT(NULL);
+ snprintf(cmd_buf, SNPRINTF_BUF_LEN, "movsw");
+ snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "/* memcopy remainder 3 */");
+ break;
+ }
+
+ IA32_DO_EMIT(NULL);
+}
+
+/**
+ * Emit rep movsd instruction for memcopy.
+ */
+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);
+ char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN];
+
+ emit_CopyB_prolog(F, irn, rem);
+
+ snprintf(cmd_buf, SNPRINTF_BUF_LEN, "rep movsd");
+ snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "/* memcopy */");
+ IA32_DO_EMIT(irn);
+}
+
+/**
+ * Emits unrolled memcopy.
+ */
+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;
+ char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN];
+
+ emit_CopyB_prolog(F, irn, size & 0x3);
+
+ size >>= 2;
+ while (size--) {
+ snprintf(cmd_buf, SNPRINTF_BUF_LEN, "movsd");
+ snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "/* memcopy unrolled */");
+ IA32_DO_EMIT(irn);
+ }
+}
+
+
+
+/***************************
+ * _____
+ * / ____|
+ * | | ___ _ ____ __
+ * | | / _ \| '_ \ \ / /
+ * | |___| (_) | | | \ V /
+ * \_____\___/|_| |_|\_/
+ *
+ ***************************/
+
+/**
+ * Emit code for conversions (I, FP), (FP, I) and (FP, FP).
+ */
+static void emit_ia32_Conv_with_FP(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();
+ ir_mode *src_mode = get_ia32_src_mode(irn);
+ ir_mode *tgt_mode = get_ia32_tgt_mode(irn);
+ char *from, *to, buf[64];
+ char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN];
+
+ from = mode_is_float(src_mode) ? (get_mode_size_bits(src_mode) == 32 ? "ss" : "sd") : "si";
+ to = mode_is_float(tgt_mode) ? (get_mode_size_bits(tgt_mode) == 32 ? "ss" : "sd") : "si";
+
+ switch(get_ia32_op_type(irn)) {
+ case ia32_Normal:
+ lc_esnprintf(env, buf, sizeof(buf), "%1D, %3S", irn, irn);
+ break;
+ case ia32_AddrModeS:
+ lc_esnprintf(env, buf, sizeof(buf), "%1D, %s", irn, ia32_emit_am(irn, emit_env));
+ break;
+ default:
+ assert(0 && "unsupported op type for Conv");
+ }
+
+ snprintf(cmd_buf, SNPRINTF_BUF_LEN, "cvt%s2%s %s", from, to, buf);
+ lc_esnprintf(env, cmnt_buf, SNPRINTF_BUF_LEN, "/* %+F(%+F, %+F) */", irn, src_mode, tgt_mode);
+ IA32_DO_EMIT(irn);
+}
+
+static void emit_ia32_Conv_I2FP(const ir_node *irn, ia32_emit_env_t *emit_env) {
+ emit_ia32_Conv_with_FP(irn, 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);
+}
+
+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.
+ */
+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 = "movzx";
+ char *conv_cmd = NULL;
+ ir_mode *src_mode = get_ia32_src_mode(irn);
+ ir_mode *tgt_mode = get_ia32_tgt_mode(irn);
+ int n, m;
+ char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN];
+ const arch_register_t *in_reg, *out_reg;
+
+ n = get_mode_size_bits(src_mode);
+ m = get_mode_size_bits(tgt_mode);
+
+ if (mode_is_signed(n < m ? src_mode : tgt_mode)) {
+ move_cmd = "movsx";
+ if (n == 8 || m == 8)
+ conv_cmd = "cbw";
+ else if (n == 16 || m == 16)
+ conv_cmd = "cwde";
+ else
+ assert(0 && "unsupported Conv_I2I");
+ }
+
+ switch(get_ia32_op_type(irn)) {
+ case ia32_Normal:
+ in_reg = get_in_reg(irn, 2);
+ out_reg = get_out_reg(irn, 0);
+
+ if (REGS_ARE_EQUAL(in_reg, &ia32_gp_regs[REG_EAX]) &&
+ REGS_ARE_EQUAL(out_reg, in_reg) &&
+ mode_is_signed(n < m ? src_mode : tgt_mode))
+ {
+ /* argument and result are both in EAX and */
+ /* signedness is ok: -> use converts */
+ lc_esnprintf(env, cmd_buf, SNPRINTF_BUF_LEN, "%s", conv_cmd);
+ }
+ else if (REGS_ARE_EQUAL(out_reg, in_reg) &&
+ ! mode_is_signed(n < m ? src_mode : tgt_mode))
+ {
+ /* argument and result are in the same register */
+ /* and signedness is ok: -> use and with mask */
+ int mask = (1 << (n < m ? n : m)) - 1;
+ lc_esnprintf(env, cmd_buf, SNPRINTF_BUF_LEN, "and %1D, 0x%x", irn, mask);
+ }
+ else {
+ /* use move w/o sign extension */
+ lc_esnprintf(env, cmd_buf, SNPRINTF_BUF_LEN, "%s %1D, %%%s",
+ move_cmd, irn, ia32_get_reg_name_for_mode(emit_env, n < m ? src_mode : tgt_mode, in_reg));
+ }
+
+ break;
+ case ia32_AddrModeS:
+ lc_esnprintf(env, cmd_buf, SNPRINTF_BUF_LEN, "%s %1D, %s",
+ move_cmd, irn, ia32_emit_am(irn, emit_env));
+ break;
+ default:
+ assert(0 && "unsupported op type for Conv");
+ }
+
+ lc_esnprintf(env, cmnt_buf, SNPRINTF_BUF_LEN, "/* %+F(%d Bit mode_%F -> %d Bit mode_%F) */",
+ irn, n, src_mode, m, tgt_mode);
+
+ 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
+ */
+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];
+
+ if (ent) {
+ snprintf(cmd_buf, SNPRINTF_BUF_LEN, "call %s", get_entity_ld_name(ent));
+ }
+ else {
+ lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "call %1D", get_irn_n(irn, be_pos_Call_ptr));
+ }
+
+ lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* %+F (be_Call) */", irn);
+
+ IA32_DO_EMIT(irn);
+}
+
+/**
+ * Emits code to increase stack pointer.
+ */
+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);
+ char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN];
+
+ if (offs) {
+ if (dir == be_stack_dir_expand)
+ lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "sub %1S, %u", irn, offs);
+ else
+ lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "add %1S, %u", irn, offs);
+ lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* %+F (IncSP) */", irn);
+ }
+ else {
+ snprintf(cmd_buf, SNPRINTF_BUF_LEN, " ");
+ lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* omitted %+F (IncSP) with 0 */", irn);
+ }
+
+ IA32_DO_EMIT(irn);
+}
+
+/**
+ * Emits code to set stack pointer.
+ */
+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];
+
+ lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "mov %1D, %3S", irn, irn);
+ lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* %+F (restore SP) */", irn);
+ IA32_DO_EMIT(irn);
+}
+
+/**
+ * Emits code for Copy/CopyKeep.
+ */
+static void Copy_emitter(const ir_node *irn, ir_node *op, ia32_emit_env_t *emit_env) {
+ FILE *F = emit_env->out;
+ const arch_env_t *aenv = emit_env->arch_env;
+ char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN];
+
+ if (REGS_ARE_EQUAL(arch_get_irn_register(aenv, irn), arch_get_irn_register(aenv, op)) ||
+ be_is_unknown_reg(arch_get_irn_register(aenv, op)))
+ return;
+
+ 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);
+}
+
+static void emit_be_Copy(const ir_node *irn, ia32_emit_env_t *emit_env) {
+ Copy_emitter(irn, be_get_Copy_op(irn), emit_env);
+}
+
+static void emit_be_CopyKeep(const ir_node *irn, ia32_emit_env_t *emit_env) {
+ Copy_emitter(irn, be_get_CopyKeep_op(irn), emit_env);
+}
+
+/**
+ * Emits code for exchange.
+ */
+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];
+ const arch_register_t *in1, *in2;
+ const arch_register_class_t *cls1, *cls2;
+
+ in1 = arch_get_irn_register(emit_env->arch_env, get_irn_n(irn, 0));
+ in2 = arch_get_irn_register(emit_env->arch_env, get_irn_n(irn, 1));
+
+ cls1 = arch_register_get_class(in1);
+ cls2 = arch_register_get_class(in2);
+
+ assert(cls1 == cls2 && "Register class mismatch at Perm");
+
+ if (cls1 == &ia32_reg_classes[CLASS_ia32_gp]) {
+ lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "xchg %1S, %2S", irn, irn);
+ }
+ else if (cls1 == &ia32_reg_classes[CLASS_ia32_xmm]) {
+ lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN,
+ "pxor %1S, %2S\n\tpxor %2S, %1S\n\tpxor %1S, %2S", irn, irn, irn, irn, irn, irn);
+ }
+ else if (cls1 == &ia32_reg_classes[CLASS_ia32_vfp]) {
+ assert(0 && "Perm with vfp should not happen");
+ }
+ else if (cls1 == &ia32_reg_classes[CLASS_ia32_st]) {
+ assert(0 && "Perm with st(X) should not happen");
+ }
+
+ lc_esnprintf(ia32_get_arg_env(), cmnt_buf, SNPRINTF_BUF_LEN, "/* %+F(%1A, %2A) */", irn, irn, irn);
+ IA32_DO_EMIT(irn);
+}
+
+/**
+ * Emits code for Constant loading.
+ */
+static void emit_ia32_Const(const ir_node *n, ia32_emit_env_t *env) {
+ FILE *F = env->out;
+ char cmd_buf[256], cmnt_buf[256];
+ const lc_arg_env_t *arg_env = ia32_get_arg_env();
+
+ if (get_ia32_Immop_tarval(n) == get_tarval_null(get_irn_mode(n))) {
+ const char *instr = "xor";
+ if (env->isa->opt_arch == arch_pentium_4) {
+ /* P4 prefers sub r, r, others xor r, r */
+ instr = "sub";
+ }
+ lc_esnprintf(arg_env, cmd_buf, 256, "%s %1D, %1D ", instr, n, n);
+ lc_esnprintf(arg_env, cmnt_buf, 256, "/* optimized mov 0 to register */");
+ }
+ else {
+ if (get_ia32_op_type(n) == ia32_SymConst) {
+ lc_esnprintf(arg_env, cmd_buf, 256, "mov %1D, OFFSET FLAT:%C ", n, n);
+ lc_esnprintf(arg_env, cmnt_buf, 256, "/* Move address of SymConst into register */");
+ }
+ else {
+ lc_esnprintf(arg_env, cmd_buf, 256, "mov %1D, %C ", n, n);
+ lc_esnprintf(arg_env, cmnt_buf, 256, "/* Mov Const into register */");
+ }
+ }
+ lc_efprintf(arg_env, F, "\t%-35s %-60s /* %+F (%+G) */\n", cmd_buf, cmnt_buf, n, n);
+}
+
+static void emit_be_Return(const ir_node *n, ia32_emit_env_t *env) {
+ FILE *F = env->out;
+ const lc_arg_env_t *arg_env = ia32_get_arg_env();
+
+ lc_efprintf(arg_env, F, "\t%-35s %-60s /* %+F (%+G) */\n", "ret", "/* be_Return */", n, n);
+}
+