- implemented ia32_ClimbFrame() pseudo-instruction
authorMichael Beck <beck@ipd.info.uni-karlsruhe.de>
Fri, 19 Dec 2008 15:03:36 +0000 (15:03 +0000)
committerMichael Beck <beck@ipd.info.uni-karlsruhe.de>
Fri, 19 Dec 2008 15:03:36 +0000 (15:03 +0000)
- fully implemented __builtin_(frame|return)_address in the backend

[r24812]

ir/be/ia32/bearch_ia32.c
ir/be/ia32/bearch_ia32_t.h
ir/be/ia32/ia32_emitter.c
ir/be/ia32/ia32_new_nodes.c
ir/be/ia32/ia32_new_nodes.h
ir/be/ia32/ia32_nodes_attr.h
ir/be/ia32/ia32_spec.pl
ir/be/ia32/ia32_transform.c

index 035d96f..2e61109 100644 (file)
@@ -521,6 +521,14 @@ ir_entity *ia32_get_return_address_entity(void) {
        return ia32_curr_fp_ommitted ? omit_fp_ret_addr_ent : ret_addr_ent;
 }
 
+/**
+ * Return the stack entity that contains the frame address.
+ */
+ir_entity *ia32_get_frame_address_entity(void) {
+       ia32_build_between_type();
+       return ia32_curr_fp_ommitted ? NULL : old_bp_ent;
+}
+
 /**
  * Get the estimated cycle count for @p irn.
  *
index a85ca4b..d58250a 100644 (file)
@@ -181,4 +181,9 @@ ir_entity *ia32_create_intrinsic_fkt(ir_type *method, const ir_op *op,
  */
 ir_entity *ia32_get_return_address_entity(void);
 
+/**
+ * Return the stack entity that contains the frame address.
+ */
+ir_entity *ia32_get_frame_address_entity(void);
+
 #endif
index 135ff9a..804bef4 100644 (file)
@@ -79,6 +79,7 @@ static ir_node *get_prev_block_sched(const ir_node *block)
        return get_irn_link(block);
 }
 
+/** Checks if the current block is a fall-through target. */
 static int is_fallthrough(const ir_node *cfgpred)
 {
        ir_node *pred;
@@ -92,6 +93,10 @@ static int is_fallthrough(const ir_node *cfgpred)
        return 1;
 }
 
+/**
+ * returns non-zero if the given block needs a label
+ * because of being a jump-target (and not a fall-through)
+ */
 static int block_needs_label(const ir_node *block)
 {
        int need_label = 1;
@@ -505,7 +510,8 @@ static void ia32_emit_cmp_suffix(int pnc)
 
 typedef enum ia32_emit_mod_t {
        EMIT_RESPECT_LS   = 1U << 0,
-       EMIT_ALTERNATE_AM = 1U << 1
+       EMIT_ALTERNATE_AM = 1U << 1,
+       EMIT_LONG         = 1U << 2
 } ia32_emit_mod_t;
 
 /**
@@ -524,10 +530,12 @@ typedef enum ia32_emit_mod_t {
  * %Sx  <node>                  source register x
  * %s   const char*             string
  * %u   unsigned int            unsigned int
+ * %d   signed int              signed int
  *
  * x starts at 0
  * # modifier for %ASx, %D and %S uses ls mode of node to alter register width
  * * modifier does not prefix immediates with $, but AM with *
+ * l modifier for %lu and %ld
  */
 static void ia32_emitf(const ir_node *node, const char *fmt, ...)
 {
@@ -566,6 +574,11 @@ static void ia32_emitf(const ir_node *node, const char *fmt, ...)
                        ++fmt;
                }
 
+               if (*fmt == 'l') {
+                       mod |= EMIT_LONG;
+                       ++fmt;
+               }
+
                switch (*fmt++) {
                        case '%':
                                be_emit_char('%');
@@ -679,15 +692,29 @@ emit_S:
                                break;
                        }
 
-                       case 'u': {
-                               unsigned num = va_arg(ap, unsigned);
-                               be_emit_irprintf("%u", num);
+                       case 'u':
+                               if (mod & EMIT_LONG) {
+                                       unsigned long num = va_arg(ap, unsigned long);
+                                       be_emit_irprintf("%lu", num);
+                               } else {
+                                       unsigned num = va_arg(ap, unsigned);
+                                       be_emit_irprintf("%u", num);
+                               }
+                               break;
+
+                       case 'd':
+                               if (mod & EMIT_LONG) {
+                                       long num = va_arg(ap, long);
+                                       be_emit_irprintf("%ld", num);
+                               } else {
+                                       int num = va_arg(ap, int);
+                                       be_emit_irprintf("%d", num);
+                               }
                                break;
-                       }
 
                        default:
 unknown:
-                               panic("unknown conversion");
+                               panic("unknown format conversion in ia32_emitf()");
                }
        }
 
