ia32_IJmp needs the no-out-requirements hack, too.
[libfirm] / ir / be / ia32 / ia32_emitter.c
index d1650d6..522c795 100644 (file)
@@ -2251,19 +2251,21 @@ static void build_reg_map(void)
        reg_gp_map[REG_ESI] = 0x6;
        reg_gp_map[REG_EDI] = 0x7;
 
-       pnc_map_signed[pn_Cmp_Eq] = 0x04;
-       pnc_map_signed[pn_Cmp_Lt] = 0x0C;
-       pnc_map_signed[pn_Cmp_Le] = 0x0E;
-       pnc_map_signed[pn_Cmp_Gt] = 0x0F;
-       pnc_map_signed[pn_Cmp_Ge] = 0x0D;
-       pnc_map_signed[pn_Cmp_Ne] = 0x05;
-
-       pnc_map_unsigned[pn_Cmp_Eq] = 0x04;
-       pnc_map_unsigned[pn_Cmp_Lt] = 0x02;
-       pnc_map_unsigned[pn_Cmp_Le] = 0x06;
-       pnc_map_unsigned[pn_Cmp_Gt] = 0x07;
-       pnc_map_unsigned[pn_Cmp_Ge] = 0x03;
-       pnc_map_unsigned[pn_Cmp_Ne] = 0x05;
+       pnc_map_signed[pn_Cmp_False] = 0xFF;
+       pnc_map_signed[pn_Cmp_Eq]    = 0x04;
+       pnc_map_signed[pn_Cmp_Lt]    = 0x0C;
+       pnc_map_signed[pn_Cmp_Le]    = 0x0E;
+       pnc_map_signed[pn_Cmp_Gt]    = 0x0F;
+       pnc_map_signed[pn_Cmp_Ge]    = 0x0D;
+       pnc_map_signed[pn_Cmp_Lg]    = 0x05;
+
+       pnc_map_unsigned[pn_Cmp_False] = 0xFF;
+       pnc_map_unsigned[pn_Cmp_Eq]    = 0x04;
+       pnc_map_unsigned[pn_Cmp_Lt]    = 0x02;
+       pnc_map_unsigned[pn_Cmp_Le]    = 0x06;
+       pnc_map_unsigned[pn_Cmp_Gt]    = 0x07;
+       pnc_map_unsigned[pn_Cmp_Ge]    = 0x03;
+       pnc_map_unsigned[pn_Cmp_Lg]    = 0x05;
 }
 
 #define GET_MODE(code) ((code) & 0xC0)
