sparc: implement 64bit lowering
authorMatthias Braun <matze@braunis.de>
Wed, 11 May 2011 12:36:05 +0000 (14:36 +0200)
committerMatthias Braun <matze@braunis.de>
Mon, 23 May 2011 12:51:20 +0000 (14:51 +0200)
ir/be/sparc/bearch_sparc.c
ir/be/sparc/bearch_sparc_t.h
ir/be/sparc/sparc_emitter.c
ir/be/sparc/sparc_lower64.c [new file with mode: 0644]
ir/be/sparc/sparc_new_nodes.c
ir/be/sparc/sparc_nodes_attr.h
ir/be/sparc/sparc_spec.pl
ir/be/sparc/sparc_transform.c
ir/lower/lower_dw.c
ir/lower/lower_dw.h

index cdb5877..e342111 100644 (file)
@@ -38,6 +38,7 @@
 #include "irtools.h"
 #include "irdump.h"
 #include "lowering.h"
+#include "lower_dw.h"
 
 #include "bitset.h"
 #include "debug.h"
@@ -125,7 +126,7 @@ static int sparc_get_sp_bias(const ir_node *node)
 
 /* fill register allocator interface */
 
-static const arch_irn_ops_t sparc_irn_ops = {
+const arch_irn_ops_t sparc_irn_ops = {
        sparc_classify,
        sparc_get_frame_entity,
        sparc_set_frame_offset,
@@ -555,9 +556,10 @@ static void sparc_lower_for_target(void)
                NULL,                                  /* find pointer type */
                NULL,                                  /* ret_compound_in_regs */
        };
-
        lower_calls_with_compounds(&params);
 
+       sparc_lower_64bit();
+
        for (i = 0; i < n_irgs; ++i) {
                ir_graph *irg = get_irp_irg(i);
                ir_lower_mode_b(irg, &lower_mode_b_config);
index 650ec68..6e69d80 100644 (file)
@@ -50,6 +50,8 @@ struct sparc_transform_env_t {
        ir_mode  *mode;     /**< The mode of the irn */
 };
 
+extern const arch_irn_ops_t sparc_irn_ops;
+
 /**
  * SPARC ABI requires some space which is always available at the top of
  * the stack. It contains:
@@ -76,4 +78,6 @@ void sparc_finish(ir_graph *irg);
 
 void sparc_introduce_prolog_epilog(ir_graph *irg);
 
+void sparc_lower_64bit(void);
+
 #endif
index d55c6da..d9734a6 100644 (file)
@@ -176,12 +176,12 @@ void sparc_emit_dest_register(const ir_node *node, int pos)
  */
 void sparc_emit_reg_or_imm(const ir_node *node, int pos)
 {
-       if (get_irn_arity(node) > pos) {
-               // we have reg input
-               sparc_emit_source_register(node, pos);
-       } else {
+       if (arch_irn_get_flags(node) & ((arch_irn_flags_t)sparc_arch_irn_flag_immediate_form)) {
                // we have a imm input
                sparc_emit_immediate(node);
+       } else {
+               // we have reg input
+               sparc_emit_source_register(node, pos);
        }
 }
 
diff --git a/ir/be/sparc/sparc_lower64.c b/ir/be/sparc/sparc_lower64.c
new file mode 100644 (file)
index 0000000..0e9e5b0
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 1995-2010 University of Karlsruhe.  All right reserved.
+ *
+ * This file is part of libFirm.
+ *
+ * This file may be distributed and/or modified under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation and appearing in the file LICENSE.GPL included in the
+ * packaging of this file.
+ *
+ * Licensees holding valid libFirm Professional Edition licenses may use
+ * this file in accordance with the libFirm Commercial License.
+ * Agreement provided with the Software.
+ *
+ * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE.
+ */
+
+/**
+ * @file
+ * @brief    Sparc 64bit lowering
+ * @author   Matthias Braun
+ */
+#include "config.h"
+
+#include "bearch_sparc_t.h"
+#include "gen_sparc_new_nodes.h"
+#include "lower_dw.h"
+#include "ircons_t.h"
+#include "util.h"
+
+static void lower64_add(ir_node *node, ir_mode *mode)
+{
+       dbg_info *dbgi       = get_irn_dbg_info(node);
+       ir_node  *block      = get_nodes_block(node);
+       ir_node  *left       = get_Add_left(node);
+       ir_node  *right      = get_Add_right(node);
+       ir_node  *left_low   = get_lowered_low(left);
+       ir_node  *left_high  = get_lowered_high(left);
+       ir_node  *right_low  = get_lowered_low(right);
+       ir_node  *right_high = get_lowered_high(right);
+       ir_node  *addcc      = new_bd_sparc_AddCC_t(dbgi, block, left_low,
+                                                   right_low);
+       ir_node  *res_low    = new_r_Proj(addcc, mode_Iu, pn_sparc_AddCC_t_res);
+       ir_node  *res_flags  = new_r_Proj(addcc, mode_ANY, pn_sparc_AddCC_t_flags);
+       ir_node  *addx       = new_bd_sparc_AddX_t(dbgi, block, left_high,
+                                                  right_high, res_flags, mode);
+       ir_set_dw_lowered(node, res_low, addx);
+}
+
+static void lower64_sub(ir_node *node, ir_mode *mode)
+{
+       dbg_info *dbgi       = get_irn_dbg_info(node);
+       ir_node  *block      = get_nodes_block(node);
+       ir_node  *left       = get_Sub_left(node);
+       ir_node  *right      = get_Sub_right(node);
+       ir_node  *left_low   = get_lowered_low(left);
+       ir_node  *left_high  = get_lowered_high(left);
+       ir_node  *right_low  = get_lowered_low(right);
+       ir_node  *right_high = get_lowered_high(right);
+       ir_node  *subcc      = new_bd_sparc_SubCC_t(dbgi, block, left_low,
+                                                   right_low);
+       ir_node  *res_low    = new_r_Proj(subcc, mode_Iu, pn_sparc_SubCC_t_res);
+       ir_node  *res_flags  = new_r_Proj(subcc, mode_ANY, pn_sparc_SubCC_t_flags);
+       ir_node  *subx       = new_bd_sparc_SubX_t(dbgi, block, left_high,
+                                                  right_high, res_flags, mode);
+       ir_set_dw_lowered(node, res_low, subx);
+}
+
+static void lower64_minus(ir_node *node, ir_mode *mode)
+{
+       dbg_info *dbgi         = get_irn_dbg_info(node);
+       ir_graph *irg          = get_irn_irg(node);
+       ir_node  *block        = get_nodes_block(node);
+       ir_node  *op           = get_Minus_op(node);
+       ir_node  *right_low    = get_lowered_low(op);
+       ir_node  *right_high   = get_lowered_high(op);
+       ir_mode  *low_unsigned = get_irn_mode(right_low);
+       ir_node  *left_low     = new_r_Const(irg, get_mode_null(low_unsigned));
+       ir_node  *left_high    = new_r_Const(irg, get_mode_null(mode));
+       ir_node  *subcc        = new_bd_sparc_SubCC_t(dbgi, block, left_low,
+                                                     right_low);
+       ir_node  *res_low      = new_r_Proj(subcc, mode_Iu, pn_sparc_SubCC_t_res);
+       ir_node  *res_flags    = new_r_Proj(subcc, mode_ANY, pn_sparc_SubCC_t_flags);
+       ir_node  *subx         = new_bd_sparc_SubX_t(dbgi, block, left_high,
+                                                    right_high, res_flags, mode);
+       ir_set_dw_lowered(node, res_low, subx);
+}
+
+static ir_entity *create_64_intrinsic_fkt(ir_type *method, const ir_op *op,
+                                          const ir_mode *imode,
+                                          const ir_mode *omode, void *context)
+{
+       ir_type    *glob = get_glob_type();
+       const char *name;
+       ident      *id;
+       ir_entity  *result;
+       (void) context;
+       (void) omode;
+
+       if (op == op_Mul) {
+               name = "__muldi3";
+       } else if (op == op_Div) {
+               name = mode_is_signed(imode) ? "__divdi3" : "__udivdi3";
+       } else if (op == op_Mod) {
+               name = mode_is_signed(imode) ? "__moddi3" : "__umoddi3";
+       } else {
+               panic("Can't lower unexpected 64bit operation %s", get_op_name(op));
+       }
+       id     = new_id_from_str(name);
+       result = new_entity(glob, id, method);
+       set_entity_ld_ident(result, id);
+       set_entity_visibility(result, ir_visibility_external);
+       return result;
+}
+
+void sparc_lower_64bit(void)
+{
+       lwrdw_param_t lower_dw_params = {
+               0,  /* big endian */
+               64, /* doubleword size */
+               create_64_intrinsic_fkt,
+               NULL
+       };
+
+       /* make sure opcodes are initialized */
+       sparc_create_opcodes(&sparc_irn_ops);
+
+       ir_prepare_dw_lowering(&lower_dw_params);
+       ir_register_dw_lower_function(op_Add,   lower64_add);
+       ir_register_dw_lower_function(op_Minus, lower64_minus);
+       ir_register_dw_lower_function(op_Sub,   lower64_sub);
+       ir_lower_dw_ops();
+}
index 235d258..6a58960 100644 (file)
@@ -93,6 +93,9 @@ static void sparc_dump_node(FILE *F, ir_node *n, dump_reason_t reason)
                break;
 
        case dump_node_info_txt:
+               if (is_sparc_AddX_t(n) || is_sparc_AddCC_t(n))
+                       break;
+
                arch_dump_reqs_and_registers(F, n);
                attr = get_sparc_attr_const(n);
                if (attr->immediate_value_entity) {
@@ -141,6 +144,7 @@ static void sparc_set_attr_imm(ir_node *res, ir_entity *entity,
        sparc_attr_t *attr           = (sparc_attr_t*)get_irn_generic_attr(res);
        attr->immediate_value_entity = entity;
        attr->immediate_value        = immediate_value;
+       arch_irn_add_flags(res, (arch_irn_flags_t)sparc_arch_irn_flag_immediate_form);
 }
 
 static void init_sparc_jmp_cond_attr(ir_node *node, ir_relation relation,
index 02acff2..4a44251 100644 (file)
@@ -45,6 +45,7 @@ enum sparc_arch_irn_flags_t {
        sparc_arch_irn_flag_modifies_flags        = arch_irn_flags_backend << 0,
        sparc_arch_irn_flag_modifies_fp_flags     = arch_irn_flags_backend << 1,
        sparc_arch_irn_flag_needs_64bit_spillslot = arch_irn_flags_backend << 2,
+       sparc_arch_irn_flag_immediate_form        = arch_irn_flags_backend << 3,
 };
 
 /**
index 332c253..a2d4d1b 100644 (file)
@@ -174,6 +174,33 @@ my %binop_operand_constructors = (
        },
 );
 
+my %binopcc_operand_constructors = (
+       imm => {
+               attr       => "ir_entity *immediate_entity, int32_t immediate_value",
+               custominit => "sparc_set_attr_imm(res, immediate_entity, immediate_value);",
+               reg_req    => { in => [ "gp" ], out => [ "gp", "flags" ] },
+               ins        => [ "left" ],
+       },
+       reg => {
+               reg_req    => { in => [ "gp", "gp" ], out => [ "gp", "flags" ] },
+               ins        => [ "left", "right" ],
+       },
+);
+
+my %binopx_operand_constructors = (
+       imm => {
+               attr       => "ir_entity *immediate_entity, int32_t immediate_value",
+               custominit => "sparc_set_attr_imm(res, immediate_entity, immediate_value);",
+               reg_req    => { in => [ "gp", "flags" ], out => [ "gp" ] },
+               ins        => [ "left", "carry" ],
+       },
+       reg => {
+               reg_req    => { in => [ "gp", "gp", "flags" ], out => [ "gp" ] },
+               ins        => [ "left", "right", "carry" ],
+       },
+);
+
+
 my %binopcczero_operand_constructors = (
        imm => {
                attr       => "ir_entity *immediate_entity, int32_t immediate_value",
@@ -237,6 +264,33 @@ Add => {
        constructors => \%binop_operand_constructors,
 },
 
+AddCC => {
+       irn_flags    => [ "rematerializable" ],
+       emit         => '. addcc %S0, %R1I, %D0',
+       outs         => [ "res", "flags" ],
+       constructors => \%binopcc_operand_constructors,
+},
+
+AddX => {
+       # At the moment not rematerializable because of assert in beflags.c/
+       # (it claims that spiller can't rematerialize flag stuff correctly)
+       #irn_flags    => [ "rematerializable" ],
+       emit         => '. addx %S0, %R1I, %D0',
+       constructors => \%binopx_operand_constructors,
+       mode         => $mode_gp,
+},
+
+AddCC_t => {
+       ins       => [ "left", "right" ],
+       outs      => [ "res", "flags" ],
+       attr_type => "",
+},
+
+AddX_t => {
+       ins       => [ "left", "right", "flags_input" ],
+       attr_type => "",
+},
+
 Sub => {
        irn_flags    => [ "rematerializable" ],
        mode         => $mode_gp,
@@ -244,6 +298,31 @@ Sub => {
        constructors => \%binop_operand_constructors,
 },
 
+SubCC => {
+       irn_flags    => [ "rematerializable" ],
+       emit         => '. subcc %S0, %R1I, %D0',
+       outs         => [ "res", "flags" ],
+       constructors => \%binopcc_operand_constructors,
+},
+
+SubX => {
+       # Not rematerializable (see AddX)
+       emit         => '. subx %S0, %R1I, %D0',
+       constructors => \%binopx_operand_constructors,
+       mode         => $mode_gp,
+},
+
+SubCC_t => {
+       ins       => [ "left", "right" ],
+       outs      => [ "res", "flags" ],
+       attr_type => "",
+},
+
+SubX_t => {
+       ins       => [ "left", "right", "flags_input" ],
+       attr_type => "",
+},
+
 # Load / Store
 Ld => {
        op_flags  => [ "labeled", "fragile" ],
index 238d827..b83d466 100644 (file)
@@ -66,6 +66,7 @@ static const arch_register_t *sp_reg = &sparc_registers[REG_SP];
 static const arch_register_t *fp_reg = &sparc_registers[REG_FRAME_POINTER];
 static calling_convention_t  *cconv  = NULL;
 static ir_mode               *mode_gp;
+static ir_mode               *mode_flags;
 static ir_mode               *mode_fp;
 static ir_mode               *mode_fp2;
 //static ir_mode               *mode_fp4;
@@ -336,6 +337,45 @@ static ir_node *gen_helper_unfpop(ir_node *node, ir_mode *mode,
        panic("unsupported mode %+F for float op", mode);
 }
 
+typedef ir_node* (*new_binopx_imm_func)(dbg_info *dbgi, ir_node *block,
+                                        ir_node *op1, ir_node *flags,
+                                        ir_entity *imm_entity, int32_t imm);
+
+typedef ir_node* (*new_binopx_reg_func)(dbg_info *dbgi, ir_node *block,
+                                        ir_node *op1, ir_node *op2,
+                                        ir_node *flags);
+
+static ir_node *gen_helper_binopx(ir_node *node, match_flags_t match_flags,
+                                  new_binopx_reg_func new_binopx_reg,
+                                  new_binopx_imm_func new_binopx_imm)
+{
+       dbg_info *dbgi      = get_irn_dbg_info(node);
+       ir_node  *block     = be_transform_node(get_nodes_block(node));
+       ir_node  *op1       = get_irn_n(node, 0);
+       ir_node  *op2       = get_irn_n(node, 1);
+       ir_node  *flags     = get_irn_n(node, 2);
+       ir_node  *new_flags = be_transform_node(flags);
+       ir_node  *new_op1;
+       ir_node  *new_op2;
+
+       /* only support for mode-neutral implemented so far */
+       assert(match_flags & MATCH_MODE_NEUTRAL);
+
+       if (is_imm_encodeable(op2)) {
+               ir_node *new_op1   = be_transform_node(op1);
+               int32_t  immediate = get_tarval_long(get_Const_tarval(op2));
+               return new_binopx_imm(dbgi, block, new_op1, new_flags, NULL, immediate);
+       }
+       new_op2 = be_transform_node(op2);
+       if ((match_flags & MATCH_COMMUTATIVE) && is_imm_encodeable(op1)) {
+               int32_t immediate = get_tarval_long(get_Const_tarval(op1));
+               return new_binopx_imm(dbgi, block, new_op2, new_flags, NULL, immediate);
+       }
+       new_op1 = be_transform_node(op1);
+       return new_binopx_reg(dbgi, block, new_op1, new_op2, new_flags);
+
+}
+
 static ir_node *get_g0(void)
 {
        return be_prolog_get_reg_value(abihelper, &sparc_registers[REG_G0]);
@@ -446,6 +486,34 @@ static ir_node *gen_Add(ir_node *node)
                                new_bd_sparc_Add_reg, new_bd_sparc_Add_imm);
 }
 
+static ir_node *gen_AddCC_t(ir_node *node)
+{
+       return gen_helper_binop(node, MATCH_COMMUTATIVE | MATCH_MODE_NEUTRAL,
+                               new_bd_sparc_AddCC_reg, new_bd_sparc_AddCC_imm);
+}
+
+static ir_node *gen_Proj_AddCC_t(ir_node *node)
+{
+       long     pn       = get_Proj_proj(node);
+       ir_node *pred     = get_Proj_pred(node);
+       ir_node *new_pred = be_transform_node(pred);
+
+       switch (pn) {
+       case pn_sparc_AddCC_t_res:
+               return new_r_Proj(new_pred, mode_gp, pn_sparc_AddCC_res);
+       case pn_sparc_AddCC_t_flags:
+               return new_r_Proj(new_pred, mode_flags, pn_sparc_AddCC_flags);
+       default:
+               panic("Invalid AddCC_t proj found");
+       }
+}
+
+static ir_node *gen_AddX_t(ir_node *node)
+{
+       return gen_helper_binopx(node, MATCH_COMMUTATIVE | MATCH_MODE_NEUTRAL,
+                                new_bd_sparc_AddX_reg, new_bd_sparc_AddX_imm);
+}
+
 /**
  * Creates an sparc Sub.
  *
@@ -465,6 +533,34 @@ static ir_node *gen_Sub(ir_node *node)
                                new_bd_sparc_Sub_reg, new_bd_sparc_Sub_imm);
 }
 
+static ir_node *gen_SubCC_t(ir_node *node)
+{
+       return gen_helper_binop(node, MATCH_MODE_NEUTRAL,
+                               new_bd_sparc_SubCC_reg, new_bd_sparc_SubCC_imm);
+}
+
+static ir_node *gen_Proj_SubCC_t(ir_node *node)
+{
+       long     pn       = get_Proj_proj(node);
+       ir_node *pred     = get_Proj_pred(node);
+       ir_node *new_pred = be_transform_node(pred);
+
+       switch (pn) {
+       case pn_sparc_SubCC_t_res:
+               return new_r_Proj(new_pred, mode_gp, pn_sparc_SubCC_res);
+       case pn_sparc_SubCC_t_flags:
+               return new_r_Proj(new_pred, mode_flags, pn_sparc_SubCC_flags);
+       default:
+               panic("Invalid SubCC_t proj found");
+       }
+}
+
+static ir_node *gen_SubX_t(ir_node *node)
+{
+       return gen_helper_binopx(node, MATCH_MODE_NEUTRAL,
+                                new_bd_sparc_SubX_reg, new_bd_sparc_SubX_imm);
+}
+
 static ir_node *create_ldf(dbg_info *dbgi, ir_node *block, ir_node *ptr,
                            ir_node *mem, ir_mode *mode, ir_entity *entity,
                            long offset, bool is_frame_entity)
@@ -2012,6 +2108,11 @@ static ir_node *gen_Proj(ir_node *node)
                /* FALLTHROUGH */
        }
        default:
+               if (is_sparc_AddCC_t(pred)) {
+                       return gen_Proj_AddCC_t(node);
+               } else if (is_sparc_SubCC_t(pred)) {
+                       return gen_Proj_SubCC_t(node);
+               }
                panic("code selection didn't expect Proj after %+F\n", pred);
        }
 }
