aboutsummaryrefslogtreecommitdiff
path: root/gcc/c/c-parser.cc
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2024-11-19 20:34:36 +0100
committerJakub Jelinek <jakub@gcc.gnu.org>2024-11-19 20:34:36 +0100
commit02fff24e2c6d4affc47dac1433b2fb182dadd4db (patch)
tree4225c90fbe2363368733eed1bda012fe3e457a28 /gcc/c/c-parser.cc
parent780720f04b0b83261d6073b92f3b02e8fbef41b9 (diff)
downloadgcc-02fff24e2c6d4affc47dac1433b2fb182dadd4db.zip
gcc-02fff24e2c6d4affc47dac1433b2fb182dadd4db.tar.gz
gcc-02fff24e2c6d4affc47dac1433b2fb182dadd4db.tar.bz2
c: Fix up __builtin_stdc_rotate_{left,right} lowering [PR117456]
Apparently the middle-end/expansion can only handle {L,R}ROTATE_EXPR on types with mode precision, or large/huge BITINT_TYPE. So, the following patch uses the rotate exprs only in those cases where it can be handled, and emits code with shifts/ior otherwise. As types without mode precision including small/medium BITINT_TYPE have unlikely power of two precision and TRUNC_MOD_EXPR is on many targets quite expensive, I chose to expand e.g. __builtin_stdc_rotate_left (arg1, arg2) as ((tem = arg1, count = arg2 % prec) ? ((tem << count) | (tem >> (prec - count))) : tem) rather than (((tem = arg1) << (count = arg2 % prec)) | (tem >> (-count % prec)) (where the assignments are really save_exprs, so no UB), because I think another TRUNC_MOD_EXPR would be more costly in most cases when the shift count is non-constant (and when it is constant, it folds to 2 shifts by constant and ior in either case). 2024-11-19 Jakub Jelinek <jakub@redhat.com> PR c/117456 gcc/c/ * c-parser.cc (c_parser_postfix_expression): Use LROTATE_EXPR or RROTATE_EXPR only if type_has_mode_precision_p or if arg1 has BITINT_TYPE with precision larger than MAX_FIXED_MODE_SIZE. Otherwise build BIT_IOR_EXPR of LSHIFT_EXPR and RSHIFT_EXPR and wrap it into a COND_EXPR depending on if arg2 is 0 or not. * c-fold.cc (c_fully_fold_internal): Check for suppression of -Wshift-count-overflow warning. gcc/testsuite/ * gcc.dg/builtin-stdc-rotate-4.c: New test.
Diffstat (limited to 'gcc/c/c-parser.cc')
-rw-r--r--gcc/c/c-parser.cc34
1 files changed, 32 insertions, 2 deletions
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 70fbf94..def6b30 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -12638,8 +12638,38 @@ c_parser_postfix_expression (c_parser *parser)
build_int_cst (utype, prec));
}
- expr.value = build2_loc (loc, code, TREE_TYPE (arg1), arg1,
- arg2);
+ /* The middle-end isn't prepared to handle {L,R}ROTATE_EXPR
+ on types without mode precision, except for large/huge
+ _BitInt types. */
+ if (type_has_mode_precision_p (TREE_TYPE (arg1))
+ || (TREE_CODE (TREE_TYPE (arg1)) == BITINT_TYPE
+ && prec > MAX_FIXED_MODE_SIZE))
+ expr.value = build2_loc (loc, code, TREE_TYPE (arg1), arg1,
+ arg2);
+ else
+ {
+ arg2 = save_expr (arg2);
+ tree t1 = build2_loc (loc, (code == LROTATE_EXPR
+ ? LSHIFT_EXPR : RSHIFT_EXPR),
+ TREE_TYPE (arg1), arg1, arg2);
+ tree t2 = build2_loc (loc, MINUS_EXPR,
+ TREE_TYPE (arg2),
+ build_int_cst (TREE_TYPE (arg2),
+ prec), arg2);
+ t2 = build2_loc (loc, (code == LROTATE_EXPR
+ ? RSHIFT_EXPR : LSHIFT_EXPR),
+ TREE_TYPE (arg1), arg1, t2);
+ suppress_warning (t2, OPT_Wshift_count_overflow);
+ tree t3 = build2_loc (loc, BIT_IOR_EXPR,
+ TREE_TYPE (arg1), t1, t2);
+ tree t4 = build2_loc (loc, NE_EXPR, boolean_type_node,
+ arg2,
+ build_zero_cst (TREE_TYPE (arg2)));
+ t4 = build2_loc (loc, COMPOUND_EXPR, boolean_type_node,
+ arg1, t4);
+ expr.value = build3_loc (loc, COND_EXPR,
+ TREE_TYPE (arg1), t4, t3, arg1);
+ }
if (instrument_expr)
expr.value = build2_loc (loc, COMPOUND_EXPR,
TREE_TYPE (expr.value),