@@ -2287,12 +2289,8 @@ enum Mod {
 /** create REG encoding for ModR/M */
 #define ENC_REG(x) ((x) << 3)
 
-/** create Base encoding for SIB */
-#define ENC_BASE(x) (x)
-/** create Index encoding for SIB */
-#define ENC_INDEX(x) ((x) << 3)
-/** create Scale encoding for SIB */
-#define ENC_SCALE(x) ((x) << 6)
+/** create encoding for a SIB byte */
+#define ENC_SIB(scale, index, base) ((scale) << 6 | (index) << 3 | (base))
 
 /* Node: The following routines are supposed to append bytes, words, dwords
    to the output stream.
@@ -2359,6 +2357,7 @@ static void bemit_jmp_destination(const ir_node *dest_block)
        be_emit_cstring("\t.long ");
        ia32_emit_block_name(dest_block);
        be_emit_cstring(" - . - 4\n");
+       be_emit_write_line();
 }
 
 /* end emit routines, all emitters following here should only use the functions
@@ -2416,55 +2415,6 @@ static unsigned get_signed_imm_size(int offset)
        }
 }
 
-/**
- * Emit a binop with a immediate operand.
- *
- * @param node        the node to emit
- * @param opcode_eax  the opcode for the op eax, imm variant
- * @param opcode      the opcode for the reg, imm variant
- * @param ruval       the opcode extension for opcode
- */
-static void bemit_binop_with_imm(
-       const ir_node *node,
-       unsigned char opcode_ax,
-       unsigned char opcode, unsigned char ruval)
-{
-       /* Use in-reg, because some instructions (cmp, test) have no out-reg. */
-       const arch_register_t       *reg  = get_in_reg(node, n_ia32_binary_left);
-       const ir_node               *op   = get_irn_n(node, n_ia32_binary_right);
-       const ia32_immediate_attr_t *attr = get_ia32_immediate_attr_const(op);
-       unsigned                    size;
-
-       /* Some instructions (test) have no short form with 32bit value + 8bit
-        * immediate. */
-       if (attr->symconst != NULL || opcode & SIGNEXT_IMM)
-               size = 4;
-       else {
-               /* check for sign extension */
-               size = get_signed_imm_size(attr->offset);
-       }
-
-       switch (size) {
-       case 1:
-               bemit8(opcode | SIGNEXT_IMM);
-               bemit_modru(reg, ruval);
-               bemit8((unsigned char)attr->offset);
-               return;
-       case 2:
-       case 4:
-               /* check for eax variant: this variant is shorter for 32bit immediates only */
-               if (reg->index == REG_EAX) {
-                       bemit8(opcode_ax);
-               } else {
-                       bemit8(opcode);
-                       bemit_modru(reg, ruval);
-               }
-               bemit_entity(attr->symconst, attr->sc_sign, attr->offset, false);
-               return;
-       }
-       panic("invalid imm size?!?");
-}
-
 /**
  * Emit an address mode.
  *
@@ -2483,6 +2433,7 @@ static void bemit_mod_am(unsigned reg, const ir_node *node)
        unsigned   sib       = 0;
        unsigned   emitoffs  = 0;
        bool       emitsib   = false;
+       unsigned   base_enc;
 
        /* set the mod part depending on displacement */
        if (ent != NULL) {
@@ -2499,53 +2450,41 @@ static void bemit_mod_am(unsigned reg, const ir_node *node)
                emitoffs = 32;
        }
 
-       /* determine if we need a SIB byte */
+       if (has_base) {
+               const arch_register_t *base_reg = arch_get_irn_register(base);
+               base_enc = reg_gp_map[base_reg->index];
+       } else {
+               /* Use the EBP encoding + MOD_IND if NO base register. There is
+                * always a 32bit offset present in this case. */
+               modrm    = MOD_IND;
+               base_enc = 0x05;
+               emitoffs = 32;
+       }
+
+       /* Determine if we need a SIB byte. */
        if (has_index) {
-               int scale;
                const arch_register_t *reg_index = arch_get_irn_register(index);
-               assert(reg_index->index != REG_ESP);
-               sib |= ENC_INDEX(reg_gp_map[reg_index->index]);
-
-               if (has_base) {
-                       const arch_register_t *base_reg = arch_get_irn_register(base);
-                       sib |= ENC_BASE(reg_gp_map[base_reg->index]);
-               } else {
-                       /* use the EBP encoding if NO base register */
-                       sib |= 0x05;
-               }
-
-               scale = get_ia32_am_scale(node);
+               int                    scale     = get_ia32_am_scale(node);
                assert(scale < 4);
-               sib |= ENC_SCALE(scale);
+               /* R/M set to ESP means SIB in 32bit mode. */
+               modrm   |= ENC_RM(0x04);
+               sib      = ENC_SIB(scale, reg_gp_map[reg_index->index], base_enc);
                emitsib = true;
-       }
-
-       /* determine modrm byte */
-       if (emitsib) {
-               /* R/M set to ESP means SIB in 32bit mode */
-               modrm |= ENC_RM(0x04);
-       } else if (has_base) {
-               const arch_register_t *base_reg = arch_get_irn_register(base);
-               if (base_reg->index == REG_ESP) {
-                       /* for the above reason we are forced to emit a sib when base is
-                        * ESP. Only the base is used, index must be ESP too, which means no
-                        * index. */
-                       sib     = ENC_BASE(0x04) | ENC_INDEX(0x04);
-                       emitsib = true;
-
-               /* we are forced to emit a 8bit offset as EBP base without
-                  offset is a special case for SIB without base register */
-               } else if (base_reg->index == REG_EBP && emitoffs == 0) {
-                       assert(GET_MODE(modrm) == MOD_IND);
-                       emitoffs  = 8;
+       } else if (base_enc == 0x04) {
+               /* for the above reason we are forced to emit a SIB when base is ESP.
+                * Only the base is used, index must be ESP too, which means no index.
+                */
+               modrm   |= ENC_RM(0x04);
+               sib      = ENC_SIB(0, 0x04, 0x04);
+               emitsib  = true;
+       } else {
+               modrm |= ENC_RM(base_enc);
+               /* We are forced to emit an 8bit offset as EBP base without offset is a
+                * special case for SIB without base register. */
+               if (base_enc == 0x05 && emitoffs == 0) {
                        modrm    |= MOD_IND_BYTE_OFS;
+                       emitoffs  = 8;
                }
-
-               modrm |= ENC_RM(reg_gp_map[base_reg->index]);
-       } else {
-               /* only displacement: Use EBP + disp encoding in 32bit mode */
-               emitoffs = 32;
-               modrm    = ENC_RM(0x05);
        }
 
        modrm |= ENC_REG(reg);
@@ -2562,6 +2501,66 @@ static void bemit_mod_am(unsigned reg, const ir_node *node)
        }
 }
 
