Lower double word builtins.
authorSebastian Buchwald <Sebastian.Buchwald@kit.edu>
Tue, 4 Oct 2011 19:22:56 +0000 (21:22 +0200)
committerSebastian Buchwald <Sebastian.Buchwald@kit.edu>
Thu, 13 Oct 2011 08:48:36 +0000 (10:48 +0200)
ir/lower/lower_dw.c

index 9d7eaaf..03f5917 100644 (file)
 #include <stdbool.h>
 #include <assert.h>
 
+#include "be.h"
 #include "error.h"
 #include "lowering.h"
 #include "irnode_t.h"
+#include "irnodeset.h"
 #include "irgraph_t.h"
 #include "irmode_t.h"
 #include "iropt_t.h"
@@ -46,6 +48,7 @@
 #include "irgwalk.h"
 #include "ircons.h"
 #include "irflag.h"
+#include "iroptimize.h"
 #include "irtools.h"
 #include "debug.h"
 #include "set.h"
@@ -65,9 +68,15 @@ static set *conv_types;
 /** A map from a method type to its lowered type. */
 static pmap *lowered_type;
 
+/** A map from a builtin type to its lower and higher type. */
+static pmap *lowered_builtin_type_high;
+static pmap *lowered_builtin_type_low;
+
 /** The types for the binop and unop intrinsics. */
 static ir_type *binop_tp_u, *binop_tp_s, *unop_tp_u, *unop_tp_s, *tp_s, *tp_u;
 
+static ir_nodeset_t created_mux_nodes;
+
 /** the debug handle */
 DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
 
@@ -2315,6 +2324,337 @@ static void lower_ASM(ir_node *asmn, ir_mode *mode)
        }
 }
 
