- implemented __builtin_return_address(0)
[libfirm] / ir / be / ia32 / bearch_ia32.c
index b06d3b0..035d96f 100644 (file)
@@ -101,6 +101,14 @@ static set *cur_reg_set = NULL;
 ir_mode         *mode_fpcw       = NULL;
 ia32_code_gen_t *ia32_current_cg = NULL;
 
+/** The current omit-fp state */
+static unsigned ia32_curr_fp_ommitted  = 0;
+static ir_type *omit_fp_between_type   = NULL;
+static ir_type *between_type           = NULL;
+static ir_entity *old_bp_ent           = NULL;
+static ir_entity *ret_addr_ent         = NULL;
+static ir_entity *omit_fp_ret_addr_ent = NULL;
+
 /**
  * The environment for the intrinsic mapping.
  */
@@ -337,6 +345,7 @@ static const arch_register_t *ia32_abi_prologue(void *self, ir_node **mem, pmap
        ia32_code_gen_t  *cg       = ia32_current_cg;
        const arch_env_t *arch_env = env->aenv;
 
+       ia32_curr_fp_ommitted = env->flags.try_omit_fp;
        if (! env->flags.try_omit_fp) {
                ir_graph *irg     = env->irg;
                ir_node  *bl      = get_irg_start_block(irg);
@@ -464,23 +473,11 @@ static void ia32_abi_done(void *self) {
 }
 
 /**
- * Produces the type which sits between the stack args and the locals on the stack.
- * it will contain the return address and space to store the old base pointer.
- * @return The Firm type modeling the ABI between type.
+ * Build the between type and entities if not already build.
  */
-static ir_type *ia32_abi_get_between_type(void *self)
-{
+static void ia32_build_between_type(void) {
 #define IDENT(s) new_id_from_chars(s, sizeof(s)-1)
-       static ir_type *omit_fp_between_type = NULL;
-       static ir_type *between_type         = NULL;
-
-       ia32_abi_env_t *env = self;
-
        if (! between_type) {
-               ir_entity *old_bp_ent;
-               ir_entity *ret_addr_ent;
-               ir_entity *omit_fp_ret_addr_ent;
-
                ir_type *old_bp_type   = new_type_primitive(IDENT("bp"), mode_Iu);
                ir_type *ret_addr_type = new_type_primitive(IDENT("return_addr"), mode_Iu);
 
@@ -500,9 +497,28 @@ static ir_type *ia32_abi_get_between_type(void *self)
                set_type_size_bytes(omit_fp_between_type, get_type_size_bytes(ret_addr_type));
                set_type_state(omit_fp_between_type, layout_fixed);
        }
+#undef IDENT
+}
 
+/**
+ * Produces the type which sits between the stack args and the locals on the stack.
+ * it will contain the return address and space to store the old base pointer.
+ * @return The Firm type modeling the ABI between type.
+ */
+static ir_type *ia32_abi_get_between_type(void *self)
+{
+       ia32_abi_env_t *env = self;
+
+       ia32_build_between_type();
        return env->flags.try_omit_fp ? omit_fp_between_type : between_type;
-#undef IDENT
+}
+
+/**
+ * Return the stack entity that contains the return address.
+ */
+ir_entity *ia32_get_return_address_entity(void) {
+       ia32_build_between_type();
+       return ia32_curr_fp_ommitted ? omit_fp_ret_addr_ent : ret_addr_ent;
 }
 
 /**
@@ -867,11 +883,18 @@ static void ia32_before_abi(void *self) {
  * Transforms the standard firm graph into
  * an ia32 firm graph
  */
-static void ia32_prepare_graph(void *self) {
-       ia32_code_gen_t *cg = self;
+static void ia32_prepare_graph(void *self)
+{
+       ia32_code_gen_t *cg  = self;
+       ir_graph        *irg = cg->irg;
 
        /* do local optimizations */
-       optimize_graph_df(cg->irg);
+       optimize_graph_df(irg);
+
+       /* we have to do cfopt+remove_critical_edges as we can't have Bad-blocks
+        * or critical edges in the backend */
+       optimize_cf(irg);
+       remove_critical_cf_edges(irg);
 
        /* TODO: we often have dead code reachable through out-edges here. So for
         * now we rebuild edges (as we need correct user count for code selection)
@@ -2043,9 +2066,38 @@ static void ia32_mark_remat(const void *self, ir_node *node) {
 }
 
 /**
- * Check for Abs or Nabs.
+ * Check for Abs or -Abs.
+ */
+static int psi_is_Abs_or_Nabs(ir_node *cmp, ir_node *sel, ir_node *t, ir_node *f) {
+       ir_node *l, *r;
+       pn_Cmp  pnc;
+
+       if (cmp == NULL)
+               return 0;
+
+       /* must be <, <=, >=, > */
+       pnc = get_Proj_proj(sel);
+       if (pnc != pn_Cmp_Ge && pnc != pn_Cmp_Gt &&
+               pnc != pn_Cmp_Le && pnc != pn_Cmp_Lt)
+               return 0;
+
+       l = get_Cmp_left(cmp);
+       r = get_Cmp_right(cmp);
+
+       /* must be x cmp 0 */
+       if ((l != t && l != f) || !is_Const(r) || !is_Const_null(r))
+               return 0;
+
+       if ((!is_Minus(t) || get_Minus_op(t) != f) &&
+               (!is_Minus(f) || get_Minus_op(f) != t))
+               return 0;
+       return 1;
+}
+
+/**
+ * Check for Abs only
  */
-static int is_Abs_or_Nabs(ir_node *cmp, ir_node *sel, ir_node *t, ir_node *f) {
+static int psi_is_Abs(ir_node *cmp, ir_node *sel, ir_node *t, ir_node *f) {
        ir_node *l, *r;
        pn_Cmp  pnc;
 
@@ -2068,11 +2120,22 @@ static int is_Abs_or_Nabs(ir_node *cmp, ir_node *sel, ir_node *t, ir_node *f) {
        if ((!is_Minus(t) || get_Minus_op(t) != f) &&
                (!is_Minus(f) || get_Minus_op(f) != t))
                return 0;
+
+       if (pnc & pn_Cmp_Gt) {
+               /* x >= 0 ? -x : x is NABS */
+               if (is_Minus(t))
+                       return 0;
+       } else {
+               /* x < 0 ? x : -x is NABS */
+               if (is_Minus(f))
+                       return 0;
+       }
        return 1;
 }
 
+
 /**
- * Allows or disallows the creation of Psi nodes for the given Phi nodes.
+ * Allows or disallows the creation of Mux nodes for the given Phi nodes.
  *
  * @param sel        A selector of a Cond.
  * @param phi_list   List of Phi nodes about to be converted (linked via get_Phi_next() field)
@@ -2081,30 +2144,45 @@ static int is_Abs_or_Nabs(ir_node *cmp, ir_node *sel, ir_node *t, ir_node *f) {
  *
  * @return 1 if allowed, 0 otherwise
  */
-static int ia32_is_psi_allowed(ir_node *sel, ir_node *phi_list, int i, int j)
+static int ia32_is_mux_allowed(ir_node *sel, ir_node *phi_list, int i, int j)
 {
        ir_node *phi;
-       ir_node *cmp = NULL;
+       ir_node *cmp;
+       pn_Cmp  pn;
+       ir_node *cl, *cr;
 
-       /* we can't handle Psis with 64bit compares yet */
+       /* we can't handle Muxs with 64bit compares yet */
        if (is_Proj(sel)) {
                cmp = get_Proj_pred(sel);
                if (is_Cmp(cmp)) {
                        ir_node *left     = get_Cmp_left(cmp);
                        ir_mode *cmp_mode = get_irn_mode(left);
-                       if (!mode_is_float(cmp_mode) && get_mode_size_bits(cmp_mode) > 32)
-                               return 0;
+                       if (!mode_is_float(cmp_mode) && get_mode_size_bits(cmp_mode) > 32) {
+                               /* 64bit Abs IS supported */
+                               for (phi = phi_list; phi; phi = get_Phi_next(phi)) {
+                                       ir_node *t = get_Phi_pred(phi, i);
+                                       ir_node *f = get_Phi_pred(phi, j);
+
+                                       if (! psi_is_Abs(cmp, sel, t, f))
+                                               return 0;
+                               }
+                               return 1;
+                       }
                } else {
-                       cmp = NULL;
+                       /* we do not support nodes without Cmp yet */
+                       return 0;
                }
+       } else {
+               /* we do not support nodes without Cmp yet */
+               return 0;
        }
 
-       if (ia32_cg_config.use_cmov) {
-               if (ia32_cg_config.use_sse2 && cmp != NULL) {
-                       pn_Cmp pn   = get_Proj_proj(sel);
-                       ir_node *cl = get_Cmp_left(cmp);
-                       ir_node *cr = get_Cmp_right(cmp);
+       pn = get_Proj_proj(sel);
+       cl = get_Cmp_left(cmp);
+       cr = get_Cmp_right(cmp);
 
+       if (ia32_cg_config.use_cmov) {
+               if (ia32_cg_config.use_sse2) {
                        /* check the Phi nodes: no 64bit and no floating point cmov */
                        for (phi = phi_list; phi; phi = get_Phi_next(phi)) {
                                ir_mode *mode = get_irn_mode(phi);
@@ -2113,23 +2191,22 @@ static int ia32_is_psi_allowed(ir_node *sel, ir_node *phi_list, int i, int j)
                                        /* check for Min, Max */
                                        ir_node *t = get_Phi_pred(phi, i);
                                        ir_node *f = get_Phi_pred(phi, j);
-                                       int res    = 0;
 
                                        /* SSE2 supports Min & Max */
                                        if (pn == pn_Cmp_Lt || pn == pn_Cmp_Le || pn == pn_Cmp_Ge || pn == pn_Cmp_Gt) {
                                                if (cl == t && cr == f) {
-                                                       /* Psi(a <=/>= b, a, b) => MIN, MAX */
-                                                       res = 1;
+                                                       /* Mux(a <=/>= b, a, b) => MIN, MAX */
+                                                       continue;
                                                } else if (cl == f && cr == t) {
-                                                       /* Psi(a <=/>= b, b, a) => MAX, MIN */
-                                                       res = 1;
+                                                       /* Mux(a <=/>= b, b, a) => MAX, MIN */
+                                                       continue;
                                                }
                                        }
-                                       if (! res)
-                                               return 0;
-
-                               } else if (get_mode_size_bits(mode) > 32)
                                        return 0;
+                               } else if (get_mode_size_bits(mode) > 32) {
+                                       /* no 64bit cmov */
+                                       return 0;
+                               }
                        }
                } else {
                        /* check the Phi nodes: no 64bit and no floating point cmov */
@@ -2140,7 +2217,11 @@ static int ia32_is_psi_allowed(ir_node *sel, ir_node *phi_list, int i, int j)
                                        ir_node *t = get_Phi_pred(phi, i);
                                        ir_node *f = get_Phi_pred(phi, j);
 
-                                       if (! is_Abs_or_Nabs(cmp, sel, t, f))
+                                       /* always support Mux(!float, C1, C2) */
+                                       if (is_Const(t) && is_Const(f) && !mode_is_float(get_irn_mode(cl)))
+                                               continue;
+                                       /* only abs or nabs supported */
+                                       if (! psi_is_Abs_or_Nabs(cmp, sel, t, f))
                                                return 0;
                                } else if (get_mode_size_bits(mode) > 32)
                                        return 0;
@@ -2148,30 +2229,22 @@ static int ia32_is_psi_allowed(ir_node *sel, ir_node *phi_list, int i, int j)
                }
 
                return 1;
-       } else {
-               ir_node *cl, *cr;
-               pn_Cmp  pn;
-
-               /* No Cmov, only some special cases */
-               if (cmp == NULL)
-                       return 0;
+       } else { /* No Cmov, only some special cases */
 
                /* Now some supported cases here */
-               pn = get_Proj_proj(sel);
-               cl = get_Cmp_left(cmp);
-               cr = get_Cmp_right(cmp);
-
                for (phi = phi_list; phi; phi = get_Phi_next(phi)) {
                        ir_mode *mode = get_irn_mode(phi);
-                       int res = 0;
                        ir_node *t, *f;
 
                        t = get_Phi_pred(phi, i);
                        f = get_Phi_pred(phi, j);
 
                        if (mode_is_float(mode)) {
+                               /* always support Mux(!float, C1, C2) */
+                               if (is_Const(t) && is_Const(f) && !mode_is_float(get_irn_mode(cl)))
+                                       continue;
                                /* only abs or nabs supported */
-                               if (! is_Abs_or_Nabs(cmp, sel, t, f))
+                               if (! psi_is_Abs_or_Nabs(cmp, sel, t, f))
                                        return 0;
                        } else if (get_mode_size_bits(mode) > 32) {
                                /* no 64bit yet */
@@ -2180,41 +2253,43 @@ static int ia32_is_psi_allowed(ir_node *sel, ir_node *phi_list, int i, int j)
 
                        if (is_Const(t) && is_Const(f)) {
                                if ((is_Const_null(t) && is_Const_one(f)) || (is_Const_one(t) && is_Const_null(f))) {
-                                       /* always support Psi(x, C1, C2) */
-                                       res = 1;
+                                       /* always support Mux(x, C1, C2) */
+                                       continue;
                                }
                        } else if (pn == pn_Cmp_Lt || pn == pn_Cmp_Le || pn == pn_Cmp_Ge || pn == pn_Cmp_Gt) {
-                               if (0) {
 #if 0
-                               } else if (cl == t && cr == f) {
-                                       /* Psi(a <=/>= b, a, b) => Min, Max */
-                                       res = 1;
-                               } else if (cl == f && cr == t) {
-                                       /* Psi(a <=/>= b, b, a) => Max, Min */
-                                       res = 1;
+                               if (cl == t && cr == f) {
+                                       /* Mux(a <=/>= b, a, b) => Min, Max */
+                                       continue;
+                               }
+                               if (cl == f && cr == t) {
+                                       /* Mux(a <=/>= b, b, a) => Max, Min */
+                                       continue;
+                               }
 #endif
-                               } else if ((pn & pn_Cmp_Gt) && !mode_is_signed(mode) &&
-                                          is_Const(f) && is_Const_null(f) && is_Sub(t) &&
-                                          get_Sub_left(t) == cl && get_Sub_right(t) == cr) {
-                                       /* Psi(a >=u b, a - b, 0) unsigned Doz */
-                                       res = 1;
-                               } else if ((pn & pn_Cmp_Lt) && !mode_is_signed(mode) &&
-                                          is_Const(t) && is_Const_null(t) && is_Sub(f) &&
-                                          get_Sub_left(f) == cl && get_Sub_right(f) == cr) {
-                                       /* Psi(a <=u b, 0, a - b) unsigned Doz */
-                                       res = 1;
-                               } else if (is_Const(cr) && is_Const_null(cr)) {
+                               if ((pn & pn_Cmp_Gt) && !mode_is_signed(mode) &&
+                                   is_Const(f) && is_Const_null(f) && is_Sub(t) &&
+                                   get_Sub_left(t) == cl && get_Sub_right(t) == cr) {
+                                       /* Mux(a >=u b, a - b, 0) unsigned Doz */
+                                       continue;
+                               }
+                               if ((pn & pn_Cmp_Lt) && !mode_is_signed(mode) &&
+                                   is_Const(t) && is_Const_null(t) && is_Sub(f) &&
+                                   get_Sub_left(f) == cl && get_Sub_right(f) == cr) {
+                                       /* Mux(a <=u b, 0, a - b) unsigned Doz */
+                                       continue;
+                               }
+                               if (is_Const(cr) && is_Const_null(cr)) {
                                        if (cl == t && is_Minus(f) && get_Minus_op(f) == cl) {
-                                               /* Psi(a <=/>= 0 ? a : -a) Nabs/Abs */
-                                               res = 1;
+                                               /* Mux(a <=/>= 0 ? a : -a) Nabs/Abs */
+                                               continue;
                                        } else if (cl == f && is_Minus(t) && get_Minus_op(t) == cl) {
-                                               /* Psi(a <=/>= 0 ? -a : a) Abs/Nabs */
-                                               res = 1;
+                                               /* Mux(a <=/>= 0 ? -a : a) Abs/Nabs */
+                                               continue;
                                        }
                                }
                        }
-                       if (! res)
-                               return 0;
+                       return 0;
                }
                /* all checks passed */
                return 1;
@@ -2239,13 +2314,40 @@ static int ia32_is_valid_clobber(const void *self, const char *clobber)
        return ia32_get_clobber_register(clobber) != NULL;
 }
 
+/**
+ * Create the trampoline code.
+ */
+static ir_node *ia32_create_trampoline_fkt(ir_node *block, ir_node *mem, ir_node *trampoline, ir_node *env, ir_node *callee)
+{
+       ir_graph *irg    = get_Block_irg(block);
+       ir_node  *st, *p = trampoline;
+       ir_mode *mode    = get_irn_mode(p);
+
+       /* mov  ecx,<env> */
+       st  = new_r_Store(irg, block, mem, p, new_Const_long(mode_Bu, 0xb9), 0);
+       mem = new_r_Proj(irg, block, st, mode_M, pn_Store_M);
+       p   = new_r_Add(irg, block, p, new_Const_long(mode_Iu, 1), mode);
+       st  = new_r_Store(irg, block, mem, p, env, 0);
+       mem = new_r_Proj(irg, block, st, mode_M, pn_Store_M);
+       p   = new_r_Add(irg, block, p, new_Const_long(mode_Iu, 4), mode);
+       /* jmp  <callee> */
+       st  = new_r_Store(irg, block, mem, p, new_Const_long(mode_Bu, 0xe9), 0);
+       mem = new_r_Proj(irg, block, st, mode_M, pn_Store_M);
+       p   = new_r_Add(irg, block, p, new_Const_long(mode_Iu, 1), mode);
+       st  = new_r_Store(irg, block, mem, p, callee, 0);
+       mem = new_r_Proj(irg, block, st, mode_M, pn_Store_M);
+       p   = new_r_Add(irg, block, p, new_Const_long(mode_Iu, 4), mode);
+
+       return mem;
+}
+
 /**
  * Returns the libFirm configuration parameter for this backend.
  */
 static const backend_params *ia32_get_libfirm_params(void) {
        static const ir_settings_if_conv_t ifconv = {
-               4,                    /* maxdepth, doesn't matter for Psi-conversion */
-               ia32_is_psi_allowed   /* allows or disallows Psi creation for given selector */
+               4,                    /* maxdepth, doesn't matter for Mux-conversion */
+               ia32_is_mux_allowed   /* allows or disallows Mux creation for given selector */
        };
        static const ir_settings_arch_dep_t ad = {
                1,                   /* also use subs */
@@ -2255,18 +2357,19 @@ static const backend_params *ia32_get_libfirm_params(void) {
 
                1,  /* allow Mulhs */
                1,  /* allow Mulus */
-               32  /* Mulh allowed up to 32 bit */
+               32, /* Mulh allowed up to 32 bit */
        };
        static backend_params p = {
                1,     /* need dword lowering */
                1,     /* support inline assembly */
-               0,     /* no immediate floating point mode. */
-               NULL,  /* no additional opcodes */
                NULL,  /* will be set later */
                ia32_create_intrinsic_fkt,
                &intrinsic_env,  /* context for ia32_create_intrinsic_fkt */
-               NULL,  /* will be set below */
-               NULL   /* will be set below */
+               NULL,  /* ifconv info will be set below */
+               NULL,  /* float arithmetic mode, will be set below */
+               12,    /* size of trampoline code */
+               4,     /* alignment of trampoline code */
+               ia32_create_trampoline_fkt,
        };
 
        ia32_setup_cg_config();
@@ -2275,8 +2378,9 @@ static const backend_params *ia32_get_libfirm_params(void) {
         * is called... */
        init_asm_constraints();
 
-       p.dep_param    = &ad;
-       p.if_conv_info = &ifconv;
+       p.dep_param             = &ad;
+       p.if_conv_info          = &ifconv;
+       p.mode_float_arithmetic = mode_E;
        return &p;
 }