Hi!
On Tue, Oct 29, 2013 at 10:41:56AM +0100, Richard Biener wrote:
> For a "quick" GCC implementation of the builtins you could expand
> them to a open-coded sequence during gimplification. But due to
> the issues pointed out above I'm not sure it is the best interface
> to support (though now the names are taken).
I played around with gcc internals for the first time today and came
up with this. As this is my first patch to gcc I am very happy to hear
feedback. Thanks!
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index fba9c7d..7d01c91 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -134,7 +134,10 @@ DEF_PRIMITIVE_TYPE (BT_I8, builtin_type_for_size
(BITS_PER_UNIT*8, 1))
DEF_PRIMITIVE_TYPE (BT_I16, builtin_type_for_size (BITS_PER_UNIT*16, 1))
DEF_POINTER_TYPE (BT_PTR_CONST_STRING, BT_CONST_STRING)
+DEF_POINTER_TYPE (BT_PTR_UINT, BT_UINT)
DEF_POINTER_TYPE (BT_PTR_LONG, BT_LONG)
+DEF_POINTER_TYPE (BT_PTR_ULONG, BT_ULONG)
+DEF_POINTER_TYPE (BT_PTR_LONGLONG, BT_LONGLONG)
DEF_POINTER_TYPE (BT_PTR_ULONGLONG, BT_ULONGLONG)
DEF_POINTER_TYPE (BT_PTR_PTR, BT_PTR)
@@ -431,6 +434,21 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I8_INT, BT_VOID,
BT_VOLATILE_PTR, BT_I8, BT
DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I16_INT, BT_VOID, BT_VOLATILE_PTR,
BT_I16, BT_INT)
DEF_FUNCTION_TYPE_3 (BT_FN_INT_PTRPTR_SIZE_SIZE, BT_INT, BT_PTR_PTR, BT_SIZE,
BT_SIZE)
+DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_INT_INT_PTR_INT, BT_BOOL, BT_INT, BT_INT,
+ BT_INT_PTR)
+DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_LONG_LONG_PTR_LONG, BT_BOOL, BT_LONG, BT_LONG,
+ BT_PTR_LONG)
+DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_LONGLONG_LONGLONG_PTR_LONGLONG, BT_BOOL,
+ BT_LONGLONG, BT_LONGLONG, BT_PTR_LONGLONG)
+
+DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_UINT_UINT_PTR_UINT, BT_BOOL, BT_UINT, BT_UINT,
+ BT_PTR_UINT)
+DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_ULONG_ULONG_PTR_ULONG, BT_BOOL, BT_ULONG,
BT_ULONG,
+ BT_PTR_ULONG)
+DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_ULONGLONG_ULONGLONG_PTR_ULONGLONG, BT_BOOL,
+ BT_ULONGLONG, BT_ULONGLONG, BT_PTR_ULONGLONG)
+
+
DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
DEF_FUNCTION_TYPE_4 (BT_FN_INT_STRING_SIZE_CONST_STRING_VALIST_ARG,
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 5a76ba3..c1f5609 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -853,6 +853,37 @@ DEF_GCC_BUILTIN (BUILT_IN_FILE, "FILE",
BT_FN_CONST_STRING, ATTR_NOTHROW_LEAF_LI
DEF_GCC_BUILTIN (BUILT_IN_FUNCTION, "FUNCTION", BT_FN_CONST_STRING,
ATTR_NOTHROW_LEAF_LIST)
DEF_GCC_BUILTIN (BUILT_IN_LINE, "LINE", BT_FN_INT, ATTR_NOTHROW_LEAF_LIST)
+/* overflow builtins. */
+DEF_GCC_BUILTIN (BUILT_IN_UADD_OVERFLOW, "uadd_overflow",
+ BT_FN_BOOL_UINT_UINT_PTR_UINT, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_UADDL_OVERFLOW, "uaddl_overflow",
+ BT_FN_BOOL_ULONG_ULONG_PTR_ULONG, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_UADDLL_OVERFLOW, "uaddll_overflow",
+ BT_FN_BOOL_ULONGLONG_ULONGLONG_PTR_ULONGLONG,
+ ATTR_NOTHROW_LEAF_LIST)
+
+DEF_GCC_BUILTIN (BUILT_IN_USUB_OVERFLOW, "usub_overflow",
+ BT_FN_BOOL_UINT_UINT_PTR_UINT, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_USUBL_OVERFLOW, "usubl_overflow",
+ BT_FN_BOOL_ULONG_ULONG_PTR_ULONG, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_USUBLL_OVERFLOW, "usubll_overflow",
+ BT_FN_BOOL_ULONGLONG_ULONGLONG_PTR_ULONGLONG,
+ ATTR_NOTHROW_LEAF_LIST)
+
+DEF_GCC_BUILTIN (BUILT_IN_SADD_OVERFLOW, "sadd_overflow",
+ BT_FN_BOOL_INT_INT_PTR_INT, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_SADDL_OVERFLOW, "saddl_overflow",
+ BT_FN_BOOL_LONG_LONG_PTR_LONG, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_SADDLL_OVERFLOW, "saddll_overflow",
+ BT_FN_BOOL_LONGLONG_LONGLONG_PTR_LONGLONG,
ATTR_NOTHROW_LEAF_LIST)
+
+DEF_GCC_BUILTIN (BUILT_IN_SSUB_OVERFLOW, "ssub_overflow",
+ BT_FN_BOOL_INT_INT_PTR_INT, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_SSUBL_OVERFLOW, "ssubl_overflow",
+ BT_FN_BOOL_LONG_LONG_PTR_LONG, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_SSUBLL_OVERFLOW, "ssubll_overflow",
+ BT_FN_BOOL_LONGLONG_LONGLONG_PTR_LONGLONG,
ATTR_NOTHROW_LEAF_LIST)
+
/* Synchronization Primitives. */
#include "sync-builtins.def"
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 7441784..b4494a0 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -2224,6 +2224,138 @@ maybe_fold_stmt (gimple_stmt_iterator *gsi)
return fold_stmt (gsi);
}
+/* Is this an overflow builtin? */
+
+static bool
+builtin_overflow_p(tree fndecl)
+{
+ if (!fndecl)
+ return false;
+ else if (DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL)
+ return false;
+ else
+ switch (DECL_FUNCTION_CODE (fndecl))
+ {
+ case BUILT_IN_UADD_OVERFLOW:
+ case BUILT_IN_UADDL_OVERFLOW:
+ case BUILT_IN_UADDLL_OVERFLOW:
+ case BUILT_IN_SADD_OVERFLOW:
+ case BUILT_IN_SADDL_OVERFLOW:
+ case BUILT_IN_SADDLL_OVERFLOW:
+ case BUILT_IN_USUB_OVERFLOW:
+ case BUILT_IN_USUBL_OVERFLOW:
+ case BUILT_IN_USUBLL_OVERFLOW:
+ case BUILT_IN_SSUB_OVERFLOW:
+ case BUILT_IN_SSUBL_OVERFLOW:
+ case BUILT_IN_SSUBLL_OVERFLOW:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static tree
+gimplify_build_no_overflow_tree(enum tree_code tcode, tree res_p, tree arg1,
+ tree arg2)
+{
+ tree op, res, mod;
+
+ op = size_binop (tcode, arg1, arg2);
+ res = build2 (MEM_REF, TREE_TYPE (res_p), res_p,
+ build_int_cst (TREE_TYPE (res_p), 0));
+ mod = build2 (MODIFY_EXPR, TREE_TYPE (res), res, op);
+
+ return build2 (COMPOUND_EXPR, boolean_type_node, mod, boolean_false_node);
+}
+
+static enum gimplify_status
+gimplify_overflow_builtin(tree *expr_p)
+{
+ tree check, no_ov_tree;
+ tree arg1, arg2, res_p;
+ tree expr = *expr_p;
+ tree fndecl = get_callee_fndecl (expr);
+ location_t loc = EXPR_LOCATION(expr);
+
+ arg1 = CALL_EXPR_ARG(expr, 0);
+ arg2 = CALL_EXPR_ARG(expr, 1);
+ res_p = CALL_EXPR_ARG(expr, 2);
+
+
+ switch (DECL_FUNCTION_CODE (fndecl))
+ {
+ case BUILT_IN_SADD_OVERFLOW:
+ case BUILT_IN_SADDL_OVERFLOW:
+ case BUILT_IN_SADDLL_OVERFLOW:
+ no_ov_tree = gimplify_build_no_overflow_tree(PLUS_EXPR, res_p, arg1,
arg2);
+ check = build2 (TRUTH_ORIF_EXPR, boolean_type_node,
+ build2 (TRUTH_ANDIF_EXPR, boolean_type_node,
+ build2 (GT_EXPR, boolean_type_node,
+ arg2, build_int_cst (TREE_TYPE (arg2),
0)),
+ build2 (GT_EXPR, boolean_type_node,
+ arg1,
+ build2 (MINUS_EXPR, TREE_TYPE (arg2),
+ TYPE_MAX_VALUE (TREE_TYPE (arg2)),
+ arg2))),
+ build2 (TRUTH_ANDIF_EXPR, boolean_type_node,
+ build2 (LT_EXPR, boolean_type_node,
+ arg2, build_int_cst (TREE_TYPE (arg2),
0)),
+ build2 (LT_EXPR, boolean_type_node,
+ arg1,
+ build2 (MINUS_EXPR, TREE_TYPE (arg2),
+ TYPE_MIN_VALUE (TREE_TYPE (arg2)),
+ arg2))));
+ break;
+
+ case BUILT_IN_SSUB_OVERFLOW:
+ case BUILT_IN_SSUBL_OVERFLOW:
+ case BUILT_IN_SSUBLL_OVERFLOW:
+ no_ov_tree = gimplify_build_no_overflow_tree(MINUS_EXPR, res_p, arg1,
arg2);
+ check = build2 (TRUTH_ORIF_EXPR, boolean_type_node,
+ build2 (TRUTH_ANDIF_EXPR, boolean_type_node,
+ build2 (GT_EXPR, boolean_type_node,
+ arg2, build_int_cst (TREE_TYPE (arg2),
0)),
+ build2 (LT_EXPR, boolean_type_node,
+ arg1,
+ build2 (PLUS_EXPR, TREE_TYPE (arg2),
+ TYPE_MIN_VALUE (TREE_TYPE (arg2)),
+ arg2))),
+ build2 (TRUTH_ANDIF_EXPR, boolean_type_node,
+ build2 (LT_EXPR, boolean_type_node,
+ arg2, build_int_cst (TREE_TYPE (arg2),
0)),
+ build2 (GT_EXPR, boolean_type_node,
+ arg1,
+ build2 (PLUS_EXPR, TREE_TYPE (arg2),
+ TYPE_MAX_VALUE (TREE_TYPE (arg2)),
+ arg2))));
+ break;
+
+ case BUILT_IN_UADD_OVERFLOW:
+ case BUILT_IN_UADDL_OVERFLOW:
+ case BUILT_IN_UADDLL_OVERFLOW:
+ no_ov_tree = gimplify_build_no_overflow_tree(PLUS_EXPR, res_p, arg1,
arg2);
+ check = build2 (LT_EXPR, boolean_type_node,
+ build2 (MINUS_EXPR, TREE_TYPE (arg1),
+ TYPE_MAX_VALUE (TREE_TYPE (arg1)), arg1),
+ arg2);
+ break;
+
+ case BUILT_IN_USUB_OVERFLOW:
+ case BUILT_IN_USUBL_OVERFLOW:
+ case BUILT_IN_USUBLL_OVERFLOW:
+ no_ov_tree = gimplify_build_no_overflow_tree(MINUS_EXPR, res_p, arg1,
arg2);
+ check = build2 (LT_EXPR, boolean_type_node, arg1, arg2);
+ break;
+
+ default:
+ gcc_unreachable();
+ }
+
+ *expr_p = fold_build3_loc (loc, COND_EXPR, boolean_type_node, check,
+ boolean_true_node, no_ov_tree);
+ return GS_OK;
+}
+
/* Gimplify the CALL_EXPR node *EXPR_P into the GIMPLE sequence PRE_P.
WANT_VALUE is true if the result of the call is desired. */
@@ -2300,6 +2432,10 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p,
bool want_value)
default:
;
}
+
+ if (builtin_overflow_p(fndecl))
+ return gimplify_overflow_builtin(expr_p);
+
if (fndecl && DECL_BUILT_IN (fndecl))
{
tree new_tree = fold_call_expr (input_location, *expr_p, !want_value);
diff --git a/gcc/testsuite/gcc.dg/builtin-overflow-1.c
b/gcc/testsuite/gcc.dg/builtin-overflow-1.c
new file mode 100644
index 0000000..3bbcaa0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-overflow-1.c
@@ -0,0 +1,158 @@
+/* { dg-do run } */
+/* { dg-options "-Wall -O2 -std=gnu11" } */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+
+#define SADD_IMPL(POSTFIX, TYPE, TYPE_CAPS) \
+ static _Bool sadd ## POSTFIX (TYPE a, TYPE b, TYPE *c) \
+ { \
+ if (((b > 0) && (a > (TYPE_CAPS ## _MAX - b))) \
+ || ((b < 0) && (a < (TYPE_CAPS ## _MIN - b)))) \
+ { \
+ return 1; \
+ } \
+ else \
+ { \
+ *c = a + b; \
+ return 0; \
+ } \
+ }
+
+#define UADD_IMPL(POSTFIX, TYPE, TYPE_CAPS) \
+ static _Bool uadd ## POSTFIX (TYPE a, TYPE b, TYPE *c) \
+ { \
+ if (TYPE_CAPS ## _MAX - a < b) \
+ { \
+ return 1; \
+ } \
+ else \
+ { \
+ *c = a + b; \
+ return 0; \
+ } \
+ }
+
+#define SSUB_IMPL(POSTFIX, TYPE, TYPE_CAPS) \
+ static _Bool ssub ## POSTFIX (TYPE a, TYPE b, TYPE *c) \
+ { \
+ if ((b > 0 && a < TYPE_CAPS ## _MIN + b) \
+ || (b < 0 && a > TYPE_CAPS ## _MAX + b)) \
+ { \
+ return 1; \
+ } \
+ else \
+ { \
+ *c = a - b; \
+ return 0; \
+ } \
+ }
+
+#define USUB_IMPL(POSTFIX, TYPE, TYPE_CAPS) \
+ static _Bool usub ## POSTFIX (TYPE a, TYPE b, TYPE *c) \
+ { \
+ if (a < b) \
+ { \
+ return 1; \
+ } \
+ else \
+ { \
+ *c = a - b; \
+ return 0; \
+ } \
+ }
+
+SADD_IMPL (, int, INT)
+SADD_IMPL (l, long, LONG)
+SADD_IMPL (ll, long long, LLONG)
+UADD_IMPL (, unsigned int, UINT)
+UADD_IMPL (l, unsigned long, ULONG)
+UADD_IMPL (ll, unsigned long long, ULLONG)
+SSUB_IMPL (, int, INT)
+SSUB_IMPL (l, long, LONG)
+SSUB_IMPL (ll, long long, LLONG)
+USUB_IMPL (, unsigned int, INT)
+USUB_IMPL (l, unsigned long, ULONG)
+USUB_IMPL (ll, unsigned long long, ULLONG)
+
+#define TESTOV(OP, arg1, arg2) do { \
+ __label__ err; \
+ typeof(arg1) _arg1 = (arg1); \
+ typeof(arg2) _arg2 = (arg2); \
+ (void)(&_arg1 == &_arg2); \
+ \
+ typeof(_arg1) res, res_ref; \
+ _Bool ov, ov_ref; \
+ \
+ ov = __builtin_ ## OP ## _overflow(_arg1, _arg2, &res); \
+ ov_ref = OP (_arg1, _arg2, &res_ref); \
+ if (ov != ov_ref) \
+ goto err; \
+ if (ov) \
+ break; \
+ if (res == res_ref) \
+ break; \
+err: \
+ fputs("error: " #OP " " #arg1 " " #arg2 "\n", stderr); \
+ abort(); \
+ } while (0)
+
+#define TESTOV_COM(OP, arg1, arg2) do { \
+ TESTOV (OP, arg1, arg2); \
+ if (arg1 != arg2) \
+ TESTOV (OP, arg2,arg1); \
+ } while (0)
+
+#define CAST(x) ((typeof(type_marker))(x))
+#define ONE (CAST(1))
+#define ZERO (CAST(0))
+#define MINUS_ONE (CAST(-1))
+
+#define SIGNED_TESTCASES(OP, TYPE) do { \
+ typeof(TYPE ## _MAX) type_marker = 0; \
+ TESTOV_COM (OP, MINUS_ONE, MINUS_ONE); \
+ TESTOV_COM (OP, MINUS_ONE, ONE); \
+ TESTOV_COM (OP, MINUS_ONE, ZERO); \
+ TESTOV_COM (OP, MINUS_ONE, TYPE ## _MAX); \
+ TESTOV_COM (OP, MINUS_ONE, TYPE ## _MIN); \
+ TESTOV_COM (OP, ONE, ONE); \
+ TESTOV_COM (OP, ONE, ZERO); \
+ TESTOV_COM (OP, ONE, TYPE ## _MAX); \
+ TESTOV_COM (OP, ONE, TYPE ## _MIN); \
+ TESTOV_COM (OP, ZERO, ZERO); \
+ TESTOV_COM (OP, ZERO, TYPE ## _MAX); \
+ TESTOV_COM (OP, ZERO, TYPE ## _MIN); \
+ TESTOV_COM (OP, TYPE ## _MAX, TYPE ## _MAX); \
+ TESTOV_COM (OP, TYPE ## _MAX, TYPE ## _MIN); \
+ TESTOV_COM (OP, TYPE ## _MIN, TYPE ## _MIN); \
+ } while (0)
+
+#define UNSIGNED_TESTCASES(OP, TYPE) do { \
+ typeof(TYPE ## _MAX) type_marker = 0; \
+ TESTOV_COM (OP, ONE, ONE); \
+ TESTOV_COM (OP, ONE, ZERO); \
+ TESTOV_COM (OP, ONE, TYPE ## _MAX); \
+ TESTOV_COM (OP, ZERO, ZERO); \
+ TESTOV_COM (OP, ZERO, TYPE ## _MAX); \
+ TESTOV_COM (OP, TYPE ## _MAX, TYPE ## _MAX); \
+ } while (0)
+
+int main(int argc, char **argv)
+{
+ SIGNED_TESTCASES(sadd, INT);
+ SIGNED_TESTCASES(saddl, LONG);
+ SIGNED_TESTCASES(saddll, LLONG);
+
+ SIGNED_TESTCASES(ssub, INT);
+ SIGNED_TESTCASES(ssubl, LONG);
+ SIGNED_TESTCASES(ssubll, LLONG);
+
+ UNSIGNED_TESTCASES(uadd, UINT);
+ UNSIGNED_TESTCASES(uaddl, ULONG);
+ UNSIGNED_TESTCASES(uaddll, ULLONG);
+
+ UNSIGNED_TESTCASES(usub, UINT);
+ UNSIGNED_TESTCASES(usubl, ULONG);
+ UNSIGNED_TESTCASES(usubll, ULLONG);
+}