+/**
+ * Lower the builtin type to its higher part.
+ *
+ * @param mtp  the builtin type to lower
+ *
+ * @return the lowered type
+ */
+static ir_type *lower_Builtin_type_high(ir_type *mtp)
+{
+       ir_type *res;
+       size_t   i;
+       size_t   n_params;
+       size_t   n_res;
+       bool     must_be_lowered;
+
+       res = (ir_type*)pmap_get(lowered_builtin_type_high, mtp);
+       if (res != NULL)
+               return res;
+
+       n_params        = get_method_n_params(mtp);
+       n_res           = get_method_n_ress(mtp);
+       must_be_lowered = false;
+
+       /* count new number of params */
+       for (i = n_params; i > 0;) {
+               ir_type *tp = get_method_param_type(mtp, --i);
+
+               if (is_Primitive_type(tp)) {
+                       ir_mode *mode = get_type_mode(tp);
+
+                       if (mode == env->high_signed || mode == env->high_unsigned) {
+                               must_be_lowered = true;
+                               break;
+                       }
+               }
+       }
+
+       if (!must_be_lowered) {
+               set_type_link(mtp, NULL);
+               return mtp;
+       }
+
+       res = new_d_type_method(n_params, n_res, get_type_dbg_info(mtp));
+
+       /* set param types and result types */
+       for (i = 0; i < n_params; ++i) {
+               ir_type *tp = get_method_param_type(mtp, i);
+
+               if (is_Primitive_type(tp)) {
+                       ir_mode *mode = get_type_mode(tp);
+
+                       if (mode == env->high_signed) {
+                               if (env->params->little_endian) {
+                                       set_method_param_type(res, i, tp_u);
+                               } else {
+                                       set_method_param_type(res, i, tp_s);
+                               }
+                       } else if (mode == env->high_unsigned) {
+                               set_method_param_type(res, i, tp_u);
+                       } else {
+                               set_method_param_type(res, i, tp);
+                       }
+               } else {
+                       set_method_param_type(res, i, tp);
+               }
+       }
+       for (i = n_res = 0; i < n_res; ++i) {
+               ir_type *tp = get_method_res_type(mtp, i);
+
+               set_method_res_type(res, i, tp);
+       }
+
+       set_method_variadicity(res, get_method_variadicity(mtp));
+       set_method_calling_convention(res, get_method_calling_convention(mtp));
+       set_method_additional_properties(res, get_method_additional_properties(mtp));
+
+       pmap_insert(lowered_builtin_type_high, mtp, res);
+       return res;
+}
+
+/**
+ * Lower the builtin type to its lower part.
+ *
+ * @param mtp  the builtin type to lower
+ *
+ * @return the lowered type
+ */
+static ir_type *lower_Builtin_type_low(ir_type *mtp)
+{
+       ir_type *res;
+       size_t   i;
+       size_t   n_params;
+       size_t   n_ress;
+       bool     must_be_lowered;
+
+       res = (ir_type*)pmap_get(lowered_builtin_type_low, mtp);
+       if (res != NULL)
+               return res;
+
+       n_params        = get_method_n_params(mtp);
+       n_ress          = get_method_n_ress(mtp);
+       must_be_lowered = false;
+
+       /* count new number of params */
+       for (i = n_params; i > 0;) {
+               ir_type *tp = get_method_param_type(mtp, --i);
+
+               if (is_Primitive_type(tp)) {
+                       ir_mode *mode = get_type_mode(tp);
+
+                       if (mode == env->high_signed || mode == env->high_unsigned) {
+                               must_be_lowered = true;
+                               break;
+                       }
+               }
+       }
+
+       /* count new number of results */
+       for (i = n_ress; i > 0;) {
+               ir_type *tp = get_method_res_type(mtp, --i);
+
+               if (is_Primitive_type(tp)) {
+                       ir_mode *mode = get_type_mode(tp);
+
+                       if (mode == env->high_signed || mode == env->high_unsigned) {
+                               must_be_lowered = true;
+                       }
+               }
+       }
+       if (!must_be_lowered) {
+               set_type_link(mtp, NULL);
+               return mtp;
+       }
+
+       res = new_d_type_method(n_params, n_ress, get_type_dbg_info(mtp));
+
+       /* set param types and result types */
+       for (i = 0; i < n_params; ++i) {
+               ir_type *tp = get_method_param_type(mtp, i);
+
+               if (is_Primitive_type(tp)) {
+                       ir_mode *mode = get_type_mode(tp);
+
+                       if (mode == env->high_signed) {
+                               if (env->params->little_endian) {
+                                       set_method_param_type(res, i, tp_s);
+                               } else {
+                                       set_method_param_type(res, i, tp_u);
+                               }
+                       } else if (mode == env->high_unsigned) {
+                               set_method_param_type(res, i, tp_u);
+                       } else {
+                               set_method_param_type(res, i, tp);
+                       }
+               } else {
+                       set_method_param_type(res, i, tp);
+               }
+       }
+       for (i = 0; i < n_ress; ++i) {
+               ir_type *tp = get_method_res_type(mtp, i);
+
+               set_method_res_type(res, i, tp);
+       }
+
+       set_method_variadicity(res, get_method_variadicity(mtp));
+       set_method_calling_convention(res, get_method_calling_convention(mtp));
+       set_method_additional_properties(res, get_method_additional_properties(mtp));
+
+       pmap_insert(lowered_builtin_type_low, mtp, res);
+       return res;
+}
+
+/**
+ * Lower double word builtins.
+ */
+static void lower_Builtin(ir_node *builtin, ir_mode *mode)
+{
+       ir_builtin_kind  kind         = get_Builtin_kind(builtin);
+       ir_node         *operand;
+       ir_mode         *operand_mode;
+
+       switch (kind) {
+       case ir_bk_trap:
+       case ir_bk_debugbreak:
+       case ir_bk_return_address:
+       case ir_bk_frame_address:
+       case ir_bk_prefetch:
+       case ir_bk_bswap:
+       case ir_bk_inport:
+       case ir_bk_outport:
+       case ir_bk_inner_trampoline:
+               /* Nothing to do. */
+               return;
+       case ir_bk_ffs:
+       case ir_bk_clz:
+       case ir_bk_ctz:
+       case ir_bk_popcount:
+       case ir_bk_parity:
+               break;
+       default:
+               panic("unknown builtin");
+       }
+
+       operand      = get_Builtin_param(builtin, 0);
+       operand_mode = get_irn_mode(operand);
+
+       if (operand_mode != env->high_signed && operand_mode != env->high_unsigned)
+               return;
+
+       arch_allow_ifconv_func  allow_ifconv      = be_get_backend_param()->allow_ifconv;
+       int                     arity             = get_irn_arity(builtin);
+       dbg_info               *dbgi              = get_irn_dbg_info(builtin);
+       ir_graph               *irg               = get_irn_irg(builtin);
+       ir_type                *type              = get_Builtin_type(builtin);
+       ir_type                *lowered_type_high = lower_Builtin_type_high(type);
+       ir_type                *lowered_type_low  = lower_Builtin_type_low(type);
+       ir_node                *block             = get_nodes_block(builtin);
+       ir_node                *mem               = get_Builtin_mem(builtin);
+       ir_node                *res;
+
+       assert(is_NoMem(mem));
+       assert(arity == 2);
+
+       switch (kind) {
+       case ir_bk_ffs:
+               {
+                       const lower64_entry_t *entry          = get_node_entry(operand);
+                       ir_node               *in_high[1]     = {entry->high_word};
+                       ir_node               *in_low[1]      = {entry->low_word};
+                       ir_node               *number_of_bits = new_r_Const_long(irg, mode_Is, get_mode_size_bits(env->low_unsigned));
+                       ir_node               *zero_signed    = new_rd_Const(dbgi, irg, get_mode_null(mode_Is));
+                       ir_node               *zero_unsigned  = new_rd_Const(dbgi, irg, get_mode_null(mode_Iu));
+                       ir_node               *cmp_low        = new_rd_Cmp(dbgi, block, entry->low_word, zero_unsigned, ir_relation_equal);
+                       ir_node               *cmp_high       = new_rd_Cmp(dbgi, block, entry->high_word, zero_unsigned, ir_relation_equal);
+                       ir_node               *ffs_high       = new_rd_Builtin(dbgi, block, mem, 1, in_high, kind, lowered_type_high);
+                       ir_node               *high_proj      = new_r_Proj(ffs_high, mode_Is, pn_Builtin_1_result);
+                       ir_node               *high           = new_rd_Add(dbgi, block, high_proj, number_of_bits, mode_Is);
+                       ir_node               *ffs_low        = new_rd_Builtin(dbgi, block, mem, 1, in_low, kind, lowered_type_low);
+                       ir_node               *low            = new_r_Proj(ffs_low, mode_Is, pn_Builtin_1_result);
+                       ir_node               *mux_high       = new_rd_Mux(dbgi, block, cmp_high, high, zero_signed, mode_Is);
+
+                       if (! allow_ifconv(cmp_high, high, zero_signed))
+                               ir_nodeset_insert(&created_mux_nodes, mux_high);
+
+                       res = new_rd_Mux(dbgi, block, cmp_low, low, mux_high, mode_Is);
+
+                       if (! allow_ifconv(cmp_low, low, mux_high))
+                               ir_nodeset_insert(&created_mux_nodes, res);
+               }
+               break;
+       case ir_bk_clz:
+               {
+                       const lower64_entry_t *entry          = get_node_entry(operand);
+                       ir_node               *in_high[1]     = {entry->high_word};
+                       ir_node               *in_low[1]      = {entry->low_word};
+                       ir_node               *number_of_bits = new_r_Const_long(irg, mode_Is, get_mode_size_bits(mode));
+                       ir_node               *zero_unsigned  = new_rd_Const(dbgi, irg, get_mode_null(mode_Iu));
+                       ir_node               *cmp_high       = new_rd_Cmp(dbgi, block, entry->high_word, zero_unsigned, ir_relation_equal);
+                       ir_node               *clz_high       = new_rd_Builtin(dbgi, block, mem, 1, in_high, kind, lowered_type_high);
+                       ir_node               *high           = new_r_Proj(clz_high, mode_Is, pn_Builtin_1_result);
+                       ir_node               *clz_low        = new_rd_Builtin(dbgi, block, mem, 1, in_low, kind, lowered_type_low);
+                       ir_node               *low_proj       = new_r_Proj(clz_low, mode_Is, pn_Builtin_1_result);
+                       ir_node               *low            = new_rd_Add(dbgi, block, low_proj, number_of_bits, mode_Is);
+
+                       res = new_rd_Mux(dbgi, block, cmp_high, high, low, mode_Is);
+
+                       if (! allow_ifconv(cmp_high, high, low))
+                               ir_nodeset_insert(&created_mux_nodes, res);
+               }
+               break;
+       case ir_bk_ctz:
+               {
+                       const lower64_entry_t *entry          = get_node_entry(operand);
+                       ir_node               *in_high[1]     = {entry->high_word};
+                       ir_node               *in_low[1]      = {entry->low_word};
+                       ir_node               *number_of_bits = new_r_Const_long(irg, mode_Is, get_mode_size_bits(env->low_unsigned));
+                       ir_node               *zero_unsigned  = new_rd_Const(dbgi, irg, get_mode_null(mode_Iu));
+                       ir_node               *cmp_low        = new_rd_Cmp(dbgi, block, entry->low_word, zero_unsigned, ir_relation_equal);
+                       ir_node               *ffs_high       = new_rd_Builtin(dbgi, block, mem, 1, in_high, kind, lowered_type_high);
+                       ir_node               *high_proj      = new_r_Proj(ffs_high, mode_Is, pn_Builtin_1_result);
+                       ir_node               *high           = new_rd_Add(dbgi, block, high_proj, number_of_bits, mode_Is);
+                       ir_node               *ffs_low        = new_rd_Builtin(dbgi, block, mem, 1, in_low, kind, lowered_type_low);
+                       ir_node               *low            = new_r_Proj(ffs_low, mode_Is, pn_Builtin_1_result);
+
+                       res = new_rd_Mux(dbgi, block, cmp_low, low, high, mode_Is);
+
+                       if (! allow_ifconv(cmp_low, low, high))
+                               ir_nodeset_insert(&created_mux_nodes, res);
+               }
+               break;
+       case ir_bk_popcount:
+               {
+                       const lower64_entry_t *entry         = get_node_entry(operand);
+                       ir_node               *in_high[1]    = {entry->high_word};
+                       ir_node               *in_low[1]     = {entry->low_word};
+                       ir_node               *popcount_high = new_rd_Builtin(dbgi, block, mem, 1, in_high, kind, lowered_type_high);
+                       ir_node               *popcount_low  = new_rd_Builtin(dbgi, block, mem, 1, in_low, kind, lowered_type_low);
+                       ir_node               *high          = new_r_Proj(popcount_high, mode_Is, pn_Builtin_1_result);
+                       ir_node               *low           = new_r_Proj(popcount_low, mode_Is, pn_Builtin_1_result);
+
+                       res = new_rd_Add(dbgi, block, high, low, mode_Is);
+               }
+               break;
+       case ir_bk_parity:
+               {
+                       const lower64_entry_t *entry = get_node_entry(operand);
+                       ir_node *in_high[1] = {entry->high_word};
+                       ir_node *in_low[1] = {entry->low_word};
+                       ir_node  *parity_high;
+                       ir_node  *parity_low;
+                       ir_node  *high;
+                       ir_node  *low;
+
+                       assert(arity == 2);
+
+                       parity_high = new_rd_Builtin(dbgi, block, mem, 1, in_high, kind, lowered_type_high);
+                       high        = new_r_Proj(parity_high, mode_Is, pn_Builtin_1_result);
+                       parity_low  = new_rd_Builtin(dbgi, block, mem, 1, in_low, kind, lowered_type_low);
+                       low         = new_r_Proj(parity_low, mode_Is, pn_Builtin_1_result);
+                       res         = new_rd_Eor(dbgi, block, high, low, mode_Is);
+               }
+               break;
+       default:
+               panic("unexpected builtin");
+       }
+
+       turn_into_tuple(builtin, 2);
+       set_irn_n(builtin, pn_Builtin_M, mem);
+       set_irn_n(builtin, pn_Builtin_1_result, res);
+}
+
 /**
  * check for opcodes that must always be lowered.
  */
