fix binary emitter for cmp with addressmode and immediate
[libfirm] / ir / be / ia32 / ia32_emitter.c
index bbef146..0980117 100644 (file)
@@ -2237,6 +2237,8 @@ static const lc_opt_table_entry_t ia32_emitter_options[] = {
 static unsigned char reg_gp_map[N_ia32_gp_REGS];
 static unsigned char reg_mmx_map[N_ia32_mmx_REGS];
 static unsigned char reg_sse_map[N_ia32_xmm_REGS];
+static unsigned char pnc_map_signed[8];
+static unsigned char pnc_map_unsigned[8];
 
 static void build_reg_map(void)
 {
@@ -2248,15 +2250,21 @@ static void build_reg_map(void)
        reg_gp_map[REG_EBP] = 0x5;
        reg_gp_map[REG_ESI] = 0x6;
        reg_gp_map[REG_EDI] = 0x7;
-}
 
-/** The mod encoding of the ModR/M */
-enum Mod {
-       MOD_IND          = 0x00, /**< [reg1] */
-       MOD_IND_BYTE_OFS = 0x40, /**< [reg1 + byte ofs] */
-       MOD_IND_WORD_OFS = 0x80, /**< [reg1 + word ofs] */
-       MOD_REG          = 0xC0  /**< reg1 */
-};
+       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;
+}
 
 #define GET_MODE(code) ((code) & 0xC0)
 
@@ -2266,6 +2274,14 @@ enum SignExt {
        SIGNEXT_IMM  = 2,  /**< sign extended immediate */
 };
 
+/** The mod encoding of the ModR/M */
+enum Mod {
+       MOD_IND          = 0x00, /**< [reg1] */
+       MOD_IND_BYTE_OFS = 0x40, /**< [reg1 + byte ofs] */
+       MOD_IND_WORD_OFS = 0x80, /**< [reg1 + word ofs] */
+       MOD_REG          = 0xC0  /**< reg1 */
+};
+
 /** create R/M encoding for ModR/M */
 #define ENC_RM(x) (x)
 /** create REG encoding for ModR/M */
@@ -2303,6 +2319,10 @@ static void bemit32(const unsigned u32)
        be_emit_write_line();
 }
 
+/**
+ * Emit address of an entity. If @p is_relative is true then a relative
+ * offset from behind the address to the entity is created.
+ */
 static void bemit_entity(ir_entity *entity, bool entity_sign, int offset,
                          bool is_relative)
 {
@@ -2311,6 +2331,10 @@ static void bemit_entity(ir_entity *entity, bool entity_sign, int offset,
                return;
        }
 
+       if (is_relative) {
+               offset -= 4;
+       }
+
        /* the final version should remember the position in the bytestream
           and patch it with the correct address at linktime... */
        be_emit_cstring("\t.long ");
@@ -2330,6 +2354,13 @@ static void bemit_entity(ir_entity *entity, bool entity_sign, int offset,
        be_emit_write_line();
 }
 
+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");
+}
+
 /* end emit routines, all emitters following here should only use the functions
    above. */
 
@@ -2378,59 +2409,13 @@ static unsigned get_signed_imm_size(int offset)
 {
        if (-128 <= offset && offset < 128) {
                return 1;
-       } else if (offset >= -32768 && offset < 32767) {
+       } else if (-32768 <= offset && offset < 32768) {
                return 2;
        } else {
                return 4;
        }
 }
 
