]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/function-abi.cc
Correct a function pre/postcondition [PR102403].
[thirdparty/gcc.git] / gcc / function-abi.cc
index e2c35b6a274cb87c4560779c4350c5585595622e..6d2c5c3c8b363550853fbd15c8b93725f1d25f59 100644 (file)
@@ -1,5 +1,5 @@
 /* Information about fuunction binary interfaces.
-   Copyright (C) 2019 Free Software Foundation, Inc.
+   Copyright (C) 2019-2021 Free Software Foundation, Inc.
 
 This file is part of GCC
 
@@ -50,7 +50,7 @@ predefined_function_abi::initialize (unsigned int id,
 
      If the ABI specifies that part of a hard register R is call-clobbered,
      we should be able to find a single-register mode M for which
-     targetm.hard_regno_call_part_clobbered (NULL, R, M) is true.
+     targetm.hard_regno_call_part_clobbered (m_id, R, M) is true.
      In other words, it shouldn't be the case that R can hold all
      single-register modes across a call, but can't hold part of
      a multi-register mode.
@@ -66,7 +66,7 @@ predefined_function_abi::initialize (unsigned int id,
       for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno)
        if (targetm.hard_regno_mode_ok (regno, mode)
            && hard_regno_nregs (regno, mode) == 1
-           && targetm.hard_regno_call_part_clobbered (NULL, regno, mode))
+           && targetm.hard_regno_call_part_clobbered (m_id, regno, mode))
          SET_HARD_REG_BIT (m_full_and_partial_reg_clobbers, regno);
     }
 
@@ -89,7 +89,7 @@ predefined_function_abi::initialize (unsigned int id,
       for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno)
        if (targetm.hard_regno_mode_ok (regno, mode)
            && !overlaps_hard_reg_set_p (m_full_reg_clobbers, mode, regno)
-           && !targetm.hard_regno_call_part_clobbered (NULL, regno, mode))
+           && !targetm.hard_regno_call_part_clobbered (m_id, regno, mode))
          remove_from_hard_reg_set (&m_mode_clobbers[i], mode, regno);
     }
 
@@ -104,7 +104,7 @@ predefined_function_abi::initialize (unsigned int id,
        for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno)
          if (targetm.hard_regno_mode_ok (regno, mode)
              && !overlaps_hard_reg_set_p (m_full_reg_clobbers, mode, regno)
-             && targetm.hard_regno_call_part_clobbered (NULL, regno, mode))
+             && targetm.hard_regno_call_part_clobbered (m_id, regno, mode))
            gcc_assert (overlaps_hard_reg_set_p (all_clobbers, mode, regno)
                        && overlaps_hard_reg_set_p (m_mode_clobbers[i],
                                                    mode, regno));
@@ -126,6 +126,67 @@ predefined_function_abi::add_full_reg_clobber (unsigned int regno)
     SET_HARD_REG_BIT (m_mode_clobbers[i], regno);
 }
 
+/* Return the set of registers that the caller of the recorded functions must
+   save in order to honor the requirements of CALLER_ABI.  */
+
+HARD_REG_SET
+function_abi_aggregator::
+caller_save_regs (const function_abi &caller_abi) const
+{
+  HARD_REG_SET result;
+  CLEAR_HARD_REG_SET (result);
+  for (unsigned int abi_id = 0; abi_id < NUM_ABI_IDS; ++abi_id)
+    {
+      const predefined_function_abi &callee_abi = function_abis[abi_id];
+
+      /* Skip cases that clearly aren't problematic.  */
+      if (abi_id == caller_abi.id ()
+         || hard_reg_set_empty_p (m_abi_clobbers[abi_id]))
+       continue;
+
+      /* Collect the set of registers that can be "more clobbered" by
+        CALLEE_ABI than by CALLER_ABI.  */
+      HARD_REG_SET extra_clobbers;
+      CLEAR_HARD_REG_SET (extra_clobbers);
+      for (unsigned int i = 0; i < NUM_MACHINE_MODES; ++i)
+       {
+         machine_mode mode = (machine_mode) i;
+         extra_clobbers |= (callee_abi.mode_clobbers (mode)
+                            & ~caller_abi.mode_clobbers (mode));
+       }
+
+      /* Restrict it to the set of registers that we actually saw
+        clobbers for (e.g. taking -fipa-ra into account).  */
+      result |= (extra_clobbers & m_abi_clobbers[abi_id]);
+    }
+  return result;
+}
+
+/* Return the set of registers that cannot be used to hold a value of
+   mode MODE across the calls in a region described by ABIS and MASK, where:
+
+   * Bit ID of ABIS is set if the region contains a call with
+     function_abi identifier ID.
+
+   * MASK contains all the registers that are fully or partially
+     clobbered by calls in the region.
+
+   This is not quite as accurate as testing each individual call,
+   but it's a close and conservatively-correct approximation.
+   It's much better for some targets than just using MASK.  */
+
+HARD_REG_SET
+call_clobbers_in_region (unsigned int abis, const_hard_reg_set mask,
+                        machine_mode mode)
+{
+  HARD_REG_SET result;
+  CLEAR_HARD_REG_SET (result);
+  for (unsigned int id = 0; abis; abis >>= 1, ++id)
+    if (abis & 1)
+      result |= function_abis[id].mode_clobbers (mode);
+  return result & mask;
+}
+
 /* Return the predefined ABI used by functions with type TYPE.  */
 
 const predefined_function_abi &
@@ -168,3 +229,26 @@ insn_callee_abi (const rtx_insn *insn)
 
   return default_function_abi;
 }
+
+/* Return the ABI of the function called by CALL_EXPR EXP.  Return the
+   default ABI for erroneous calls.  */
+
+function_abi
+expr_callee_abi (const_tree exp)
+{
+  gcc_assert (TREE_CODE (exp) == CALL_EXPR);
+
+  if (tree fndecl = get_callee_fndecl (exp))
+    return fndecl_abi (fndecl);
+
+  tree callee = CALL_EXPR_FN (exp);
+  if (callee == error_mark_node)
+    return default_function_abi;
+
+  tree type = TREE_TYPE (callee);
+  if (type == error_mark_node)
+    return default_function_abi;
+
+  gcc_assert (POINTER_TYPE_P (type));
+  return fntype_abi (TREE_TYPE (type));
+}