aboutsummaryrefslogtreecommitdiff
path: root/gcc/ada/gcc-interface/utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/ada/gcc-interface/utils.c')
-rw-r--r--gcc/ada/gcc-interface/utils.c30
1 files changed, 28 insertions, 2 deletions
diff --git a/gcc/ada/gcc-interface/utils.c b/gcc/ada/gcc-interface/utils.c
index 7ec0974..77d00b3 100644
--- a/gcc/ada/gcc-interface/utils.c
+++ b/gcc/ada/gcc-interface/utils.c
@@ -2908,7 +2908,24 @@ process_deferred_decl_context (bool force)
static unsigned int
scale_by_factor_of (tree expr, unsigned int value)
{
+ unsigned HOST_WIDE_INT addend = 0;
+ unsigned HOST_WIDE_INT factor = 1;
+
+ /* Peel conversions around EXPR and try to extract bodies from function
+ calls: it is possible to get the scale factor from size functions. */
expr = remove_conversions (expr, true);
+ if (TREE_CODE (expr) == CALL_EXPR)
+ expr = maybe_inline_call_in_expr (expr);
+
+ /* Sometimes we get PLUS_EXPR (BIT_AND_EXPR (..., X), Y), where Y is a
+ multiple of the scale factor we are looking for. */
+ if (TREE_CODE (expr) == PLUS_EXPR
+ && TREE_CODE (TREE_OPERAND (expr, 1)) == INTEGER_CST
+ && tree_fits_uhwi_p (TREE_OPERAND (expr, 1)))
+ {
+ addend = TREE_INT_CST_LOW (TREE_OPERAND (expr, 1));
+ expr = TREE_OPERAND (expr, 0);
+ }
/* An expression which is a bitwise AND with a mask has a power-of-2 factor
corresponding to the number of trailing zeros of the mask. */
@@ -2921,12 +2938,21 @@ scale_by_factor_of (tree expr, unsigned int value)
while ((mask & 1) == 0 && i < HOST_BITS_PER_WIDE_INT)
{
mask >>= 1;
- value *= 2;
+ factor *= 2;
i++;
}
}
- return value;
+ /* If the addend is not a multiple of the factor we found, give up. In
+ theory we could find a smaller common factor but it's useless for our
+ needs. This situation arises when dealing with a field F1 with no
+ alignment requirement but that is following a field F2 with such
+ requirements. As long as we have F2's offset, we don't need alignment
+ information to compute F1's. */
+ if (addend % factor != 0)
+ factor = 1;
+
+ return factor * value;
}
/* Given two consecutive field decls PREV_FIELD and CURR_FIELD, return true