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); +}