@@ -2064,7 +2165,11 @@ static void sparc_register_transformers(void)
        be_set_transform_function(op_SymConst,     gen_SymConst);
        be_set_transform_function(op_Unknown,      gen_Unknown);
 
+       be_set_transform_function(op_sparc_AddX_t, gen_AddX_t);
+       be_set_transform_function(op_sparc_AddCC_t,gen_AddCC_t);
        be_set_transform_function(op_sparc_Save,   be_duplicate_node);
+       be_set_transform_function(op_sparc_SubX_t, gen_SubX_t);
+       be_set_transform_function(op_sparc_SubCC_t,gen_SubCC_t);
 }
 
 /**
@@ -2079,9 +2184,10 @@ void sparc_transform_graph(ir_graph *irg)
 
        node_to_stack = pmap_create();
 
-       mode_gp  = mode_Iu;
-       mode_fp  = mode_F;
-       mode_fp2 = mode_D;
+       mode_gp    = mode_Iu;
+       mode_fp    = mode_F;
+       mode_fp2   = mode_D;
+       mode_flags = mode_Bu;
        //mode_fp4 = ?
 
        abihelper = be_abihelper_prepare(irg);
index 9966df7..3598075 100644 (file)
@@ -304,6 +304,11 @@ void ir_set_dw_lowered(ir_node *old, ir_node *new_low, ir_node *new_high)
        entry->high_word = new_high;
 }
 
+ir_mode *ir_get_low_unsigned_mode(void)
+{
+       return env->low_unsigned;
+}
+
 /**
  * Translate a Constant: create two.
  */