@@ -1710,6 +1737,18 @@ static void emit_ia32_GetEIP(const ir_node *node)
        ia32_emitf(node, "\tpopl %D0\n");
 }
 
+static void emit_ia32_ClimbFrame(const ir_node *node)
+{
+       const ia32_climbframe_attr_t *attr = get_ia32_climbframe_attr_const(node);
+
+       ia32_emitf(node, "\tmovl %S0, %D0\n");
+       ia32_emitf(node, "\tmovl %S1, $%u\n", attr->count);
+       ia32_emitf(NULL, BLOCK_PREFIX "%ld:\n", get_irn_node_nr(node));
+       ia32_emitf(node, "\tmovl (%D0), %D0\n");
+       ia32_emitf(node, "\tdec %S1\n");
+       ia32_emitf(node, "\tjnz " BLOCK_PREFIX "%ld\n", get_irn_node_nr(node));
+}
+
 static void emit_be_Return(const ir_node *node)
 {
        unsigned pop = be_Return_get_pop(node);
@@ -1774,6 +1813,7 @@ static void ia32_register_emitters(void)
        IA32_EMIT(LdTls);
        IA32_EMIT(Minus64Bit);
        IA32_EMIT(SwitchJmp);
+       IA32_EMIT(ClimbFrame);
 
        /* benode emitter */
        BE_EMIT(Copy);
index 4014fe3..5f90808 100644 (file)
@@ -458,6 +458,20 @@ const ia32_copyb_attr_t *get_ia32_copyb_attr_const(const ir_node *node) {
        return copyb_attr;
 }
 
+ia32_climbframe_attr_t *get_ia32_climbframe_attr(ir_node *node) {
+       ia32_attr_t            *attr            = get_ia32_attr(node);
+       ia32_climbframe_attr_t *climbframe_attr = CAST_IA32_ATTR(ia32_climbframe_attr_t, attr);
+
+       return climbframe_attr;
+}
+
+const ia32_climbframe_attr_t *get_ia32_climbframe_attr_const(const ir_node *node) {
+       const ia32_attr_t            *attr            = get_ia32_attr_const(node);
+       const ia32_climbframe_attr_t *climbframe_attr = CONST_CAST_IA32_ATTR(ia32_climbframe_attr_t, attr);
+
+       return climbframe_attr;
+}
+
 /**
  * Gets the type of an ia32 node.
  */
@@ -1027,6 +1041,16 @@ init_ia32_condcode_attributes(ir_node *res, long pnc) {
        attr->pn_code = pnc;
 }
 
+void
+init_ia32_climbframe_attributes(ir_node *res, unsigned count) {
+       ia32_climbframe_attr_t *attr = get_irn_generic_attr(res);
+
+#ifndef NDEBUG
+       attr->attr.attr_type  |= IA32_ATTR_ia32_climbframe_attr_t;
+#endif
+       attr->count = count;
+}
+
 /***************************************************************************************
  *                  _                            _                   _
  *                 | |                          | |                 | |
@@ -1094,12 +1118,13 @@ int ia32_compare_condcode_attr(ir_node *a, ir_node *b)
        attr_a = get_ia32_condcode_attr_const(a);
        attr_b = get_ia32_condcode_attr_const(b);
 
-       if(attr_a->pn_code != attr_b->pn_code)
+       if (attr_a->pn_code != attr_b->pn_code)
                return 1;
 
        return 0;
 }
 
+/** Compare node attributes for call nodes. */
 static int ia32_compare_call_attr(ir_node *a, ir_node *b)
 {
        const ia32_call_attr_t *attr_a;
@@ -1133,7 +1158,7 @@ int ia32_compare_copyb_attr(ir_node *a, ir_node *b)
        attr_a = get_ia32_copyb_attr_const(a);
        attr_b = get_ia32_copyb_attr_const(b);
 
-       if(attr_a->size != attr_b->size)
+       if (attr_a->size != attr_b->size)
                return 1;
 
        return 0;
@@ -1147,7 +1172,7 @@ int ia32_compare_asm_attr(ir_node *a, ir_node *b)
        const ia32_asm_attr_t *attr_a;
        const ia32_asm_attr_t *attr_b;
 
-       if(ia32_compare_nodes_attr(a, b))
+       if (ia32_compare_nodes_attr(a, b))
                return 1;
 
        attr_a = get_ia32_asm_attr_const(a);
@@ -1175,9 +1200,9 @@ int ia32_compare_immediate_attr(ir_node *a, ir_node *b)
        const ia32_immediate_attr_t *attr_a = get_ia32_immediate_attr_const(a);
        const ia32_immediate_attr_t *attr_b = get_ia32_immediate_attr_const(b);
 
-       if(attr_a->symconst != attr_b->symconst ||
-          attr_a->sc_sign != attr_b->sc_sign ||
-          attr_a->offset != attr_b->offset)
+       if (attr_a->symconst != attr_b->symconst ||
+           attr_a->sc_sign != attr_b->sc_sign ||
+           attr_a->offset != attr_b->offset)
                return 1;
 
        return 0;
@@ -1190,6 +1215,24 @@ int ia32_compare_x87_attr(ir_node *a, ir_node *b)
        return ia32_compare_nodes_attr(a, b);
 }
 
+/** Compare node attributes for ClimbFrame nodes. */
+static
+int ia32_compare_climbframe_attr(ir_node *a, ir_node *b)
+{
+       const ia32_climbframe_attr_t *attr_a;
+       const ia32_climbframe_attr_t *attr_b;
+
+       if (ia32_compare_nodes_attr(a, b))
+               return 1;
+
+       attr_a = get_ia32_climbframe_attr_const(a);
+       attr_b = get_ia32_climbframe_attr_const(b);
+
+       if (attr_a->count != attr_b->count)
+               return 1;
+
+       return 0;
+}
 
 /* copies the ia32 attributes */
 static void ia32_copy_attr(const ir_node *old_node, ir_node *new_node)
index 0a5d8c9..857d359 100644 (file)
@@ -97,6 +97,12 @@ const ia32_call_attr_t *get_ia32_call_attr_const(const ir_node *node);
 ia32_copyb_attr_t *get_ia32_copyb_attr(ir_node *node);
 const ia32_copyb_attr_t *get_ia32_copyb_attr_const(const ir_node *node);
 
+/**
+ * Gets the ClimbFrame node attributes.
+ */
+ia32_climbframe_attr_t *get_ia32_climbframe_attr(ir_node *node);
+const ia32_climbframe_attr_t *get_ia32_climbframe_attr_const(const ir_node *node);
+
 /**
  * Gets the type of an ia32 node.
  */
@@ -409,6 +415,7 @@ void init_ia32_immediate_attributes(ir_node *node, ir_entity *symconst,
 void init_ia32_call_attributes(ir_node *res, unsigned pop, ir_type *call_tp);
 void init_ia32_copyb_attributes(ir_node *res, unsigned size);
 void init_ia32_condcode_attributes(ir_node *res, long pnc);
+void init_ia32_climbframe_attributes(ir_node *res, unsigned count);
 
 /* Include the generated headers */
 #include "gen_ia32_new_nodes.h"
index aab1441..988e856 100644 (file)
@@ -71,14 +71,15 @@ struct ia32_op_attr_t {
 
 #ifndef NDEBUG
 typedef enum {
-       IA32_ATTR_INVALID               = 0,
-       IA32_ATTR_ia32_attr_t           = 1 << 0,
-       IA32_ATTR_ia32_x87_attr_t       = 1 << 1,
-       IA32_ATTR_ia32_asm_attr_t       = 1 << 2,
-       IA32_ATTR_ia32_immediate_attr_t = 1 << 3,
-       IA32_ATTR_ia32_condcode_attr_t  = 1 << 4,
-       IA32_ATTR_ia32_copyb_attr_t     = 1 << 5,
-       IA32_ATTR_ia32_call_attr_t      = 1 << 6
+       IA32_ATTR_INVALID                = 0,
+       IA32_ATTR_ia32_attr_t            = 1 << 0,
+       IA32_ATTR_ia32_x87_attr_t        = 1 << 1,
+       IA32_ATTR_ia32_asm_attr_t        = 1 << 2,
+       IA32_ATTR_ia32_immediate_attr_t  = 1 << 3,
+       IA32_ATTR_ia32_condcode_attr_t   = 1 << 4,
+       IA32_ATTR_ia32_copyb_attr_t      = 1 << 5,
+       IA32_ATTR_ia32_call_attr_t       = 1 << 6,
+       IA32_ATTR_ia32_climbframe_attr_t = 1 << 7,
 } ia32_attr_type_t;
 #endif
 
@@ -201,6 +202,15 @@ struct ia32_asm_attr_t {
        const ia32_asm_reg_t *register_map;
 };
 
+/**
+ * The attributes for the ClimbFrame node.
+ */
+typedef struct ia32_climbframe_attr_t ia32_climbframe_attr_t;
+struct ia32_climbframe_attr_t {
+       ia32_attr_t attr;      /**< generic attribute */
+       unsigned    count;     /**< number of frames to climb up */
+};
+
 /* the following union is necessary to indicate to the compiler that we might want to cast
  * the structs (we use them to simulate OO-inheritance) */
 union allow_casts_attr_t_ {
@@ -211,6 +221,7 @@ union allow_casts_attr_t_ {
        ia32_x87_attr_t        x87_attr;
        ia32_asm_attr_t        asm_attr;
        ia32_immediate_attr_t  immediate_attr;
+       ia32_climbframe_attr_t climbframe_attr;
 };
 
 #ifndef NDEBUG
index 8307d45..000c1a9 100644 (file)
@@ -290,16 +290,20 @@ $custom_init_attr_func = \&ia32_custom_init_attr;
        ia32_x87_attr_t =>
                "\tinit_ia32_attributes(res, flags, in_reqs, out_reqs, exec_units, n_res);\n".
                "\tinit_ia32_x87_attributes(res);",
+       ia32_climbframe_attr_t =>
+               "\tinit_ia32_attributes(res, flags, in_reqs, out_reqs, exec_units, n_res);\n".
+               "\tinit_ia32_climbframe_attributes(res, count);",
 );
 
 %compare_attr = (
        ia32_asm_attr_t       => "ia32_compare_asm_attr",
-       ia32_attr_t           => "ia32_compare_nodes_attr",
-       ia32_call_attr_t      => "ia32_compare_call_attr",
-       ia32_condcode_attr_t  => "ia32_compare_condcode_attr",
-       ia32_copyb_attr_t     => "ia32_compare_copyb_attr",
-       ia32_immediate_attr_t => "ia32_compare_immediate_attr",
-       ia32_x87_attr_t       => "ia32_compare_x87_attr",
+       ia32_attr_t            => "ia32_compare_nodes_attr",
+       ia32_call_attr_t       => "ia32_compare_call_attr",
+       ia32_condcode_attr_t   => "ia32_compare_condcode_attr",
+       ia32_copyb_attr_t      => "ia32_compare_copyb_attr",
+       ia32_immediate_attr_t  => "ia32_compare_immediate_attr",
+       ia32_x87_attr_t        => "ia32_compare_x87_attr",
+       ia32_climbframe_attr_t => "ia32_compare_climbframe_attr",
 );
 
 %operands = (
@@ -1499,6 +1503,22 @@ Call => {
        modified_flags => $status_flags
 },
 
+#
+# a Helper node for frame-climbing, needed for __builtin_(frame|return)_address
+#
+# PS: try gcc __builtin_frame_address(100000) :-)
+#
+ClimbFrame => {
+       reg_req   => { in => [ "gp", "gp", "gp"], out => [ "in_r3" ] },
+       ins       => [ "frame", "cnt", "tmp" ],
+       outs      => [ "res" ],
+       latency   => 4, # random number
+       attr_type => "ia32_climbframe_attr_t",
+       attr      => "unsigned count",
+       units     => [ "GP" ],
+       mode      => $mode_gp
+},
+
 #-----------------------------------------------------------------------------#
 #   _____ _____ ______    __ _             _                     _            #
 #  / ____/ ____|  ____|  / _| |           | |                   | |           #
index 0248137..145e10b 100644 (file)
@@ -4522,41 +4522,93 @@ static ir_node *gen_be_Call(ir_node *node)
  * Transform Builtin return_address
  */
 static ir_node *gen_return_address(ir_node *node) {
-       ir_node *param = get_Builtin_param(node, 0);
-       ir_node *frame = get_Builtin_param(node, 1);
-       dbg_info *dbgi = get_irn_dbg_info(node);
-       tarval  *tv    = get_Const_tarval(param);
-       long    value  = get_tarval_long(tv);
+       ir_node *param      = get_Builtin_param(node, 0);
+       ir_node *frame      = get_Builtin_param(node, 1);
+       dbg_info *dbgi      = get_irn_dbg_info(node);
+       tarval  *tv         = get_Const_tarval(param);
+       unsigned long value = get_tarval_long(tv);
 
-       ir_node *block = be_transform_node(get_nodes_block(node));
+       ir_node *block  = be_transform_node(get_nodes_block(node));
+       ir_node *ptr    = be_transform_node(frame);
+       ir_node *noreg  = ia32_new_NoReg_gp(env_cg);
+       ir_node *load;
 
-       if (value == 0) {
-               /* the return address of the current function */
-               ir_node   *new_op = be_transform_node(frame);
-               ir_node   *noreg  = ia32_new_NoReg_gp(env_cg);
-               ir_node   *new_node;
+       if (value > 0) {
+               ir_node *cnt = new_bd_ia32_ProduceVal(dbgi, block);
+               ir_node *res = new_bd_ia32_ProduceVal(dbgi, block);
+               ptr = new_bd_ia32_ClimbFrame(dbgi, block, ptr, cnt, res, value);
+       }
 
-               new_node = new_bd_ia32_Load(dbgi, block, new_op, noreg, get_irg_no_mem(current_ir_graph));
+       /* load the return address from this frame */
+       load = new_bd_ia32_Load(dbgi, block, ptr, noreg, get_irg_no_mem(current_ir_graph));
 
-               set_irn_pinned(new_node, get_irn_pinned(node));
-               set_ia32_op_type(new_node, ia32_AddrModeS);
-               set_ia32_ls_mode(new_node, mode_Iu);
+       set_irn_pinned(load, get_irn_pinned(node));
+       set_ia32_op_type(load, ia32_AddrModeS);
+       set_ia32_ls_mode(load, mode_Iu);
 
-               set_ia32_am_offs_int(new_node, 0);
-               set_ia32_use_frame(new_node);
-               set_ia32_frame_ent(new_node, ia32_get_return_address_entity());
+       set_ia32_am_offs_int(load, 0);
+       set_ia32_use_frame(load);
+       set_ia32_frame_ent(load, ia32_get_return_address_entity());
 
-               if (get_irn_pinned(node) == op_pin_state_floats) {
-                       assert(pn_ia32_xLoad_res == pn_ia32_vfld_res
-                                       && pn_ia32_vfld_res == pn_ia32_Load_res
-                                       && pn_ia32_Load_res == pn_ia32_res);
-                       arch_irn_add_flags(new_node, arch_irn_flags_rematerializable);
-               }
+       if (get_irn_pinned(node) == op_pin_state_floats) {
+               assert(pn_ia32_xLoad_res == pn_ia32_vfld_res
+                               && pn_ia32_vfld_res == pn_ia32_Load_res
+                               && pn_ia32_Load_res == pn_ia32_res);
+               arch_irn_add_flags(load, arch_irn_flags_rematerializable);
+       }
 
-               SET_IA32_ORIG_NODE(new_node, node);
-               return new_rd_Proj(dbgi, current_ir_graph, block, new_node, mode_Iu, pn_ia32_Load_res);
+       SET_IA32_ORIG_NODE(load, node);
+       return new_rd_Proj(dbgi, current_ir_graph, block, load, mode_Iu, pn_ia32_Load_res);
+}
+
+/**
+ * Transform Builtin frame_address
+ */
+static ir_node *gen_frame_address(ir_node *node) {
+       ir_node *param      = get_Builtin_param(node, 0);
+       ir_node *frame      = get_Builtin_param(node, 1);
+       dbg_info *dbgi      = get_irn_dbg_info(node);
+       tarval  *tv         = get_Const_tarval(param);
+       unsigned long value = get_tarval_long(tv);
+
+       ir_node *block  = be_transform_node(get_nodes_block(node));
+       ir_node *ptr    = be_transform_node(frame);
+       ir_node *noreg  = ia32_new_NoReg_gp(env_cg);
+       ir_node *load;
+       ir_entity *ent;
+
+       if (value > 0) {
+               ir_node *cnt = new_bd_ia32_ProduceVal(dbgi, block);
+               ir_node *res = new_bd_ia32_ProduceVal(dbgi, block);
+               ptr = new_bd_ia32_ClimbFrame(dbgi, block, ptr, cnt, res, value);
+       }
+
+       /* load the return address from this frame */
+       load = new_bd_ia32_Load(dbgi, block, ptr, noreg, get_irg_no_mem(current_ir_graph));
+
+       set_irn_pinned(load, get_irn_pinned(node));
+       set_ia32_op_type(load, ia32_AddrModeS);
+       set_ia32_ls_mode(load, mode_Iu);
+
+       ent = ia32_get_frame_address_entity();
+       if (ent != NULL) {
+               set_ia32_am_offs_int(load, 0);
+               set_ia32_use_frame(load);
+               set_ia32_frame_ent(load, ent);
+       } else {
+               /* will fail anyway, but gcc does this: */
+               set_ia32_am_offs_int(load, 0);
+       }
+
+       if (get_irn_pinned(node) == op_pin_state_floats) {
+               assert(pn_ia32_xLoad_res == pn_ia32_vfld_res
+                               && pn_ia32_vfld_res == pn_ia32_Load_res
+                               && pn_ia32_Load_res == pn_ia32_res);
+               arch_irn_add_flags(load, arch_irn_flags_rematerializable);
        }
-       panic("builtin_return_address(%ld) not supported in IA32", value);
+
+       SET_IA32_ORIG_NODE(load, node);
+       return new_rd_Proj(dbgi, current_ir_graph, block, load, mode_Iu, pn_ia32_Load_res);
 }
 
 /**
@@ -4569,6 +4621,7 @@ static ir_node *gen_Builtin(ir_node *node) {
        case ir_bk_return_address:
                return gen_return_address(node);
        case ir_bk_frame_addess:
+               return gen_frame_address(node);
        case ir_bk_prefetch:
                break;
        }