The padding bits of _BitInt(N) are undefined in the x86-64 and
the aarch64 ABI. In general, the current lowering and expand process
perform truncations when taking a _BitInt value as input,
while leaving them as-is in the output.

By adding truncation on the output side, we can define psABIs
(e.g. what we are planning for LoongArch) that has _BitInts extended.
Also, TARGET_PROMOTE_FUNCTION_MODE and PROMOTE_MODE are also expected
to be working for small _BitInt types so that they can behave just
like the traditional integral types of C when passed around in
registers.

gcc/c-family/ChangeLog:

        * c-common.cc (resolve_overloaded_atomic_exchange): Truncate
        _BitInt values before atomic store.
        (resolve_overloaded_atomic_compare_exchange): Same.

gcc/ChangeLog:

        * explow.cc (promote_function_mode): Allow _BitInt types
        to be promoted.
        (promote_mode): Same.
        * expr.cc (expand_expr_real_1): Do not truncate _BitInts
        from ABI bondaries if the target sets the "extended" flag.
        (EXTEND_BITINT): Same.
        * gimple-lower-bitint.cc (struct bitint_large_huge):
        Access the highest-order limb of a large/huge _BitInt using limb
        type rather than a new type with reduced precision if _BitInt(N)
        is extended by definition.
        (bitint_large_huge::limb_access_type): Same.
        (bitint_large_huge::handle_plus_minus_1): Truncate after
        operation if needed.
        (bitint_large_huge::handle_lshift): Same.
        (bitint_large_huge::handle_cast): Same.
        (bitint_large_huge::handle_load): Same.
        (bitint_large_huge::handle_stmt): Same.
        (bitint_large_huge::lower_mergeable_stmt): Same.
        (bitint_large_huge::lower_shift_stmt): Same.
        (bitint_large_huge::lower_muldiv_stmt): Same.
        (bitint_large_huge::lower_float_conv_stmt): Same.
        (bitint_large_huge::finish_arith_overflow): Same.
        (bitint_large_huge::lower_addsub_overflow): Same.
        (bitint_large_huge::lower_stmt): Same.
---
 gcc/c-family/c-common.cc   |  65 ++++++-
 gcc/explow.cc              |  32 +++-
 gcc/expr.cc                |  23 ++-
 gcc/gimple-lower-bitint.cc | 365 ++++++++++++++++++++++++++++++++++---
 4 files changed, 444 insertions(+), 41 deletions(-)

diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 587d76461e9..ed43fbc5856 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -8033,11 +8033,36 @@ resolve_overloaded_atomic_exchange (location_t loc, 
tree function,
   /* Convert new value to required type, and dereference it.
      If *p1 type can have padding or may involve floating point which
      could e.g. be promoted to wider precision and demoted afterwards,
-     state of padding bits might not be preserved.  */
+     state of padding bits might not be preserved.
+
+     However, as a special case, we still want to preserve the padding
+     bits of _BitInt values if the ABI requires them to be extended in
+     memory.  */
+
   build_indirect_ref (loc, p1, RO_UNARY_STAR);
-  p1 = build2_loc (loc, MEM_REF, I_type,
-                  build1 (VIEW_CONVERT_EXPR, I_type_ptr, p1),
-                  build_zero_cst (TREE_TYPE (p1)));
+
+  tree p1type = TREE_TYPE (p1);
+  bool bitint_extended_p = false;
+  if (TREE_CODE (TREE_TYPE (p1type)) == BITINT_TYPE)
+    {
+      struct bitint_info info;
+      unsigned prec = TYPE_PRECISION (TREE_TYPE (p1type));
+      targetm.c.bitint_type_info (prec, &info);
+      bitint_extended_p = info.extended;
+    }
+
+ if (bitint_extended_p)
+    p1 = build1_loc (loc, CONVERT_EXPR, I_type,
+                    build2_loc (loc, MEM_REF, TREE_TYPE (p1type),
+                                p1, build_zero_cst (p1type)));
+
+  /* Otherwise, the padding bits might not be preserved, as stated above.  */
+  else
+    p1 = build2_loc (loc, MEM_REF, I_type,
+                    build1 (VIEW_CONVERT_EXPR, I_type_ptr, p1),
+                    build_zero_cst (p1type));
+
+
   (*params)[1] = p1;
 
   /* Move memory model to the 3rd position, and end param list.  */
@@ -8117,11 +8142,35 @@ resolve_overloaded_atomic_compare_exchange (location_t 
loc, tree function,
   /* Convert desired value to required type, and dereference it.
      If *p2 type can have padding or may involve floating point which
      could e.g. be promoted to wider precision and demoted afterwards,
-     state of padding bits might not be preserved.  */
+     state of padding bits might not be preserved.
+
+     However, as a special case, we still want to preserve the padding
+     bits of _BitInt values if the ABI requires them to be extended in
+     memory.  */
+
   build_indirect_ref (loc, p2, RO_UNARY_STAR);
-  p2 = build2_loc (loc, MEM_REF, I_type,
-                  build1 (VIEW_CONVERT_EXPR, I_type_ptr, p2),
-                  build_zero_cst (TREE_TYPE (p2)));
+
+  tree p2type = TREE_TYPE (p2);
+  bool bitint_extended_p = false;
+  if (TREE_CODE (TREE_TYPE (p2type)) == BITINT_TYPE)
+    {
+      struct bitint_info info;
+      unsigned prec = TYPE_PRECISION (TREE_TYPE (p2type));
+      targetm.c.bitint_type_info (prec, &info);
+      bitint_extended_p = info.extended;
+    }
+
+ if (bitint_extended_p)
+    p2 = build1_loc (loc, CONVERT_EXPR, I_type,
+                    build2_loc (loc, MEM_REF, TREE_TYPE (p2type),
+                                p2, build_zero_cst (p2type)));
+
+  /* Otherwise, the padding bits might not be preserved, as stated above.  */
+  else
+    p2 = build2_loc (loc, MEM_REF, I_type,
+                    build1 (VIEW_CONVERT_EXPR, I_type_ptr, p2),
+                    build_zero_cst (p2type));
+
   (*params)[2] = p2;
 
   /* The rest of the parameters are fine. NULL means no special return value
diff --git a/gcc/explow.cc b/gcc/explow.cc
index 7799a98053b..4d4dfd26c9d 100644
--- a/gcc/explow.cc
+++ b/gcc/explow.cc
@@ -852,11 +852,26 @@ promote_function_mode (const_tree type, machine_mode 
mode, int *punsignedp,
        return mode;
     }
 
+  /* Handle _BitInt(N) that does not require promotion.  */
+  if (TREE_CODE (type) == BITINT_TYPE)
+    {
+      if (TYPE_MODE (type) == BLKmode)
+       return mode;
+
+      struct bitint_info info;
+      bool ok = targetm.c.bitint_type_info (TYPE_PRECISION (type), &info);
+      gcc_assert (ok);
+
+      if (!info.extended)
+       return mode;
+    }
+
+
   switch (TREE_CODE (type))
     {
     case INTEGER_TYPE:   case ENUMERAL_TYPE:   case BOOLEAN_TYPE:
     case REAL_TYPE:      case OFFSET_TYPE:     case FIXED_POINT_TYPE:
-    case POINTER_TYPE:   case REFERENCE_TYPE:
+    case POINTER_TYPE:   case REFERENCE_TYPE:  case BITINT_TYPE:
       return targetm.calls.promote_function_mode (type, mode, punsignedp, 
funtype,
                                                  for_return);
 
@@ -891,10 +906,25 @@ promote_mode (const_tree type ATTRIBUTE_UNUSED, 
machine_mode mode,
   code = TREE_CODE (type);
   unsignedp = *punsignedp;
 
+  /* Handle _BitInt(N) that does not require promotion.  */
+  if (code == BITINT_TYPE)
+    {
+      if (TYPE_MODE (type) == BLKmode)
+       return mode;
+
+      struct bitint_info info;
+      bool ok = targetm.c.bitint_type_info (TYPE_PRECISION (type), &info);
+      gcc_assert (ok);
+
+      if (!info.extended)
+       return mode;
+    }
+
   switch (code)
     {
     case INTEGER_TYPE:   case ENUMERAL_TYPE:   case BOOLEAN_TYPE:
     case REAL_TYPE:      case OFFSET_TYPE:     case FIXED_POINT_TYPE:
+    case BITINT_TYPE:
       /* Values of these types always have scalar mode.  */
       smode = as_a <scalar_mode> (mode);
       PROMOTE_MODE (smode, unsignedp, type);
diff --git a/gcc/expr.cc b/gcc/expr.cc
index 3815c565e2d..c2dd53aedcc 100644
--- a/gcc/expr.cc
+++ b/gcc/expr.cc
@@ -11237,6 +11237,10 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode 
tmode,
   tree ssa_name = NULL_TREE;
   gimple *g;
 
+  type = TREE_TYPE (exp);
+  mode = TYPE_MODE (type);
+  unsignedp = TYPE_UNSIGNED (type);
+
   /* Some ABIs define padding bits in _BitInt uninitialized.  Normally, RTL
      expansion sign/zero extends integral types with less than mode precision
      when reading from bit-fields and after arithmetic operations (see
@@ -11247,8 +11251,10 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode 
tmode,
      objects in memory, or function arguments, return value).  Because we
      internally extend after arithmetic operations, we can avoid doing that
      when reading from SSA_NAMEs of vars.  */
+
 #define EXTEND_BITINT(expr) \
   ((TREE_CODE (type) == BITINT_TYPE                                    \
+    && !bitint_type_info.extended                                      \
     && reduce_bit_field                                                        
\
     && mode != BLKmode                                                 \
     && modifier != EXPAND_MEMORY                                       \
@@ -11257,9 +11263,13 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode 
tmode,
     && modifier != EXPAND_CONST_ADDRESS)                               \
    ? reduce_to_bit_field_precision ((expr), NULL_RTX, type) : (expr))
 
-  type = TREE_TYPE (exp);
-  mode = TYPE_MODE (type);
-  unsignedp = TYPE_UNSIGNED (type);
+  struct bitint_info bitint_type_info;
+  if (TREE_CODE (type) == BITINT_TYPE)
+    {
+      bool ok = targetm.c.bitint_type_info (TYPE_PRECISION (type),
+                                           &bitint_type_info);
+      gcc_assert (ok);
+    }
 
   treeop0 = treeop1 = treeop2 = NULL_TREE;
   if (!VL_EXP_CLASS_P (exp))
@@ -11595,12 +11605,9 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode 
tmode,
       {
        if (TREE_CODE (type) == BITINT_TYPE)
          {
-           unsigned int prec = TYPE_PRECISION (type);
-           struct bitint_info info;
-           bool ok = targetm.c.bitint_type_info (prec, &info);
-           gcc_assert (ok);
            scalar_int_mode limb_mode
-             = as_a <scalar_int_mode> (info.limb_mode);
+             = as_a <scalar_int_mode> (bitint_type_info.limb_mode);
+           unsigned int prec = TYPE_PRECISION (type);
            unsigned int limb_prec = GET_MODE_PRECISION (limb_mode);
            if (prec > limb_prec && prec > MAX_FIXED_MODE_SIZE)
              {
diff --git a/gcc/gimple-lower-bitint.cc b/gcc/gimple-lower-bitint.cc
index 6fefc834762..5240ff09e11 100644
--- a/gcc/gimple-lower-bitint.cc
+++ b/gcc/gimple-lower-bitint.cc
@@ -425,7 +425,7 @@ struct bitint_large_huge
   ~bitint_large_huge ();
 
   void insert_before (gimple *);
-  tree limb_access_type (tree, tree);
+  tree limb_access_type (tree, tree, bool = false);
   tree limb_access (tree, tree, tree, bool);
   tree build_bit_field_ref (tree, tree, unsigned HOST_WIDE_INT,
                            unsigned HOST_WIDE_INT);
@@ -438,6 +438,7 @@ struct bitint_large_huge
   tree prepare_data_in_out (tree, tree, tree *, tree = NULL_TREE);
   tree add_cast (tree, tree);
   tree handle_plus_minus (tree_code, tree, tree, tree);
+  tree handle_plus_minus_1 (tree_code, tree, tree, tree);
   tree handle_lshift (tree, tree, tree);
   tree handle_cast (tree, tree, tree);
   tree handle_bit_field_ref (tree, tree);
@@ -553,6 +554,7 @@ struct bitint_large_huge
   unsigned m_upwards_2limb;
   bool m_upwards;
   bool m_cast_conditional;
+  bool m_force_extend;
   unsigned m_bitfld_load;
   vec<tree> m_data;
   unsigned int m_data_cnt;
@@ -578,7 +580,7 @@ bitint_large_huge::~bitint_large_huge ()
 void
 bitint_large_huge::insert_before (gimple *g)
 {
-  gimple_set_location (g, m_loc);
+  gimple_set_location (g, m_loc) ;
   gsi_insert_before (&m_gsi, g, GSI_SAME_STMT);
 }
 
@@ -587,18 +589,34 @@ bitint_large_huge::insert_before (gimple *g)
    significant limb if any.  */
 
 tree
-bitint_large_huge::limb_access_type (tree type, tree idx)
+bitint_large_huge::limb_access_type (tree type, tree idx,
+                                    bool force_extend)
 {
   if (type == NULL_TREE)
     return m_limb_type;
+
+  /* Either force_extend or m_force_extend should be set
+     when we want this function to return the type of the
+     truncated partial (high) limb.  */
+  force_extend |= m_force_extend;
+
   unsigned HOST_WIDE_INT i = tree_to_uhwi (idx);
   unsigned int prec = TYPE_PRECISION (type);
   gcc_assert (i * limb_prec < prec);
   if ((i + 1) * limb_prec <= prec)
     return m_limb_type;
   else
-    return build_nonstandard_integer_type (prec % limb_prec,
-                                          TYPE_UNSIGNED (type));
+    {
+      struct bitint_info info;
+      bool ok = targetm.c.bitint_type_info (prec, &info);
+      gcc_assert (ok);
+
+      if (info.extended && !force_extend)
+       return m_limb_type;
+      else
+       return build_nonstandard_integer_type (prec % limb_prec,
+                                              TYPE_UNSIGNED (type));
+    }
 }
 
 /* Return a tree how to access limb IDX of VAR corresponding to BITINT_TYPE
@@ -1126,6 +1144,42 @@ bitint_large_huge::add_cast (tree type, tree val)
 tree
 bitint_large_huge::handle_plus_minus (tree_code code, tree rhs1, tree rhs2,
                                      tree idx)
+{
+  /* Truncate the result if the target ABI requires so.  */
+  struct bitint_info info;
+  bool ok = targetm.c.bitint_type_info (
+           TYPE_PRECISION (rhs1 == NULL_TREE
+                           ? TREE_TYPE (rhs2) : TREE_TYPE (rhs1)),
+           &info);
+
+  gcc_assert (ok);
+
+  bool m_force_extend_save = m_force_extend;
+  if (info.extended)
+    m_force_extend = true;
+
+  rhs2 = handle_operand (rhs2, idx);
+  if (rhs1 == NULL_TREE)
+    rhs1 = build_zero_cst (TREE_TYPE (rhs2));
+  else
+    rhs1 = handle_operand (rhs1, idx);
+
+  if (info.extended)
+    m_force_extend = m_force_extend_save;
+
+  tree ret = handle_plus_minus_1 (code, rhs1, rhs2, idx);
+
+  if (info.extended && !m_force_extend
+      && !types_compatible_p (TREE_TYPE (ret), m_limb_type))
+    ret = add_cast (m_limb_type, ret);
+
+  return ret;
+}
+
+
+tree
+bitint_large_huge::handle_plus_minus_1 (tree_code code, tree rhs1, tree rhs2,
+                                       tree idx)
 {
   tree lhs, data_out, ctype;
   tree rhs1_type = TREE_TYPE (rhs1);
@@ -1234,9 +1288,27 @@ bitint_large_huge::handle_lshift (tree rhs1, tree rhs2, 
tree idx)
 {
   unsigned HOST_WIDE_INT cnt = tree_to_uhwi (rhs2);
   gcc_checking_assert (cnt < (unsigned) limb_prec);
+
   if (cnt == 0)
     return rhs1;
 
+  /* Truncate the result if the target ABI requires so.  */
+  struct bitint_info info;
+  bool ok = targetm.c.bitint_type_info (TYPE_PRECISION (TREE_TYPE (rhs1)),
+                                       &info);
+  gcc_assert (ok);
+
+  if (info.extended)
+    {
+      bool m_force_extend_save = m_force_extend;
+      m_force_extend = true;
+      rhs1 = handle_operand (rhs1, idx);
+      m_force_extend = m_force_extend_save;
+    }
+  else
+    rhs1 = handle_operand (rhs1, idx);
+
+
   tree lhs, data_out, rhs1_type = TREE_TYPE (rhs1);
   gimple *g;
   tree data_in = prepare_data_in_out (build_zero_cst (m_limb_type), idx,
@@ -1275,6 +1347,15 @@ bitint_large_huge::handle_lshift (tree rhs1, tree rhs2, 
tree idx)
     }
   else
     lhs = data_in;
+
+  if (info.extended && !m_force_extend
+      && !types_compatible_p (rhs1_type, m_limb_type))
+    {
+      /* Restore the type to full limb type after downcasting,
+        so that it can compute with _BitInt operands of other sources.  */
+      lhs = add_cast (m_limb_type, lhs);
+    }
+
   m_data[m_data_cnt] = data_out;
   m_data_cnt += 2;
   return lhs;
@@ -1286,6 +1367,10 @@ bitint_large_huge::handle_lshift (tree rhs1, tree rhs2, 
tree idx)
 tree
 bitint_large_huge::handle_cast (tree lhs_type, tree rhs1, tree idx)
 {
+  struct bitint_info info;
+  bool ok = targetm.c.bitint_type_info (0, &info);
+  gcc_assert (ok);
+
   tree rhs_type = TREE_TYPE (rhs1);
   gimple *g;
   if ((TREE_CODE (rhs1) == SSA_NAME || TREE_CODE (rhs1) == INTEGER_CST)
@@ -1315,9 +1400,20 @@ bitint_large_huge::handle_cast (tree lhs_type, tree 
rhs1, tree idx)
          rhs1 = handle_operand (rhs1, idx);
          if (tree_fits_uhwi_p (idx))
            {
-             tree type = limb_access_type (lhs_type, idx);
+             tree type = limb_access_type (lhs_type, idx, true);
              if (!types_compatible_p (type, TREE_TYPE (rhs1)))
                rhs1 = add_cast (type, rhs1);
+
+             /* Restore the type to full limb type after downcasting,
+                so that it can compute with _BitInt operands of other sources.
+
+                However, we want to avoid this if handle_cast is called during
+                handle_operand when m_force_extend is set, because it needs to
+                behave exactly the same as the non-"info.extended" case.  */
+
+             if (info.extended && !m_force_extend
+                 && !types_compatible_p (type, m_limb_type))
+               rhs1 = add_cast (m_limb_type, rhs1);
            }
          return rhs1;
        }
@@ -1595,9 +1691,14 @@ bitint_large_huge::handle_cast (tree lhs_type, tree 
rhs1, tree idx)
              else
                t = m_data[save_data_cnt + 1];
            }
-         tree type = limb_access_type (lhs_type, idx);
+         tree type = limb_access_type (lhs_type, idx, true);
          if (!useless_type_conversion_p (type, m_limb_type))
            t = add_cast (type, t);
+
+         if (info.extended && !m_force_extend
+             && !types_compatible_p (type, m_limb_type))
+           t = add_cast (m_limb_type, t);
+
          m_first = save_first;
          return t;
        }
@@ -1681,10 +1782,14 @@ bitint_large_huge::handle_cast (tree lhs_type, tree 
rhs1, tree idx)
            t = m_data[m_data_cnt + 1];
          else
            {
-             tree type = limb_access_type (lhs_type, idx);
+             tree type = limb_access_type (lhs_type, idx, true);
              t = m_data[m_data_cnt + 2];
              if (!useless_type_conversion_p (type, m_limb_type))
                t = add_cast (type, t);
+
+             if (info.extended && !m_force_extend
+                 && !types_compatible_p (type, m_limb_type))
+               t = add_cast (m_limb_type, t);
            }
          m_data_cnt += 3;
          return t;
@@ -1697,7 +1802,7 @@ bitint_large_huge::handle_cast (tree lhs_type, tree rhs1, 
tree idx)
        }
       if (tree_fits_uhwi_p (idx))
        {
-         tree type = limb_access_type (lhs_type, idx);
+         tree type = limb_access_type (lhs_type, idx, true);
          if (integer_zerop (idx))
            t = m_data[m_data_cnt];
          else if (TYPE_PRECISION (rhs_type) > limb_prec
@@ -1707,6 +1812,11 @@ bitint_large_huge::handle_cast (tree lhs_type, tree 
rhs1, tree idx)
            t = m_data[m_data_cnt + 2];
          if (!useless_type_conversion_p (type, m_limb_type))
            t = add_cast (type, t);
+
+         if (info.extended && !m_force_extend
+             && !types_compatible_p (type, m_limb_type))
+           t = add_cast (m_limb_type, t);
+
          m_data_cnt += 3;
          return t;
        }
@@ -1847,6 +1957,7 @@ bitint_large_huge::handle_load (gimple *stmt, tree idx)
 {
   tree rhs1 = gimple_assign_rhs1 (stmt);
   tree rhs_type = TREE_TYPE (rhs1);
+  bool load_bitfield_p = false;
   bool eh = stmt_ends_bb_p (stmt);
   edge eh_edge = NULL;
   gimple *g;
@@ -1870,11 +1981,17 @@ bitint_large_huge::handle_load (gimple *stmt, tree idx)
       gcc_assert (tree_fits_uhwi_p (DECL_FIELD_BIT_OFFSET (fld)));
       if (DECL_OFFSET_ALIGN (fld) >= TYPE_ALIGN (TREE_TYPE (rhs1))
          && (tree_to_uhwi (DECL_FIELD_BIT_OFFSET (fld)) % limb_prec) == 0)
-       goto normal_load;
+       {
+         load_bitfield_p = true;
+         goto normal_load;
+       }
       /* Even if DECL_FIELD_BIT_OFFSET (fld) is a multiple of UNITS_PER_BIT,
         handle it normally for now.  */
       if ((tree_to_uhwi (DECL_FIELD_BIT_OFFSET (fld)) % BITS_PER_UNIT) == 0)
-       goto normal_load;
+       {
+         load_bitfield_p = true;
+         goto normal_load;
+       }
       tree repr = DECL_BIT_FIELD_REPRESENTATIVE (fld);
       poly_int64 bitoffset;
       poly_uint64 field_offset, repr_offset;
@@ -2069,9 +2186,15 @@ bitint_large_huge::handle_load (gimple *stmt, tree idx)
        }
       if (tree_fits_uhwi_p (idx))
        {
-         tree atype = limb_access_type (rhs_type, idx);
+         tree atype = limb_access_type (rhs_type, idx, true);
          if (!useless_type_conversion_p (atype, TREE_TYPE (iv)))
            iv = add_cast (atype, iv);
+
+         /* For ABI-extended _BitInts, convert them back to nominal type
+            after truncation to work with other operands out there.  */
+         tree ntype = limb_access_type (rhs_type, idx);
+         if (!types_compatible_p (ntype, atype))
+           iv = add_cast (ntype, iv);
        }
       m_data_cnt += 3;
       return iv;
@@ -2085,6 +2208,20 @@ normal_load:
   tree ret = make_ssa_name (TREE_TYPE (rhs1));
   g = gimple_build_assign (ret, rhs1);
   insert_before (g);
+
+  /* A "normal load" of a bitfield may bring over other bitfields
+     sharing the same representative at a higher offset.
+     Truncate them if required.  */
+
+  bool truncate_p = false;
+  if (load_bitfield_p)
+    {
+      struct bitint_info info;
+      bool ok = targetm.c.bitint_type_info (TYPE_PRECISION (rhs_type), &info);
+      gcc_assert (ok);
+      truncate_p = info.extended;
+    }
+
   if (eh)
     {
       maybe_duplicate_eh_stmt (g, stmt);
@@ -2094,13 +2231,24 @@ normal_load:
          m_gsi = gsi_after_labels (e->dest);
          add_eh_edge (e->src, eh_edge);
        }
-      if (tree_fits_uhwi_p (idx))
+      if (tree_fits_uhwi_p (idx) && !truncate_p)
        {
          tree atype = limb_access_type (rhs_type, idx);
          if (!useless_type_conversion_p (atype, TREE_TYPE (rhs1)))
            ret = add_cast (atype, ret);
        }
     }
+
+  if (truncate_p && tree_fits_uhwi_p (idx))
+    {
+      tree atype = limb_access_type (rhs_type, idx, true);
+      if (!useless_type_conversion_p (atype, TREE_TYPE (rhs1)))
+       {
+         ret = add_cast (atype, ret);
+         ret = add_cast (TREE_TYPE (rhs1), ret);
+       }
+    }
+
   return ret;
 }
 
@@ -2129,20 +2277,36 @@ bitint_large_huge::handle_stmt (gimple *stmt, tree idx)
          g = gimple_build_assign (lhs, gimple_assign_rhs_code (stmt),
                                   rhs1, rhs2);
          insert_before (g);
+
+         /* For BIT_NOT_EXPR, truncate the partial limb if necessary.  */
+         if (rhs2 == NULL_TREE
+             && tree_fits_uhwi_p (idx))
+           {
+             tree rhs_type = TREE_TYPE (gimple_assign_rhs1 (stmt));
+             tree atype = limb_access_type (rhs_type, idx, true);
+             struct bitint_info info;
+             bool ok = targetm.c.bitint_type_info (TYPE_PRECISION (rhs_type),
+                                                   &info);
+             gcc_assert (ok);
+
+             if (info.extended
+                 && !types_compatible_p (atype, TREE_TYPE (rhs1)))
+               {
+                 lhs = add_cast (atype, lhs);
+                 lhs = add_cast (TREE_TYPE (rhs1), lhs);
+               }
+           }
          return lhs;
        case PLUS_EXPR:
        case MINUS_EXPR:
-         rhs1 = handle_operand (gimple_assign_rhs1 (stmt), idx);
-         rhs2 = handle_operand (gimple_assign_rhs2 (stmt), idx);
          return handle_plus_minus (gimple_assign_rhs_code (stmt),
-                                   rhs1, rhs2, idx);
+                                   gimple_assign_rhs1 (stmt),
+                                   gimple_assign_rhs2 (stmt), idx);
        case NEGATE_EXPR:
-         rhs2 = handle_operand (gimple_assign_rhs1 (stmt), idx);
-         rhs1 = build_zero_cst (TREE_TYPE (rhs2));
-         return handle_plus_minus (MINUS_EXPR, rhs1, rhs2, idx);
+         return handle_plus_minus (MINUS_EXPR, NULL_TREE,
+                                   gimple_assign_rhs1 (stmt), idx);
        case LSHIFT_EXPR:
-         return handle_lshift (handle_operand (gimple_assign_rhs1 (stmt),
-                                               idx),
+         return handle_lshift (gimple_assign_rhs1 (stmt),
                                gimple_assign_rhs2 (stmt), idx);
        case SSA_NAME:
        case PAREN_EXPR:
@@ -2642,8 +2806,20 @@ bitint_large_huge::lower_mergeable_stmt (gimple *stmt, 
tree_code &cmp_code,
        idx = size_int (end + (i > 2));
       if (eq_p)
        {
+         struct bitint_info info;
+         bool ok = targetm.c.bitint_type_info (prec, &info);
+         gcc_assert (ok);
+
+         bool m_force_extend_save = m_force_extend;
+         if (info.extended)
+           m_force_extend = true;
+
          rhs1 = handle_operand (cmp_op1, idx);
          tree rhs2 = handle_operand (cmp_op2, idx);
+
+         if (info.extended)
+           m_force_extend = m_force_extend_save;
+
          g = gimple_build_cond (NE_EXPR, rhs1, rhs2, NULL_TREE, NULL_TREE);
          insert_before (g);
          edge e1 = split_block (gsi_bb (m_gsi), g);
@@ -2925,6 +3101,19 @@ bitint_large_huge::lower_mergeable_stmt (gimple *stmt, 
tree_code &cmp_code,
          /* Otherwise, stores to any other lhs.  */
          if (!done)
            {
+             struct bitint_info info;
+             bool ok = targetm.c.bitint_type_info (TYPE_PRECISION (type),
+                                                   &info);
+             gcc_assert (ok);
+
+             /* Truncate the partial limb for both the large / huge cases.  */
+             if (info.extended && i == cnt - 1 && prec % limb_prec != 0)
+               {
+                 // gcc_assert (tree_fits_uhwi_p (idx));
+                 rhs1 = add_cast (limb_access_type (type, idx, true), rhs1);
+                 rhs1 = add_cast (m_limb_type, rhs1);
+               }
+
              tree l = limb_access (nlhs ? NULL_TREE : lhs_type,
                                    nlhs ? nlhs : lhs, nidx, true);
              g = gimple_build_assign (l, rhs1);
@@ -3497,6 +3686,30 @@ bitint_large_huge::lower_shift_stmt (tree obj, gimple 
*stmt)
          insert_before (g);
          t1 = gimple_assign_lhs (g);
        }
+
+      /* Truncate the result if the target ABI requires so.  */
+      struct bitint_info info;
+      bool ok = targetm.c.bitint_type_info (prec, &info);
+      gcc_assert (ok);
+
+      if (info.extended)
+       {
+         tree hi_type = limb_access_type (TREE_TYPE (lhs), p, true);
+         t1 = build3 (COND_EXPR, TREE_TYPE (t1),
+                      fold_build2 (EQ_EXPR, boolean_type_node, idx, p),
+                      build1 (NOP_EXPR, TREE_TYPE (t1),
+                              build1 (CONVERT_EXPR, hi_type, t1)),
+                      t1);
+
+         t1 = force_gimple_operand_gsi (&m_gsi, t1, true, NULL_TREE, true,
+                                        GSI_SAME_STMT);
+
+         g = gimple_build_assign (make_ssa_name (TREE_TYPE (t1)), t1);
+         insert_before (g);
+
+         t1 = gimple_assign_lhs (g);
+       }
+
       tree l = limb_access (TREE_TYPE (lhs), obj, idx, true);
       g = gimple_build_assign (l, t1);
       insert_before (g);
@@ -3529,6 +3742,25 @@ bitint_large_huge::lower_shift_stmt (tree obj, gimple 
*stmt)
                                   LSHIFT_EXPR, t1, n1);
          insert_before (g);
          t1 = gimple_assign_lhs (g);
+
+         if (info.extended)
+           {
+             tree hi_type = limb_access_type (TREE_TYPE (lhs), p, true);
+             t1 = build3 (COND_EXPR, TREE_TYPE (t1),
+                          fold_build2 (EQ_EXPR, boolean_type_node, idx, p),
+                          build1 (NOP_EXPR, TREE_TYPE (t1),
+                                  build1 (CONVERT_EXPR, hi_type, t1)),
+                          t1);
+
+             t1 = force_gimple_operand_gsi (&m_gsi, t1, true, NULL_TREE, true,
+                                            GSI_SAME_STMT);
+
+             g = gimple_build_assign (make_ssa_name (TREE_TYPE (t1)), t1);
+             insert_before (g);
+
+             t1 = gimple_assign_lhs (g);
+           }
+
          l = limb_access (TREE_TYPE (lhs), obj, idx, true);
          g = gimple_build_assign (l, t1);
          insert_before (g);
@@ -3624,6 +3856,33 @@ bitint_large_huge::lower_muldiv_stmt (tree obj, gimple 
*stmt)
     default:
       gcc_unreachable ();
     }
+
+  /* Truncate the result if necessary (libgcc won't do this now).  */
+  if (prec % limb_prec != 0)
+    {
+      struct bitint_info info;
+      bool ok = targetm.c.bitint_type_info (prec, &info);
+      gcc_assert (ok);
+
+      if (info.extended)
+       {
+         tree idx = size_int (prec / limb_prec);
+         tree src = limb_access (type, obj, idx, false);
+         tree dst = limb_access (type, obj, idx, true);
+         tree hi_type = limb_access_type (type, idx, true);
+
+         g = gimple_build_assign (make_ssa_name (m_limb_type), src);
+         insert_before (g);
+         src = gimple_assign_lhs (g);
+
+         src = add_cast (hi_type, src);
+         src = add_cast (m_limb_type, src);
+
+         g = gimple_build_assign (dst, src);
+         insert_before (g);
+       }
+    }
+
   if (stmt_ends_bb_p (stmt))
     {
       maybe_duplicate_eh_stmt (g, stmt);
@@ -3655,7 +3914,8 @@ bitint_large_huge::lower_float_conv_stmt (tree obj, 
gimple *stmt)
   gimple *g;
   if (rhs_code == FIX_TRUNC_EXPR)
     {
-      int prec = TYPE_PRECISION (TREE_TYPE (lhs));
+      tree type = TREE_TYPE (lhs);
+      int prec = TYPE_PRECISION (type);
       if (!TYPE_UNSIGNED (TREE_TYPE (lhs)))
        prec = -prec;
       if (obj == NULL_TREE)
@@ -3669,7 +3929,7 @@ bitint_large_huge::lower_float_conv_stmt (tree obj, 
gimple *stmt)
        {
          lhs = build_fold_addr_expr (obj);
          lhs = force_gimple_operand_gsi (&m_gsi, lhs, true,
-                                         NULL_TREE, true, GSI_SAME_STMT);
+                                          NULL_TREE, true, GSI_SAME_STMT);
        }
       scalar_mode from_mode
        = as_a <scalar_mode> (TYPE_MODE (TREE_TYPE (rhs1)));
@@ -3690,6 +3950,34 @@ bitint_large_huge::lower_float_conv_stmt (tree obj, 
gimple *stmt)
                                      lhs, build_int_cst (sitype, prec),
                                      rhs1);
       insert_before (g);
+
+      /* Truncate the result if necessary (libgcc won't do this now).  */
+      if (prec % limb_prec != 0)
+       {
+         prec = prec < 0 ? -prec : prec;
+
+         struct bitint_info info;
+         bool ok = targetm.c.bitint_type_info (prec, &info);
+         gcc_assert (ok);
+
+         if (info.extended)
+           {
+             tree idx = size_int (prec / limb_prec);
+             tree src = limb_access (type, obj, idx, false);
+             tree dst = limb_access (type, obj, idx, true);
+             tree hi_type = limb_access_type (type, idx, true);
+
+             g = gimple_build_assign (make_ssa_name (m_limb_type), src);
+             insert_before (g);
+             src = gimple_assign_lhs (g);
+
+             src = add_cast (hi_type, src);
+             src = add_cast (m_limb_type, src);
+
+             g = gimple_build_assign (dst, src);
+             insert_before (g);
+           }
+       }
     }
   else
     {
@@ -3882,6 +4170,34 @@ bitint_large_huge::finish_arith_overflow (tree var, tree 
obj, tree type,
          g = gimple_build_assign (v1, v2);
          insert_before (g);
        }
+
+      /* Truncate the result if necessary (libgcc won't do this now).  */
+      int prec = TYPE_PRECISION (type);
+      if (obj && prec % limb_prec != 0)
+       {
+         struct bitint_info info;
+         bool ok = targetm.c.bitint_type_info (prec, &info);
+         gcc_assert (ok);
+
+         if (info.extended)
+           {
+             tree idx = size_int (prec / limb_prec);
+             tree src = limb_access (type, obj, idx, false);
+             tree dst = limb_access (type, obj, idx, true);
+             tree hi_type = limb_access_type (type, idx, true);
+
+             g = gimple_build_assign (make_ssa_name (m_limb_type), src);
+             insert_before (g);
+             src = gimple_assign_lhs (g);
+
+             src = add_cast (hi_type, src);
+             src = add_cast (m_limb_type, src);
+
+             g = gimple_build_assign (dst, src);
+             insert_before (g);
+           }
+       }
+
       if (orig_obj == NULL_TREE && obj)
        {
          ovf = add_cast (m_limb_type, ovf);
@@ -4234,7 +4550,7 @@ bitint_large_huge::lower_addsub_overflow (tree obj, 
gimple *stmt)
                }
            }
        }
-      tree rhs = handle_plus_minus (code, rhs1, rhs2, idx);
+      tree rhs = handle_plus_minus_1 (code, rhs1, rhs2, idx);
       if (ovf != boolean_false_node)
        {
          if (tree_fits_uhwi_p (idx))
@@ -5416,6 +5732,7 @@ bitint_large_huge::lower_stmt (gimple *stmt)
   m_upwards = false;
   m_var_msb = false;
   m_cast_conditional = false;
+  m_force_extend = false;
   m_bitfld_load = 0;
   m_loc = gimple_location (stmt);
   if (is_gimple_call (stmt))
-- 
2.34.1

Reply via email to