Home | History | Annotate | Line # | Download | only in gcc
gimple-isel.cc revision 1.1.1.1
      1  1.1  mrg /* Schedule GIMPLE vector statements.
      2  1.1  mrg    Copyright (C) 2020-2022 Free Software Foundation, Inc.
      3  1.1  mrg 
      4  1.1  mrg This file is part of GCC.
      5  1.1  mrg 
      6  1.1  mrg GCC is free software; you can redistribute it and/or modify it
      7  1.1  mrg under the terms of the GNU General Public License as published by the
      8  1.1  mrg Free Software Foundation; either version 3, or (at your option) any
      9  1.1  mrg later version.
     10  1.1  mrg 
     11  1.1  mrg GCC is distributed in the hope that it will be useful, but WITHOUT
     12  1.1  mrg ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13  1.1  mrg FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14  1.1  mrg for more details.
     15  1.1  mrg 
     16  1.1  mrg You should have received a copy of the GNU General Public License
     17  1.1  mrg along with GCC; see the file COPYING3.  If not see
     18  1.1  mrg <http://www.gnu.org/licenses/>.  */
     19  1.1  mrg 
     20  1.1  mrg #include "config.h"
     21  1.1  mrg #include "system.h"
     22  1.1  mrg #include "coretypes.h"
     23  1.1  mrg #include "backend.h"
     24  1.1  mrg #include "rtl.h"
     25  1.1  mrg #include "tree.h"
     26  1.1  mrg #include "gimple.h"
     27  1.1  mrg #include "tree-pass.h"
     28  1.1  mrg #include "ssa.h"
     29  1.1  mrg #include "expmed.h"
     30  1.1  mrg #include "optabs-tree.h"
     31  1.1  mrg #include "tree-eh.h"
     32  1.1  mrg #include "gimple-iterator.h"
     33  1.1  mrg #include "gimplify-me.h"
     34  1.1  mrg #include "gimplify.h"
     35  1.1  mrg #include "tree-cfg.h"
     36  1.1  mrg #include "bitmap.h"
     37  1.1  mrg #include "tree-ssa-dce.h"
     38  1.1  mrg #include "memmodel.h"
     39  1.1  mrg #include "optabs.h"
     40  1.1  mrg #include "gimple-fold.h"
     41  1.1  mrg #include "internal-fn.h"
     42  1.1  mrg 
     43  1.1  mrg /* Expand all ARRAY_REF(VIEW_CONVERT_EXPR) gimple assignments into calls to
     44  1.1  mrg    internal function based on vector type of selected expansion.
     45  1.1  mrg    i.e.:
     46  1.1  mrg      VIEW_CONVERT_EXPR<int[4]>(u)[_1] = i_4(D);
     47  1.1  mrg    =>
     48  1.1  mrg      _7 = u;
     49  1.1  mrg      _8 = .VEC_SET (_7, i_4(D), _1);
     50  1.1  mrg      u = _8;  */
     51  1.1  mrg 
     52  1.1  mrg static bool
     53  1.1  mrg gimple_expand_vec_set_expr (struct function *fun, gimple_stmt_iterator *gsi)
     54  1.1  mrg {
     55  1.1  mrg   enum tree_code code;
     56  1.1  mrg   gcall *new_stmt = NULL;
     57  1.1  mrg   gassign *ass_stmt = NULL;
     58  1.1  mrg   bool cfg_changed = false;
     59  1.1  mrg 
     60  1.1  mrg   /* Only consider code == GIMPLE_ASSIGN.  */
     61  1.1  mrg   gassign *stmt = dyn_cast<gassign *> (gsi_stmt (*gsi));
     62  1.1  mrg   if (!stmt)
     63  1.1  mrg     return false;
     64  1.1  mrg 
     65  1.1  mrg   tree lhs = gimple_assign_lhs (stmt);
     66  1.1  mrg   code = TREE_CODE (lhs);
     67  1.1  mrg   if (code != ARRAY_REF)
     68  1.1  mrg     return false;
     69  1.1  mrg 
     70  1.1  mrg   tree val = gimple_assign_rhs1 (stmt);
     71  1.1  mrg   tree op0 = TREE_OPERAND (lhs, 0);
     72  1.1  mrg   if (TREE_CODE (op0) == VIEW_CONVERT_EXPR && DECL_P (TREE_OPERAND (op0, 0))
     73  1.1  mrg       && VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (op0, 0)))
     74  1.1  mrg       && TYPE_MODE (TREE_TYPE (lhs))
     75  1.1  mrg 	   == TYPE_MODE (TREE_TYPE (TREE_TYPE (TREE_OPERAND (op0, 0)))))
     76  1.1  mrg     {
     77  1.1  mrg       tree pos = TREE_OPERAND (lhs, 1);
     78  1.1  mrg       tree view_op0 = TREE_OPERAND (op0, 0);
     79  1.1  mrg       machine_mode outermode = TYPE_MODE (TREE_TYPE (view_op0));
     80  1.1  mrg       if (auto_var_in_fn_p (view_op0, fun->decl)
     81  1.1  mrg 	  && !TREE_ADDRESSABLE (view_op0) && can_vec_set_var_idx_p (outermode))
     82  1.1  mrg 	{
     83  1.1  mrg 	  location_t loc = gimple_location (stmt);
     84  1.1  mrg 	  tree var_src = make_ssa_name (TREE_TYPE (view_op0));
     85  1.1  mrg 	  tree var_dst = make_ssa_name (TREE_TYPE (view_op0));
     86  1.1  mrg 
     87  1.1  mrg 	  ass_stmt = gimple_build_assign (var_src, view_op0);
     88  1.1  mrg 	  gimple_set_vuse (ass_stmt, gimple_vuse (stmt));
     89  1.1  mrg 	  gimple_set_location (ass_stmt, loc);
     90  1.1  mrg 	  gsi_insert_before (gsi, ass_stmt, GSI_SAME_STMT);
     91  1.1  mrg 
     92  1.1  mrg 	  new_stmt
     93  1.1  mrg 	    = gimple_build_call_internal (IFN_VEC_SET, 3, var_src, val, pos);
     94  1.1  mrg 	  gimple_call_set_lhs (new_stmt, var_dst);
     95  1.1  mrg 	  gimple_set_location (new_stmt, loc);
     96  1.1  mrg 	  gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
     97  1.1  mrg 
     98  1.1  mrg 	  ass_stmt = gimple_build_assign (view_op0, var_dst);
     99  1.1  mrg 	  gimple_set_location (ass_stmt, loc);
    100  1.1  mrg 	  gsi_insert_before (gsi, ass_stmt, GSI_SAME_STMT);
    101  1.1  mrg 
    102  1.1  mrg 	  basic_block bb = gimple_bb (stmt);
    103  1.1  mrg 	  gimple_move_vops (ass_stmt, stmt);
    104  1.1  mrg 	  if (gsi_remove (gsi, true)
    105  1.1  mrg 	      && gimple_purge_dead_eh_edges (bb))
    106  1.1  mrg 	    cfg_changed = true;
    107  1.1  mrg 	  *gsi = gsi_for_stmt (ass_stmt);
    108  1.1  mrg 	}
    109  1.1  mrg     }
    110  1.1  mrg 
    111  1.1  mrg   return cfg_changed;
    112  1.1  mrg }
    113  1.1  mrg 
    114  1.1  mrg /* Expand all VEC_COND_EXPR gimple assignments into calls to internal
    115  1.1  mrg    function based on type of selected expansion.  */
    116  1.1  mrg 
    117  1.1  mrg static gimple *
    118  1.1  mrg gimple_expand_vec_cond_expr (struct function *fun, gimple_stmt_iterator *gsi,
    119  1.1  mrg 			     hash_map<tree, unsigned int> *vec_cond_ssa_name_uses)
    120  1.1  mrg {
    121  1.1  mrg   tree lhs, op0a = NULL_TREE, op0b = NULL_TREE;
    122  1.1  mrg   enum tree_code code;
    123  1.1  mrg   enum tree_code tcode;
    124  1.1  mrg   machine_mode cmp_op_mode;
    125  1.1  mrg   bool unsignedp;
    126  1.1  mrg   enum insn_code icode;
    127  1.1  mrg   imm_use_iterator imm_iter;
    128  1.1  mrg 
    129  1.1  mrg   /* Only consider code == GIMPLE_ASSIGN.  */
    130  1.1  mrg   gassign *stmt = dyn_cast<gassign *> (gsi_stmt (*gsi));
    131  1.1  mrg   if (!stmt)
    132  1.1  mrg     return NULL;
    133  1.1  mrg 
    134  1.1  mrg   code = gimple_assign_rhs_code (stmt);
    135  1.1  mrg   if (code != VEC_COND_EXPR)
    136  1.1  mrg     return NULL;
    137  1.1  mrg 
    138  1.1  mrg   tree op0 = gimple_assign_rhs1 (stmt);
    139  1.1  mrg   tree op1 = gimple_assign_rhs2 (stmt);
    140  1.1  mrg   tree op2 = gimple_assign_rhs3 (stmt);
    141  1.1  mrg   lhs = gimple_assign_lhs (stmt);
    142  1.1  mrg   machine_mode mode = TYPE_MODE (TREE_TYPE (lhs));
    143  1.1  mrg 
    144  1.1  mrg   /* Lower mask typed, non-vector mode VEC_COND_EXPRs to bitwise operations.
    145  1.1  mrg      Those can end up generated by folding and at least for integer mode masks
    146  1.1  mrg      we cannot expect vcond expanders to exist.  We lower a ? b : c
    147  1.1  mrg      to (b & a) | (c & ~a).  */
    148  1.1  mrg   if (VECTOR_BOOLEAN_TYPE_P (TREE_TYPE (lhs))
    149  1.1  mrg       && !VECTOR_MODE_P (mode))
    150  1.1  mrg     {
    151  1.1  mrg       gcc_assert (types_compatible_p (TREE_TYPE (op0), TREE_TYPE (op1)));
    152  1.1  mrg       gimple_seq stmts = NULL;
    153  1.1  mrg       tree type = TREE_TYPE (lhs);
    154  1.1  mrg       location_t loc = gimple_location (stmt);
    155  1.1  mrg       tree tem0 = gimple_build (&stmts, loc, BIT_AND_EXPR, type, op1, op0);
    156  1.1  mrg       tree tem1 = gimple_build (&stmts, loc, BIT_NOT_EXPR, type, op0);
    157  1.1  mrg       tree tem2 = gimple_build (&stmts, loc, BIT_AND_EXPR, type, op2, tem1);
    158  1.1  mrg       tree tem3 = gimple_build (&stmts, loc, BIT_IOR_EXPR, type, tem0, tem2);
    159  1.1  mrg       gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
    160  1.1  mrg       return gimple_build_assign (lhs, tem3);
    161  1.1  mrg     }
    162  1.1  mrg 
    163  1.1  mrg   bool can_compute_op0 = true;
    164  1.1  mrg   gcc_assert (!COMPARISON_CLASS_P (op0));
    165  1.1  mrg   if (TREE_CODE (op0) == SSA_NAME)
    166  1.1  mrg     {
    167  1.1  mrg       unsigned int used_vec_cond_exprs = 0;
    168  1.1  mrg       unsigned int *slot = vec_cond_ssa_name_uses->get (op0);
    169  1.1  mrg       if (slot)
    170  1.1  mrg 	used_vec_cond_exprs = *slot;
    171  1.1  mrg       else
    172  1.1  mrg 	{
    173  1.1  mrg 	  gimple *use_stmt;
    174  1.1  mrg 	  FOR_EACH_IMM_USE_STMT (use_stmt, imm_iter, op0)
    175  1.1  mrg 	    {
    176  1.1  mrg 	      gassign *assign = dyn_cast<gassign *> (use_stmt);
    177  1.1  mrg 	      if (assign != NULL
    178  1.1  mrg 		  && gimple_assign_rhs_code (assign) == VEC_COND_EXPR
    179  1.1  mrg 		  && gimple_assign_rhs1 (assign) == op0)
    180  1.1  mrg 		used_vec_cond_exprs++;
    181  1.1  mrg 	    }
    182  1.1  mrg 	  vec_cond_ssa_name_uses->put (op0, used_vec_cond_exprs);
    183  1.1  mrg 	}
    184  1.1  mrg 
    185  1.1  mrg       gassign *def_stmt = dyn_cast<gassign *> (SSA_NAME_DEF_STMT (op0));
    186  1.1  mrg       if (def_stmt)
    187  1.1  mrg 	{
    188  1.1  mrg 	  tcode = gimple_assign_rhs_code (def_stmt);
    189  1.1  mrg 	  op0a = gimple_assign_rhs1 (def_stmt);
    190  1.1  mrg 	  op0b = gimple_assign_rhs2 (def_stmt);
    191  1.1  mrg 
    192  1.1  mrg 	  tree op0_type = TREE_TYPE (op0);
    193  1.1  mrg 	  tree op0a_type = TREE_TYPE (op0a);
    194  1.1  mrg 	  if (TREE_CODE_CLASS (tcode) == tcc_comparison)
    195  1.1  mrg 	    can_compute_op0 = expand_vec_cmp_expr_p (op0a_type, op0_type,
    196  1.1  mrg 						     tcode);
    197  1.1  mrg 
    198  1.1  mrg 	  /* Try to fold x CMP y ? -1 : 0 to x CMP y.  */
    199  1.1  mrg 	  if (can_compute_op0
    200  1.1  mrg 	      && integer_minus_onep (op1)
    201  1.1  mrg 	      && integer_zerop (op2)
    202  1.1  mrg 	      && TYPE_MODE (TREE_TYPE (lhs)) == TYPE_MODE (TREE_TYPE (op0)))
    203  1.1  mrg 	    {
    204  1.1  mrg 	      tree conv_op = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (lhs), op0);
    205  1.1  mrg 	      gassign *new_stmt = gimple_build_assign (lhs, conv_op);
    206  1.1  mrg 	      gsi_replace (gsi, new_stmt, true);
    207  1.1  mrg 	      return new_stmt;
    208  1.1  mrg 	    }
    209  1.1  mrg 
    210  1.1  mrg 	  /* When the compare has EH we do not want to forward it when
    211  1.1  mrg 	     it has multiple uses and in general because of the complication
    212  1.1  mrg 	     with EH redirection.  */
    213  1.1  mrg 	  if (stmt_can_throw_internal (fun, def_stmt))
    214  1.1  mrg 	    tcode = TREE_CODE (op0);
    215  1.1  mrg 
    216  1.1  mrg 	  /* If we can compute op0 and have multiple uses, keep the SSA
    217  1.1  mrg 	     name and use vcond_mask.  */
    218  1.1  mrg 	  else if (can_compute_op0
    219  1.1  mrg 		   && used_vec_cond_exprs >= 2
    220  1.1  mrg 		   && (get_vcond_mask_icode (mode, TYPE_MODE (op0_type))
    221  1.1  mrg 		       != CODE_FOR_nothing))
    222  1.1  mrg 	    tcode = TREE_CODE (op0);
    223  1.1  mrg 	}
    224  1.1  mrg       else
    225  1.1  mrg 	tcode = TREE_CODE (op0);
    226  1.1  mrg     }
    227  1.1  mrg   else
    228  1.1  mrg     tcode = TREE_CODE (op0);
    229  1.1  mrg 
    230  1.1  mrg   if (TREE_CODE_CLASS (tcode) != tcc_comparison)
    231  1.1  mrg     {
    232  1.1  mrg       gcc_assert (VECTOR_BOOLEAN_TYPE_P (TREE_TYPE (op0)));
    233  1.1  mrg       if (get_vcond_mask_icode (mode, TYPE_MODE (TREE_TYPE (op0)))
    234  1.1  mrg 	  != CODE_FOR_nothing)
    235  1.1  mrg 	return gimple_build_call_internal (IFN_VCOND_MASK, 3, op0, op1, op2);
    236  1.1  mrg       /* Fake op0 < 0.  */
    237  1.1  mrg       else
    238  1.1  mrg 	{
    239  1.1  mrg 	  gcc_assert (GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (op0)))
    240  1.1  mrg 		      == MODE_VECTOR_INT);
    241  1.1  mrg 	  op0a = op0;
    242  1.1  mrg 	  op0b = build_zero_cst (TREE_TYPE (op0));
    243  1.1  mrg 	  tcode = LT_EXPR;
    244  1.1  mrg 	}
    245  1.1  mrg     }
    246  1.1  mrg   cmp_op_mode = TYPE_MODE (TREE_TYPE (op0a));
    247  1.1  mrg   unsignedp = TYPE_UNSIGNED (TREE_TYPE (op0a));
    248  1.1  mrg 
    249  1.1  mrg   gcc_assert (known_eq (GET_MODE_NUNITS (mode),
    250  1.1  mrg 			GET_MODE_NUNITS (cmp_op_mode)));
    251  1.1  mrg 
    252  1.1  mrg   icode = get_vcond_icode (mode, cmp_op_mode, unsignedp);
    253  1.1  mrg   /* Some targets do not have vcondeq and only vcond with NE/EQ
    254  1.1  mrg      but not vcondu, so make sure to also try vcond here as
    255  1.1  mrg      vcond_icode_p would canonicalize the optab query to.  */
    256  1.1  mrg   if (icode == CODE_FOR_nothing
    257  1.1  mrg       && (tcode == NE_EXPR || tcode == EQ_EXPR)
    258  1.1  mrg       && ((icode = get_vcond_icode (mode, cmp_op_mode, !unsignedp))
    259  1.1  mrg 	  != CODE_FOR_nothing))
    260  1.1  mrg     unsignedp = !unsignedp;
    261  1.1  mrg   if (icode == CODE_FOR_nothing)
    262  1.1  mrg     {
    263  1.1  mrg       if (tcode == LT_EXPR
    264  1.1  mrg 	  && op0a == op0)
    265  1.1  mrg 	{
    266  1.1  mrg 	  /* A VEC_COND_EXPR condition could be folded from EQ_EXPR/NE_EXPR
    267  1.1  mrg 	     into a constant when only get_vcond_eq_icode is supported.
    268  1.1  mrg 	     Try changing it to NE_EXPR.  */
    269  1.1  mrg 	  tcode = NE_EXPR;
    270  1.1  mrg 	}
    271  1.1  mrg       if ((tcode == EQ_EXPR || tcode == NE_EXPR)
    272  1.1  mrg 	  && direct_internal_fn_supported_p (IFN_VCONDEQ, TREE_TYPE (lhs),
    273  1.1  mrg 					     TREE_TYPE (op0a),
    274  1.1  mrg 					     OPTIMIZE_FOR_BOTH))
    275  1.1  mrg 	{
    276  1.1  mrg 	  tree tcode_tree = build_int_cst (integer_type_node, tcode);
    277  1.1  mrg 	  return gimple_build_call_internal (IFN_VCONDEQ, 5, op0a, op0b, op1,
    278  1.1  mrg 					     op2, tcode_tree);
    279  1.1  mrg 	}
    280  1.1  mrg 
    281  1.1  mrg       gcc_assert (VECTOR_BOOLEAN_TYPE_P (TREE_TYPE (op0))
    282  1.1  mrg 		  && can_compute_op0
    283  1.1  mrg 		  && (get_vcond_mask_icode (mode, TYPE_MODE (TREE_TYPE (op0)))
    284  1.1  mrg 		      != CODE_FOR_nothing));
    285  1.1  mrg       return gimple_build_call_internal (IFN_VCOND_MASK, 3, op0, op1, op2);
    286  1.1  mrg     }
    287  1.1  mrg 
    288  1.1  mrg   tree tcode_tree = build_int_cst (integer_type_node, tcode);
    289  1.1  mrg   return gimple_build_call_internal (unsignedp ? IFN_VCONDU : IFN_VCOND,
    290  1.1  mrg 				     5, op0a, op0b, op1, op2, tcode_tree);
    291  1.1  mrg }
    292  1.1  mrg 
    293  1.1  mrg 
    294  1.1  mrg 
    295  1.1  mrg /* Iterate all gimple statements and try to expand
    296  1.1  mrg    VEC_COND_EXPR assignments.  */
    297  1.1  mrg 
    298  1.1  mrg static unsigned int
    299  1.1  mrg gimple_expand_vec_exprs (struct function *fun)
    300  1.1  mrg {
    301  1.1  mrg   gimple_stmt_iterator gsi;
    302  1.1  mrg   basic_block bb;
    303  1.1  mrg   hash_map<tree, unsigned int> vec_cond_ssa_name_uses;
    304  1.1  mrg   auto_bitmap dce_ssa_names;
    305  1.1  mrg   bool cfg_changed = false;
    306  1.1  mrg 
    307  1.1  mrg   FOR_EACH_BB_FN (bb, fun)
    308  1.1  mrg     {
    309  1.1  mrg       for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
    310  1.1  mrg 	{
    311  1.1  mrg 	  gimple *g = gimple_expand_vec_cond_expr (fun, &gsi,
    312  1.1  mrg 						   &vec_cond_ssa_name_uses);
    313  1.1  mrg 	  if (g != NULL)
    314  1.1  mrg 	    {
    315  1.1  mrg 	      tree lhs = gimple_assign_lhs (gsi_stmt (gsi));
    316  1.1  mrg 	      gimple_set_lhs (g, lhs);
    317  1.1  mrg 	      gsi_replace (&gsi, g, false);
    318  1.1  mrg 	    }
    319  1.1  mrg 
    320  1.1  mrg 	  cfg_changed |= gimple_expand_vec_set_expr (fun, &gsi);
    321  1.1  mrg 	  if (gsi_end_p (gsi))
    322  1.1  mrg 	    break;
    323  1.1  mrg 	}
    324  1.1  mrg     }
    325  1.1  mrg 
    326  1.1  mrg   for (hash_map<tree, unsigned int>::iterator it = vec_cond_ssa_name_uses.begin ();
    327  1.1  mrg        it != vec_cond_ssa_name_uses.end (); ++it)
    328  1.1  mrg     bitmap_set_bit (dce_ssa_names, SSA_NAME_VERSION ((*it).first));
    329  1.1  mrg 
    330  1.1  mrg   simple_dce_from_worklist (dce_ssa_names);
    331  1.1  mrg 
    332  1.1  mrg   return cfg_changed ? TODO_cleanup_cfg : 0;
    333  1.1  mrg }
    334  1.1  mrg 
    335  1.1  mrg namespace {
    336  1.1  mrg 
    337  1.1  mrg const pass_data pass_data_gimple_isel =
    338  1.1  mrg {
    339  1.1  mrg   GIMPLE_PASS, /* type */
    340  1.1  mrg   "isel", /* name */
    341  1.1  mrg   OPTGROUP_VEC, /* optinfo_flags */
    342  1.1  mrg   TV_NONE, /* tv_id */
    343  1.1  mrg   PROP_cfg, /* properties_required */
    344  1.1  mrg   0, /* properties_provided */
    345  1.1  mrg   0, /* properties_destroyed */
    346  1.1  mrg   0, /* todo_flags_start */
    347  1.1  mrg   TODO_update_ssa, /* todo_flags_finish */
    348  1.1  mrg };
    349  1.1  mrg 
    350  1.1  mrg class pass_gimple_isel : public gimple_opt_pass
    351  1.1  mrg {
    352  1.1  mrg public:
    353  1.1  mrg   pass_gimple_isel (gcc::context *ctxt)
    354  1.1  mrg     : gimple_opt_pass (pass_data_gimple_isel, ctxt)
    355  1.1  mrg   {}
    356  1.1  mrg 
    357  1.1  mrg   /* opt_pass methods: */
    358  1.1  mrg   virtual bool gate (function *)
    359  1.1  mrg     {
    360  1.1  mrg       return true;
    361  1.1  mrg     }
    362  1.1  mrg 
    363  1.1  mrg   virtual unsigned int execute (function *fun)
    364  1.1  mrg     {
    365  1.1  mrg       return gimple_expand_vec_exprs (fun);
    366  1.1  mrg     }
    367  1.1  mrg 
    368  1.1  mrg }; // class pass_gimple_isel
    369  1.1  mrg 
    370  1.1  mrg } // anon namespace
    371  1.1  mrg 
    372  1.1  mrg gimple_opt_pass *
    373  1.1  mrg make_pass_gimple_isel (gcc::context *ctxt)
    374  1.1  mrg {
    375  1.1  mrg   return new pass_gimple_isel (ctxt);
    376  1.1  mrg }
    377  1.1  mrg 
    378