+
+ SET_IA32_ORIG_NODE(new_op, get_old_node_name(env));
+
+ return new_op;
+}
+
+
+/**
+ * Following conversion rules apply:
+ *
+ * INT -> INT
+ * ============
+ * 1) n bit -> m bit n < m (upscale)
+ * always ignored
+ * 2) n bit -> m bit n == m (sign change)
+ * always ignored
+ * 3) n bit -> m bit n > m (downscale)
+ * a) Un -> Um = AND Un, (1 << m) - 1
+ * b) Sn -> Um same as a)
+ * c) Un -> Sm same as a)
+ * d) Sn -> Sm = ASHL Sn, (n - m); ASHR Sn, (n - m)
+ *
+ * INT -> FLOAT
+ * ==============
+ * SSE(1/2) convert to float or double (cvtsi2ss/sd)
+ *
+ * FLOAT -> INT
+ * ==============
+ * SSE(1/2) convert from float or double to 32bit int (cvtss/sd2si)
+ * if target mode < 32bit: additional INT -> INT conversion (see above)
+ *
+ * FLOAT -> FLOAT
+ * ================
+ * SSE(1/2) convert from float or double to double or float (cvtss/sd2sd/ss)
+ */
+
+static ir_node *gen_int_downscale_conv(ia32_transform_env_t *env, ir_node *op,
+ ir_mode *src_mode, ir_mode *tgt_mode)
+{
+ int n = get_mode_size_bits(src_mode);
+ int m = get_mode_size_bits(tgt_mode);
+ dbg_info *dbg = env->dbg;
+ ir_graph *irg = env->irg;
+ ir_node *block = env->block;
+ ir_node *noreg = ia32_new_NoReg_gp(env->cg);
+ ir_node *nomem = new_rd_NoMem(irg);
+ ir_node *new_op, *proj;
+
+ assert(n > m && "downscale expected");
+
+ if (mode_is_signed(src_mode) && mode_is_signed(tgt_mode)) {
+ /* ASHL Sn, n - m */
+ new_op = new_rd_ia32_Shl(dbg, irg, block, noreg, noreg, op, noreg, nomem, mode_T);
+ proj = new_rd_Proj(dbg, irg, block, new_op, src_mode, 0);
+ set_ia32_Immop_tarval(new_op, new_tarval_from_long(n - m, mode_Is));
+ set_ia32_am_support(new_op, ia32_am_Source);
+ SET_IA32_ORIG_NODE(new_op, get_old_node_name(env));
+
+ /* ASHR Sn, n - m */
+ new_op = new_rd_ia32_Shrs(dbg, irg, block, noreg, noreg, proj, noreg, nomem, mode_T);
+ set_ia32_Immop_tarval(new_op, new_tarval_from_long(n - m, mode_Is));
+ }
+ else {
+ new_op = new_rd_ia32_And(dbg, irg, block, noreg, noreg, op, noreg, nomem, mode_T);
+ set_ia32_Immop_tarval(new_op, new_tarval_from_long((1 << m) - 1, mode_Is));
+ }
+
+ return new_op;
+}
+
+/**
+ * Transforms a Conv node.
+ *
+ * @param env The transformation environment
+ * @param op The operator
+ * @return The created ia32 Conv node
+ */
+static ir_node *gen_Conv(ia32_transform_env_t *env, ir_node *op) {
+ dbg_info *dbg = env->dbg;
+ ir_graph *irg = env->irg;
+ ir_mode *src_mode = get_irn_mode(op);
+ ir_mode *tgt_mode = env->mode;
+ ir_node *block = env->block;
+ ir_node *new_op = NULL;
+ ir_node *noreg = ia32_new_NoReg_gp(env->cg);
+ ir_node *nomem = new_rd_NoMem(irg);
+ firm_dbg_module_t *mod = env->mod;
+ ir_node *proj;
+
+ if (src_mode == tgt_mode) {
+ /* this can happen when changing mode_P to mode_Is */
+ DB((mod, LEVEL_1, "killed Conv(mode, mode) ..."));
+ edges_reroute(env->irn, op, irg);
+ }
+ else if (mode_is_float(src_mode)) {
+ /* we convert from float ... */
+ if (mode_is_float(tgt_mode)) {
+ /* ... to float */
+ DB((mod, LEVEL_1, "create Conv(float, float) ..."));
+ new_op = new_rd_ia32_Conv_FP2FP(dbg, irg, block, noreg, noreg, op, nomem, mode_T);
+ }
+ else {
+ /* ... to int */
+ DB((mod, LEVEL_1, "create Conv(float, int) ..."));
+ new_op = new_rd_ia32_Conv_FP2I(dbg, irg, block, noreg, noreg, op, nomem, mode_T);
+ /* if target mode is not int: add an additional downscale convert */
+ if (get_mode_size_bits(tgt_mode) < 32) {
+ SET_IA32_ORIG_NODE(new_op, get_old_node_name(env));
+ set_ia32_res_mode(new_op, tgt_mode);
+ set_ia32_am_support(new_op, ia32_am_Source);
+
+ proj = new_rd_Proj(dbg, irg, block, new_op, mode_Is, 0);
+ new_op = gen_int_downscale_conv(env, proj, src_mode, tgt_mode);
+ }
+ }
+ }
+ else {
+ /* we convert from int ... */
+ if (mode_is_float(tgt_mode)) {
+ /* ... to float */
+ DB((mod, LEVEL_1, "create Conv(int, float) ..."));
+ new_op = new_rd_ia32_Conv_I2FP(dbg, irg, block, noreg, noreg, op, nomem, mode_T);
+ }
+ else {
+ /* ... to int */
+ if (get_mode_size_bits(src_mode) <= get_mode_size_bits(tgt_mode)) {
+ DB((mod, LEVEL_1, "omitting upscale Conv(%+F, %+F) ...", src_mode, tgt_mode));
+ edges_reroute(env->irn, op, irg);
+ }
+ else {
+ DB((mod, LEVEL_1, "create downscale Conv(%+F, %+F) ...", src_mode, tgt_mode));
+ new_op = gen_int_downscale_conv(env, op, src_mode, tgt_mode);
+ }
+ }
+ }
+
+ if (new_op) {
+ SET_IA32_ORIG_NODE(new_op, get_old_node_name(env));
+ set_ia32_res_mode(new_op, tgt_mode);
+
+ set_ia32_am_support(new_op, ia32_am_Source);
+
+ new_op = new_rd_Proj(dbg, irg, block, new_op, tgt_mode, 0);
+ }
+
+ return new_op;