@@ -532,8 +537,11 @@ static void lower_Div(ir_node *node, ir_mode *mode)
                        set_Proj_pred(proj, call);
                        set_Proj_proj(proj, pn_Call_M);
                        break;
-               case pn_Div_X_except:  /* Execution result if exception occurred. */
-                       /* reroute to the call */
+               case pn_Div_X_regular:
+                       set_Proj_pred(proj, call);
+                       set_Proj_proj(proj, pn_Call_X_regular);
+                       break;
+               case pn_Div_X_except:
                        set_Proj_pred(proj, call);
                        set_Proj_proj(proj, pn_Call_X_except);
                        break;
@@ -604,8 +612,11 @@ static void lower_Mod(ir_node *node, ir_mode *mode)
                        set_Proj_pred(proj, call);
                        set_Proj_proj(proj, pn_Call_M);
                        break;
-               case pn_Mod_X_except:  /* Execution result if exception occurred. */
-                       /* reroute to the call */
+               case pn_Div_X_regular:
+                       set_Proj_pred(proj, call);
+                       set_Proj_proj(proj, pn_Call_X_regular);
+                       break;
+               case pn_Mod_X_except:
                        set_Proj_pred(proj, call);
                        set_Proj_proj(proj, pn_Call_X_except);
                        break;
@@ -975,7 +986,7 @@ static void lower_Shl(ir_node *node, ir_mode *mode)
                ir_node *not_shiftval = new_rd_Not(dbgi, block_true, right,
                                                   low_unsigned);
                ir_node *conv         = create_conv(block_true, left_low, mode);
-               ir_node *one          = new_r_Const(irg, get_mode_one(mode));
+               ir_node *one          = new_r_Const(irg, get_mode_one(low_unsigned));
                ir_node *carry0       = new_rd_Shr(dbgi, block_true, conv, one, mode);
                ir_node *carry1       = new_rd_Shr(dbgi, block_true, carry0,
                                                   not_shiftval, mode);
index e55f3d3..19792bf 100644 (file)
@@ -104,6 +104,12 @@ static inline ir_node *get_lowered_high(ir_node *node)
        return get_node_entry(node)->high_word;
 }
 
+/**
+ * Return the unsigned variant of the lowered mode
+ * Note: you must only call this during a dw_lowering (= in a lowering callback)
+ */
+ir_mode *ir_get_low_unsigned_mode(void);
+
 /**
  * Default implementation. Context is unused.
  */