+/**
+ * Emit a binop with a immediate operand.
+ *
+ * @param node        the node to emit
+ * @param opcode_eax  the opcode for the op eax, imm variant
+ * @param opcode      the opcode for the reg, imm variant
+ * @param ruval       the opcode extension for opcode
+ */
+static void bemit_binop_with_imm(
+       const ir_node *node,
+       unsigned char opcode_ax,
+       unsigned char opcode, unsigned char ruval)
+{
+       /* Use in-reg, because some instructions (cmp, test) have no out-reg. */
+       const ir_node               *op   = get_irn_n(node, n_ia32_binary_right);
+       const ia32_immediate_attr_t *attr = get_ia32_immediate_attr_const(op);
+       unsigned                     size;
+
+       /* Some instructions (test) have no short form with 32bit value + 8bit
+        * immediate. */
+       if (attr->symconst != NULL || opcode & SIGNEXT_IMM) {
+               size = 4;
+       } else {
+               /* check for sign extension */
+               size = get_signed_imm_size(attr->offset);
+       }
+
+       switch (size) {
+       case 1:
+               bemit8(opcode | SIGNEXT_IMM);
+               /* cmp has this special mode */
+               if (get_ia32_op_type(node) == ia32_AddrModeS) {
+                       bemit_mod_am(ruval, node);
+               } else {
+                       const arch_register_t *reg = get_in_reg(node, n_ia32_binary_left);
+                       bemit_modru(reg, ruval);
+               }
+               bemit8((unsigned char)attr->offset);
+               return;
+       case 2:
+       case 4:
+               /* check for eax variant: this variant is shorter for 32bit immediates only */
+               if (get_ia32_op_type(node) == ia32_AddrModeS) {
+                       bemit8(opcode);
+                       bemit_mod_am(ruval, node);
+               } else {
+                       const arch_register_t *reg = get_in_reg(node, n_ia32_binary_left);
+                       if (reg->index == REG_EAX) {
+                               bemit8(opcode_ax);
+                       } else {
+                               bemit8(opcode);
+                               bemit_modru(reg, ruval);
+                       }
+               }
+               bemit_entity(attr->symconst, attr->sc_sign, attr->offset, false);
+               return;
+       }
+       panic("invalid imm size?!?");
+}
+
 /**
  * Emits a binop.
  */
@@ -2629,9 +2628,8 @@ static void bemit_immediate(const ir_node *node, bool relative)
 
 static void bemit_copy(const ir_node *copy)
 {
-       const ir_node *op = be_get_Copy_op(copy);
-       const arch_register_t *in  = arch_get_irn_register(op);
-       const arch_register_t *out = arch_get_irn_register(copy);
+       const arch_register_t *in  = get_in_reg(copy, 0);
+       const arch_register_t *out = get_out_reg(copy, 0);
 
        if (in == out || is_unknown_reg(in))
                return;
@@ -2690,14 +2688,15 @@ static void bemit_ ## op(const ir_node *node) { \
        bemit_unop(node, code, ext, input);         \
 }
 