@@ -2322,6 +2662,7 @@ static bool always_lower(unsigned code)
 {
        switch (code) {
        case iro_ASM:
+       case iro_Builtin:
        case iro_Proj:
        case iro_Start:
        case iro_Call:
@@ -2611,6 +2952,7 @@ void ir_prepare_dw_lowering(const lwrdw_param_t *new_param)
        ir_register_dw_lower_function(op_Add,     lower_binop);
        ir_register_dw_lower_function(op_And,     lower_And);
        ir_register_dw_lower_function(op_Bad,     lower_Bad);
+       ir_register_dw_lower_function(op_Builtin, lower_Builtin);
        ir_register_dw_lower_function(op_Call,    lower_Call);
        ir_register_dw_lower_function(op_Cmp,     lower_Cmp);
        ir_register_dw_lower_function(op_Cond,    lower_Cond);
@@ -2635,6 +2977,14 @@ void ir_prepare_dw_lowering(const lwrdw_param_t *new_param)
        ir_register_dw_lower_function(op_Unknown, lower_Unknown);
 }
 
+/**
+ * Callback to lower only the Mux nodes we created.
+ */
+static int lower_mux_cb(ir_node *mux)
+{
+       return ir_nodeset_contains(&created_mux_nodes, mux);
+}
+
 /*
  * Do the lowering.
  */
@@ -2656,6 +3006,10 @@ void ir_lower_dw_ops(void)
                conv_types = new_set(cmp_conv_tp, 16);
        if (! lowered_type)
                lowered_type = pmap_create();
+       if (! lowered_builtin_type_low)
+               lowered_builtin_type_low = pmap_create();
+       if (! lowered_builtin_type_high)
+               lowered_builtin_type_high = pmap_create();
 
        /* create a primitive unsigned and signed type */
        if (! tp_u)
@@ -2723,7 +3077,15 @@ void ir_lower_dw_ops(void)
        /* transform all graphs */
        for (i = 0, n = get_irp_n_irgs(); i < n; ++i) {
                ir_graph *irg = get_irp_irg(i);
+
+               ir_nodeset_init(&created_mux_nodes);
+
                lower_irg(irg);
+
+               if (ir_nodeset_size(&created_mux_nodes) > 0)
+                       lower_mux(irg, lower_mux_cb);
+
+               ir_nodeset_destroy(&created_mux_nodes);
        }
        irp_free_resources(irp, IRP_RESOURCE_TYPE_LINK);
        del_pdeq(lenv.waitq);