-/**
- * 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)
-{
-       const arch_register_t       *reg  = get_out_reg(node, 0);
-       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;
-
-       if (attr->symconst != NULL)
-               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.
  *
@@ -2473,8 +2458,8 @@ static void bemit_mod_am(unsigned reg, const ir_node *node)
                sib |= ENC_INDEX(reg_gp_map[reg_index->index]);
 
                if (has_base) {
-                       const arch_register_t *reg = arch_get_irn_register(base);
-                       sib |= ENC_BASE(reg_gp_map[reg->index]);
+                       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;
@@ -2491,28 +2476,27 @@ static void bemit_mod_am(unsigned reg, const ir_node *node)
                /* R/M set to ESP means SIB in 32bit mode */
                modrm |= ENC_RM(0x04);
        } else if (has_base) {
-               const arch_register_t *reg = arch_get_irn_register(base);
-               if (reg->index == REG_ESP) {
-                       /* for the above reason we are forced to emit a sib
-                          when base is ESP. Only the base is used */
-                       sib     = ENC_BASE(0x04);
+               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 (reg->index == REG_EBP && emitoffs == 0) {
+               } else if (base_reg->index == REG_EBP && emitoffs == 0) {
                        assert(GET_MODE(modrm) == MOD_IND);
                        emitoffs  = 8;
                        modrm    |= MOD_IND_BYTE_OFS;
                }
-               modrm |= ENC_RM(reg_gp_map[reg->index]);
+
+               modrm |= ENC_RM(reg_gp_map[base_reg->index]);
        } else {
                /* only displacement: Use EBP + disp encoding in 32bit mode */
-               if (emitoffs == 0) {
-                       emitoffs = 8;
-                       modrm    = MOD_IND_BYTE_OFS;
-               }
-               modrm |= ENC_RM(0x05);
+               emitoffs = 32;
+               modrm    = ENC_RM(0x05);
        }
 
        modrm |= ENC_REG(reg);
@@ -2529,6 +2513,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.
  */
@@ -2563,10 +2607,6 @@ static void bemit_binop(const ir_node *node, const unsigned char opcodes[4])
 {
        ir_node *right = get_irn_n(node, n_ia32_binary_right);
        if (is_ia32_Immediate(right)) {
-               /* there's a shorter variant with DEST=EAX */
-               const arch_register_t *reg = get_out_reg(node, 0);
-               if (reg->index == REG_EAX)
-
                bemit_binop_with_imm(node, opcodes[1], opcodes[2], opcodes[3]);
        } else {
                bemit_binop_2(node, opcodes[0]);
@@ -2642,15 +2682,16 @@ static void bemit_ ## op(const ir_node *node) {                           \
        bemit_binop(node, op ## _codes);                                      \
 }
 
-/*   insn  def  eax,imm   imm  */
-BINOP(add, 0x01, 0x05, 0x81, 0 )
-BINOP(or,  0x09, 0x0D, 0x81, 1 )
-BINOP(adc, 0x11, 0x15, 0x81, 2 )
-BINOP(sbb, 0x19, 0x1D, 0x81, 3 )
-BINOP(and, 0x21, 0x25, 0x81, 4 )
-BINOP(sub, 0x29, 0x2D, 0x81, 5 )
-BINOP(xor, 0x31, 0x35, 0x81, 6 )
-BINOP(cmp, 0x39, 0x3D, 0x81, 7 )
+/*    insn  def  eax,imm   imm */
+BINOP(add,  0x01, 0x05, 0x81, 0)
+BINOP(or,   0x09, 0x0D, 0x81, 1)
+BINOP(adc,  0x11, 0x15, 0x81, 2)
+BINOP(sbb,  0x19, 0x1D, 0x81, 3)
+BINOP(and,  0x21, 0x25, 0x81, 4)
+BINOP(sub,  0x29, 0x2D, 0x81, 5)
+BINOP(xor,  0x31, 0x35, 0x81, 6)
+BINOP(cmp,  0x39, 0x3D, 0x81, 7)
+BINOP(test, 0x85, 0xA9, 0xF7, 0)
 
 /**
  * Creates a function for an Unop with code /ext encoding.
@@ -2669,6 +2710,45 @@ UNOP(idiv,    0xF7, 7, n_ia32_unary_op)
 
 UNOP(ijmp,    0xFF, 4, n_ia32_unary_op)
 
+#define SHIFT(op, ext) \
+static void bemit_##op(const ir_node *node) \
+{ \
+       const arch_register_t *out   = get_out_reg(node, 0); \
+       ir_node               *count = get_irn_n(node, 1); \
+       if (is_ia32_Immediate(count)) { \
+               int offset = get_ia32_immediate_attr_const(count)->offset; \
+               if (offset == 1) { \
+                       bemit8(0xD1); \
+                       bemit_modru(out, ext); \
+               } else { \
+                       bemit8(0xC1); \
+                       bemit_modru(out, ext); \
+                       bemit8(offset); \
+               } \
+       } else { \
+               bemit8(0xD3); \
+               bemit_modru(out, ext); \
+       } \
+}
+
+SHIFT(rol, 0)
+SHIFT(ror, 1)
+SHIFT(shl, 4)
+SHIFT(shr, 5)
+SHIFT(sar, 7)
+
+static void bemit_dec(const ir_node *node)
+{
+       const arch_register_t *out = get_out_reg(node, pn_ia32_Dec_res);
+       bemit8(0x48 + reg_gp_map[out->index]);
+}
+
+static void bemit_inc(const ir_node *node)
+{
+       const arch_register_t *out = get_out_reg(node, pn_ia32_Inc_res);
+       bemit8(0x40 + reg_gp_map[out->index]);
+}
+
 /**
  * Emit a Lea.
  */
@@ -2693,7 +2773,7 @@ static void bemit_ ## op(const ir_node *node) { \
 //EMIT_SINGLEOP(aaa,  0x37)
 //EMIT_SINGLEOP(aas,  0x3F)
 //EMIT_SINGLEOP(nop,  0x90)
-EMIT_SINGLEOP(cwde, 0x98)
+//EMIT_SINGLEOP(cwde, 0x98)
 EMIT_SINGLEOP(cltd, 0x99)
 //EMIT_SINGLEOP(fwait, 0x9B)
 EMIT_SINGLEOP(sahf, 0x9E)
@@ -2824,7 +2904,131 @@ static void bemit_call(const ir_node *node)
                bemit8(0xE8);
                bemit_immediate(proc, true);
        } else {
-               panic("indirect call NIY");
+               bemit8(0xFF);
+               if (get_ia32_op_type(node) == ia32_Normal) {
+                       bemit_modru(get_in_reg(node, n_ia32_unary_op), 2);
+               } else {
+                       bemit_mod_am(2, node);
+               }
+       }
+}
+
+static void bemit_jmp(const ir_node *dest_block)
+{
+       bemit8(0xE9);
+       bemit_jmp_destination(dest_block);
+}
+
+static void bemit_jump(const ir_node *node)
+{
+       if (can_be_fallthrough(node))
+               return;
+
+       bemit_jmp(get_cfop_target_block(node));
+}
+
+static void bemit_jcc(int pnc, const ir_node *dest_block)
+{
+       unsigned char cc;
+
+       if (pnc == ia32_pn_Cmp_parity) {
+               cc = 0x0A;
+       } else {
+               if (pnc & ia32_pn_Cmp_float || pnc & ia32_pn_Cmp_unsigned) {
+                       cc = pnc_map_unsigned[pnc & 0x07];
+               } else {
+                       cc = pnc_map_signed[pnc & 0x07];
+               }
+       }
+
+       bemit8(0x0F);
+       bemit8(0x80 + cc);
+       bemit_jmp_destination(dest_block);
+}
+
+static void bemit_ia32_jcc(const ir_node *node)
+{
+       int            pnc = get_ia32_condcode(node);
+       int            need_parity_label = 0;
+       const ir_node *proj_true;
+       const ir_node *proj_false;
+       const ir_node *dest_true;
+       const ir_node *dest_false;
+       const ir_node *block;
+
+       pnc = determine_final_pnc(node, 0, pnc);
+
+       /* get both Projs */
+       proj_true = get_proj(node, pn_ia32_Jcc_true);
+       assert(proj_true && "Jcc without true Proj");
+
+       proj_false = get_proj(node, pn_ia32_Jcc_false);
+       assert(proj_false && "Jcc without false Proj");
+
+       block = get_nodes_block(node);
+
+       if (can_be_fallthrough(proj_true)) {
+               /* exchange both proj's so the second one can be omitted */
+               const ir_node *t = proj_true;
+
+               proj_true  = proj_false;
+               proj_false = t;
+               pnc        = ia32_get_negated_pnc(pnc);
+       }
+
+       dest_true  = get_cfop_target_block(proj_true);
+       dest_false = get_cfop_target_block(proj_false);
+
+       if (pnc & ia32_pn_Cmp_float) {
+               panic("Float jump NIY");
+               /* Some floating point comparisons require a test of the parity flag,
+                * which indicates that the result is unordered */
+               switch (pnc & 15) {
+                       case pn_Cmp_Uo: {
+                               ia32_emitf(proj_true, "\tjp %L\n");
+                               break;
+                       }
+
+                       case pn_Cmp_Leg:
+                               ia32_emitf(proj_true, "\tjnp %L\n");
+                               break;
+
+                       case pn_Cmp_Eq:
+                       case pn_Cmp_Lt:
+                       case pn_Cmp_Le:
+                               /* we need a local label if the false proj is a fallthrough
+                                * as the falseblock might have no label emitted then */
+                               if (can_be_fallthrough(proj_false)) {
+                                       need_parity_label = 1;
+                                       ia32_emitf(proj_false, "\tjp 1f\n");
+                               } else {
+                                       ia32_emitf(proj_false, "\tjp %L\n");
+                               }
+                               goto emit_jcc;
+
+                       case pn_Cmp_Ug:
+                       case pn_Cmp_Uge:
+                       case pn_Cmp_Ne:
+                               ia32_emitf(proj_true, "\tjp %L\n");
+                               goto emit_jcc;
+
+                       default:
+                               goto emit_jcc;
+               }
+       } else {
+emit_jcc:
+               bemit_jcc(pnc, dest_true);
+       }
+
+       if (need_parity_label) {
+               panic("parity label NIY");
+       }
+
+       /* the second Proj might be a fallthrough */
+       if (can_be_fallthrough(proj_false)) {
+               /* it's a fallthrough */
+       } else {
+               bemit_jmp(dest_false);
        }
 }
 
@@ -2845,27 +3049,32 @@ static void bemit_return(const ir_node *node)
 
 static void bemit_incsp(const ir_node *node)
 {
-       const arch_register_t *reg  = get_out_reg(node, 0);
-       int                    offs = be_get_IncSP_offset(node);
-       unsigned               size = get_signed_imm_size(offs);
-       unsigned char          w    = size == 1 ? 2 : 0;
+       int                    offs;
+       const arch_register_t *reg;
+       unsigned               size;
+       unsigned               ext;
+
+       offs = be_get_IncSP_offset(node);
+       if (offs == 0)
+               return;
 
-       bemit8(0x81 | w);
        if (offs > 0) {
+               ext = 5; /* sub */
+       } else {
+               ext = 0; /* add */
+               offs = -offs;
+       }
 
-               bemit_modru(reg, 5); /* sub */
-               if (size == 8) {
-                       bemit8(offs);
-               } else {
-                       bemit32(offs);
-               }
-       } else if (offs < 0) {
-               bemit_modru(reg, 0); /* add */
-               if (size == 8) {
-                       bemit8(-offs);
-               } else {
-                       bemit32(-offs);
-               }
+       size = get_signed_imm_size(offs);
+       bemit8(size == 1 ? 0x83 : 0x81);
+
+       reg  = get_out_reg(node, 0);
+       bemit_modru(reg, ext);
+
+       if (size == 1) {
+               bemit8(offs);
+       } else {
+               bemit32(offs);
        }
 }
 
@@ -2889,38 +3098,47 @@ static void ia32_register_binary_emitters(void)
 
        /* benode emitter */
        register_emitter(op_be_Copy, bemit_copy);
-       register_emitter(op_be_Return, bemit_return);
        register_emitter(op_be_IncSP, bemit_incsp);
-       register_emitter(op_ia32_Add, bemit_add);
+       register_emitter(op_be_Return, bemit_return);
        register_emitter(op_ia32_Adc, bemit_adc);
+       register_emitter(op_ia32_Add, bemit_add);
        register_emitter(op_ia32_And, bemit_and);
-       register_emitter(op_ia32_Or, bemit_or);
-       register_emitter(op_ia32_Cmp, bemit_cmp);
+       register_emitter(op_ia32_Breakpoint, bemit_int3);
        register_emitter(op_ia32_Call, bemit_call);
        register_emitter(op_ia32_Cltd, bemit_cltd);
        register_emitter(op_ia32_Cmc, bemit_cmc);
-       register_emitter(op_ia32_Stc, bemit_stc);
-       register_emitter(op_ia32_RepPrefix, bemit_rep);
-       register_emitter(op_ia32_Breakpoint, bemit_int3);
-       register_emitter(op_ia32_Sahf, bemit_sahf);
-       register_emitter(op_ia32_Cltd, bemit_cwde);
-       register_emitter(op_ia32_Sub, bemit_sub);
-       register_emitter(op_ia32_Sbb, bemit_sbb);
-       register_emitter(op_ia32_Xor0, bemit_xor0);
-       register_emitter(op_ia32_Xor, bemit_xor);
+       register_emitter(op_ia32_Cmp, bemit_cmp);
        register_emitter(op_ia32_Const, bemit_mov_const);
+       register_emitter(op_ia32_Dec, bemit_dec);
+       register_emitter(op_ia32_Div, bemit_div);
+       register_emitter(op_ia32_IDiv, bemit_idiv);
+       register_emitter(op_ia32_IJmp, bemit_ijmp);
+       register_emitter(op_ia32_IMul1OP, bemit_imul1op);
+       register_emitter(op_ia32_Inc, bemit_inc);
+       register_emitter(op_ia32_Jcc, bemit_ia32_jcc);
+       register_emitter(op_ia32_Jmp, bemit_jump);
        register_emitter(op_ia32_Lea, bemit_lea);
        register_emitter(op_ia32_Load, bemit_load);
-       register_emitter(op_ia32_Not, bemit_not);
+       register_emitter(op_ia32_Mul, bemit_mul);
        register_emitter(op_ia32_Neg, bemit_neg);
-       register_emitter(op_ia32_Push, bemit_push);
+       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_Push, bemit_push);
+       register_emitter(op_ia32_RepPrefix, bemit_rep);
+       register_emitter(op_ia32_Rol, bemit_rol);
+       register_emitter(op_ia32_Ror, bemit_ror);
+       register_emitter(op_ia32_Sahf, bemit_sahf);
+       register_emitter(op_ia32_Sar, bemit_sar);
+       register_emitter(op_ia32_Sbb, bemit_sbb);
+       register_emitter(op_ia32_Shl, bemit_shl);
+       register_emitter(op_ia32_Shr, bemit_shr);
+       register_emitter(op_ia32_Stc, bemit_stc);
        register_emitter(op_ia32_Store, bemit_store);
-       register_emitter(op_ia32_Mul, bemit_mul);
-       register_emitter(op_ia32_IMul1OP, bemit_imul1op);
-       register_emitter(op_ia32_Div, bemit_div);
-       register_emitter(op_ia32_IDiv, bemit_idiv);
-       register_emitter(op_ia32_IJmp, bemit_ijmp);
+       register_emitter(op_ia32_Sub, bemit_sub);
+       register_emitter(op_ia32_Test, bemit_test);
+       register_emitter(op_ia32_Xor, bemit_xor);
+       register_emitter(op_ia32_Xor0, bemit_xor0);
 
        /* ignore the following nodes */
        register_emitter(op_ia32_ProduceVal, emit_Nothing);