-UNOP(not,     0xF7, 2, n_ia32_unary_op)
-UNOP(neg,     0xF7, 3, n_ia32_unary_op)
-UNOP(mul,     0xF7, 4, n_ia32_binary_right)
-UNOP(imul1op, 0xF7, 5, n_ia32_binary_right)
-UNOP(div,     0xF7, 6, n_ia32_unary_op)
-UNOP(idiv,    0xF7, 7, n_ia32_unary_op)
+UNOP(not,     0xF7, 2, n_ia32_Not_val)
+UNOP(neg,     0xF7, 3, n_ia32_Neg_val)
+UNOP(mul,     0xF7, 4, n_ia32_Mul_right)
+UNOP(imul1op, 0xF7, 5, n_ia32_IMul1OP_right)
+UNOP(div,     0xF7, 6, n_ia32_Div_divisor)
+UNOP(idiv,    0xF7, 7, n_ia32_IDiv_divisor)
 
-UNOP(ijmp,    0xFF, 4, n_ia32_unary_op)
+/* TODO: am support for IJmp */
+UNOP(ijmp,    0xFF, 4, n_ia32_IJmp_target)
 
 #define SHIFT(op, ext) \
 static void bemit_##op(const ir_node *node) \
@@ -2865,9 +2864,12 @@ static void bemit_push(const ir_node *node)
                        bemit_immediate(value, false);
                        break;
                }
-       } else {
+       } else if (is_ia32_NoReg_GP(value)) {
                bemit8(0xFF);
                bemit_mod_am(6, node);
+       } else {
+               const arch_register_t *reg = get_in_reg(node, n_ia32_Push_val);
+               bemit8(0x50 + reg_gp_map[reg->index]);
        }
 }
 
@@ -2877,12 +2879,13 @@ static void bemit_push(const ir_node *node)
 static void bemit_pop(const ir_node *node)
 {
        const arch_register_t *reg = get_out_reg(node, pn_ia32_Pop_res);
-       if (get_ia32_op_type(node) == ia32_Normal)
-               bemit8(0x58 + reg_gp_map[reg->index]);
-       else {
-               bemit8(0x8F);
-               bemit_mod_am(0, node);
-       }
+       bemit8(0x58 + reg_gp_map[reg->index]);
+}
+
+static void bemit_popmem(const ir_node *node)
+{
+       bemit8(0x8F);
+       bemit_mod_am(0, node);
 }
 
 static void bemit_call(const ir_node *node)
@@ -2929,6 +2932,7 @@ static void bemit_jcc(int pnc, const ir_node *dest_block)
                        cc = pnc_map_signed[pnc & 0x07];
                }
        }
+       assert(cc != 0xFF);
 
        bemit8(0x0F);
        bemit8(0x80 + cc);
@@ -3087,6 +3091,7 @@ static void ia32_register_binary_emitters(void)
 
        /* benode emitter */
        register_emitter(op_be_Copy, bemit_copy);
+       register_emitter(op_be_CopyKeep, bemit_copy);
        register_emitter(op_be_IncSP, bemit_incsp);
        register_emitter(op_be_Return, bemit_return);
        register_emitter(op_ia32_Adc, bemit_adc);
@@ -3113,6 +3118,8 @@ static void ia32_register_binary_emitters(void)
        register_emitter(op_ia32_Not, bemit_not);
        register_emitter(op_ia32_Or, bemit_or);
        register_emitter(op_ia32_Pop, bemit_pop);
+       register_emitter(op_ia32_PopEbp, bemit_pop);
+       register_emitter(op_ia32_PopMem, bemit_popmem);
        register_emitter(op_ia32_Push, bemit_push);
        register_emitter(op_ia32_RepPrefix, bemit_rep);
        register_emitter(op_ia32_Rol, bemit_rol);