Commit 63d1e46d by Ian Lance Taylor Committed by Ian Lance Taylor

compiler, runtime: Add explicit checks for zero and overflow division.

	* lang.opt: Add -fgo-check-divide-zero and
	-fgo-check-divide-overflow.
	* gccgo.texi (Invoking gccgo): Document new options.

From-SVN: r186637
parent 0e27a180
2012-04-20 Ian Lance Taylor <iant@google.com>
* lang.opt: Add -fgo-check-divide-zero and
-fgo-check-divide-overflow.
* gccgo.texi (Invoking gccgo): Document new options.
2012-04-18 Steven Bosscher <steven@gcc.gnu.org> 2012-04-18 Steven Bosscher <steven@gcc.gnu.org>
* go-gcc.cc (Gcc_backend::switch_statement): Build SWITCH_EXPR * go-gcc.cc (Gcc_backend::switch_statement): Build SWITCH_EXPR
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
@include gcc-common.texi @include gcc-common.texi
@c Copyright years for this manual. @c Copyright years for this manual.
@set copyrights-go 2010 @set copyrights-go 2010, 2011, 2012
@copying @copying
@c man begin COPYRIGHT @c man begin COPYRIGHT
...@@ -174,6 +174,31 @@ By default @command{gccgo} will warn about functions which have one or ...@@ -174,6 +174,31 @@ By default @command{gccgo} will warn about functions which have one or
more return parameters but lack an explicit @code{return} statement. more return parameters but lack an explicit @code{return} statement.
This warning may be disabled using This warning may be disabled using
@option{-fno-require-return-statement}. @option{-fno-require-return-statement}.
@item -fgo-check-divide-zero
@cindex @option{-fgo-check-divide-zero}
@cindex @option{-fno-go-check-divide-zero}
Add explicit checks for division by zero. In Go a division (or
modulos) by zero causes a panic. On Unix systems this is detected in
the runtime by catching the @code{SIGFPE} signal. Some processors,
such as PowerPC, do not generate a SIGFPE on division by zero. Some
runtimes do not generate a signal that can be caught. On those
systems, this option may be used. Or the checks may be removed via
@option{-fno-go-check-divide-zero}. This option is currently on by
default, but in the future may be off by default on systems that do
not require it.
@item -fgo-check-divide-overflow
@cindex @option{-fgo-check-divide-overflow}
@cindex @option{-fno-go-check-divide-overflow}
Add explicit checks for division overflow. For example, division
overflow occurs when computing @code{INT_MIN / -1}. In Go this should
be wrapped, to produce @code{INT_MIN}. Some processors, such as x86,
generate a trap on division overflow. On those systems, this option
may be used. Or the checks may be removed via
@option{-fno-go-check-divide-overflow}. This option is currently on
by default, but in the future may be off by default on systems that do
not require it.
@end table @end table
@c man end @c man end
......
...@@ -5647,6 +5647,7 @@ Binary_expression::do_get_tree(Translate_context* context) ...@@ -5647,6 +5647,7 @@ Binary_expression::do_get_tree(Translate_context* context)
enum tree_code code; enum tree_code code;
bool use_left_type = true; bool use_left_type = true;
bool is_shift_op = false; bool is_shift_op = false;
bool is_idiv_op = false;
switch (this->op_) switch (this->op_)
{ {
case OPERATOR_EQEQ: case OPERATOR_EQEQ:
...@@ -5689,11 +5690,15 @@ Binary_expression::do_get_tree(Translate_context* context) ...@@ -5689,11 +5690,15 @@ Binary_expression::do_get_tree(Translate_context* context)
if (t->float_type() != NULL || t->complex_type() != NULL) if (t->float_type() != NULL || t->complex_type() != NULL)
code = RDIV_EXPR; code = RDIV_EXPR;
else else
{
code = TRUNC_DIV_EXPR; code = TRUNC_DIV_EXPR;
is_idiv_op = true;
}
} }
break; break;
case OPERATOR_MOD: case OPERATOR_MOD:
code = TRUNC_MOD_EXPR; code = TRUNC_MOD_EXPR;
is_idiv_op = true;
break; break;
case OPERATOR_LSHIFT: case OPERATOR_LSHIFT:
code = LSHIFT_EXPR; code = LSHIFT_EXPR;
...@@ -5714,6 +5719,7 @@ Binary_expression::do_get_tree(Translate_context* context) ...@@ -5714,6 +5719,7 @@ Binary_expression::do_get_tree(Translate_context* context)
go_unreachable(); go_unreachable();
} }
location_t gccloc = this->location().gcc_location();
tree type = use_left_type ? TREE_TYPE(left) : TREE_TYPE(right); tree type = use_left_type ? TREE_TYPE(left) : TREE_TYPE(right);
if (this->left_->type()->is_string_type()) if (this->left_->type()->is_string_type())
...@@ -5741,28 +5747,27 @@ Binary_expression::do_get_tree(Translate_context* context) ...@@ -5741,28 +5747,27 @@ Binary_expression::do_get_tree(Translate_context* context)
} }
tree eval_saved = NULL_TREE; tree eval_saved = NULL_TREE;
if (is_shift_op) if (is_shift_op
|| (is_idiv_op && (go_check_divide_zero || go_check_divide_overflow)))
{ {
// Make sure the values are evaluated. // Make sure the values are evaluated.
if (!DECL_P(left) && TREE_SIDE_EFFECTS(left)) if (!DECL_P(left))
{ {
left = save_expr(left); left = save_expr(left);
eval_saved = left; eval_saved = left;
} }
if (!DECL_P(right) && TREE_SIDE_EFFECTS(right)) if (!DECL_P(right))
{ {
right = save_expr(right); right = save_expr(right);
if (eval_saved == NULL_TREE) if (eval_saved == NULL_TREE)
eval_saved = right; eval_saved = right;
else else
eval_saved = fold_build2_loc(this->location().gcc_location(), eval_saved = fold_build2_loc(gccloc, COMPOUND_EXPR,
COMPOUND_EXPR,
void_type_node, eval_saved, right); void_type_node, eval_saved, right);
} }
} }
tree ret = fold_build2_loc(this->location().gcc_location(), tree ret = fold_build2_loc(gccloc, code,
code,
compute_type != NULL_TREE ? compute_type : type, compute_type != NULL_TREE ? compute_type : type,
left, right); left, right);
...@@ -5780,39 +5785,116 @@ Binary_expression::do_get_tree(Translate_context* context) ...@@ -5780,39 +5785,116 @@ Binary_expression::do_get_tree(Translate_context* context)
tree compare = fold_build2(LT_EXPR, boolean_type_node, right, tree compare = fold_build2(LT_EXPR, boolean_type_node, right,
build_int_cst_type(TREE_TYPE(right), bits)); build_int_cst_type(TREE_TYPE(right), bits));
tree overflow_result = fold_convert_loc(this->location().gcc_location(), tree overflow_result = fold_convert_loc(gccloc, TREE_TYPE(left),
TREE_TYPE(left),
integer_zero_node); integer_zero_node);
if (this->op_ == OPERATOR_RSHIFT if (this->op_ == OPERATOR_RSHIFT
&& !this->left_->type()->integer_type()->is_unsigned()) && !this->left_->type()->integer_type()->is_unsigned())
{ {
tree neg = tree neg =
fold_build2_loc(this->location().gcc_location(), LT_EXPR, fold_build2_loc(gccloc, LT_EXPR, boolean_type_node,
boolean_type_node, left, left,
fold_convert_loc(this->location().gcc_location(), fold_convert_loc(gccloc, TREE_TYPE(left),
TREE_TYPE(left),
integer_zero_node)); integer_zero_node));
tree neg_one = tree neg_one =
fold_build2_loc(this->location().gcc_location(), fold_build2_loc(gccloc, MINUS_EXPR, TREE_TYPE(left),
MINUS_EXPR, TREE_TYPE(left), fold_convert_loc(gccloc, TREE_TYPE(left),
fold_convert_loc(this->location().gcc_location(),
TREE_TYPE(left),
integer_zero_node), integer_zero_node),
fold_convert_loc(this->location().gcc_location(), fold_convert_loc(gccloc, TREE_TYPE(left),
TREE_TYPE(left),
integer_one_node)); integer_one_node));
overflow_result = overflow_result =
fold_build3_loc(this->location().gcc_location(), COND_EXPR, fold_build3_loc(gccloc, COND_EXPR, TREE_TYPE(left),
TREE_TYPE(left), neg, neg_one, neg, neg_one, overflow_result);
overflow_result); }
ret = fold_build3_loc(gccloc, COND_EXPR, TREE_TYPE(left),
compare, ret, overflow_result);
if (eval_saved != NULL_TREE)
ret = fold_build2_loc(gccloc, COMPOUND_EXPR, TREE_TYPE(ret),
eval_saved, ret);
}
// Add checks for division by zero and division overflow as needed.
if (is_idiv_op)
{
if (go_check_divide_zero)
{
// right == 0
tree check = fold_build2_loc(gccloc, EQ_EXPR, boolean_type_node,
right,
fold_convert_loc(gccloc,
TREE_TYPE(right),
integer_zero_node));
// __go_runtime_error(RUNTIME_ERROR_DIVISION_BY_ZERO), 0
int errcode = RUNTIME_ERROR_DIVISION_BY_ZERO;
tree panic = fold_build2_loc(gccloc, COMPOUND_EXPR, TREE_TYPE(ret),
Gogo::runtime_error(errcode,
this->location()),
fold_convert_loc(gccloc, TREE_TYPE(ret),
integer_zero_node));
// right == 0 ? (__go_runtime_error(...), 0) : ret
ret = fold_build3_loc(gccloc, COND_EXPR, TREE_TYPE(ret),
check, panic, ret);
} }
ret = fold_build3_loc(this->location().gcc_location(), COND_EXPR, if (go_check_divide_overflow)
TREE_TYPE(left), compare, ret, overflow_result); {
// right == -1
// FIXME: It would be nice to say that this test is expected
// to return false.
tree m1 = integer_minus_one_node;
tree check = fold_build2_loc(gccloc, EQ_EXPR, boolean_type_node,
right,
fold_convert_loc(gccloc,
TREE_TYPE(right),
m1));
tree overflow;
if (TYPE_UNSIGNED(TREE_TYPE(ret)))
{
// An unsigned -1 is the largest possible number, so
// dividing is always 1 or 0.
tree cmp = fold_build2_loc(gccloc, EQ_EXPR, boolean_type_node,
left, right);
if (this->op_ == OPERATOR_DIV)
overflow = fold_build3_loc(gccloc, COND_EXPR, TREE_TYPE(ret),
cmp,
fold_convert_loc(gccloc,
TREE_TYPE(ret),
integer_one_node),
fold_convert_loc(gccloc,
TREE_TYPE(ret),
integer_zero_node));
else
overflow = fold_build3_loc(gccloc, COND_EXPR, TREE_TYPE(ret),
cmp,
fold_convert_loc(gccloc,
TREE_TYPE(ret),
integer_zero_node),
left);
}
else
{
// Computing left / -1 is the same as computing - left,
// which does not overflow since Go sets -fwrapv.
if (this->op_ == OPERATOR_DIV)
overflow = fold_build1_loc(gccloc, NEGATE_EXPR, TREE_TYPE(left),
left);
else
overflow = integer_zero_node;
}
overflow = fold_convert_loc(gccloc, TREE_TYPE(ret), overflow);
// right == -1 ? - left : ret
ret = fold_build3_loc(gccloc, COND_EXPR, TREE_TYPE(ret),
check, overflow, ret);
}
if (eval_saved != NULL_TREE) if (eval_saved != NULL_TREE)
ret = fold_build2_loc(this->location().gcc_location(), COMPOUND_EXPR, ret = fold_build2_loc(gccloc, COMPOUND_EXPR, TREE_TYPE(ret),
TREE_TYPE(ret), eval_saved, ret); eval_saved, ret);
} }
return ret; return ret;
......
...@@ -2791,6 +2791,9 @@ static const int RUNTIME_ERROR_MAKE_MAP_OUT_OF_BOUNDS = 8; ...@@ -2791,6 +2791,9 @@ static const int RUNTIME_ERROR_MAKE_MAP_OUT_OF_BOUNDS = 8;
// Channel capacity out of bounds in make: negative or overflow. // Channel capacity out of bounds in make: negative or overflow.
static const int RUNTIME_ERROR_MAKE_CHAN_OUT_OF_BOUNDS = 9; static const int RUNTIME_ERROR_MAKE_CHAN_OUT_OF_BOUNDS = 9;
// Division by zero.
static const int RUNTIME_ERROR_DIVISION_BY_ZERO = 10;
// This is used by some of the langhooks. // This is used by some of the langhooks.
extern Gogo* go_get_gogo(); extern Gogo* go_get_gogo();
......
; lang.opt -- Options for the gcc Go front end. ; lang.opt -- Options for the gcc Go front end.
; Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc. ; Copyright (C) 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
; ;
; This file is part of GCC. ; This file is part of GCC.
; ;
...@@ -37,6 +37,14 @@ Wall ...@@ -37,6 +37,14 @@ Wall
Go Go
; Documented in c.opt ; Documented in c.opt
fgo-check-divide-zero
Go Var(go_check_divide_zero) Init(1)
Add explicit checks for division by zero
fgo-check-divide-overflow
Go Var(go_check_divide_overflow) Init(1)
Add explicit checks for division overflow in INT_MIN / -1
fgo-dump- fgo-dump-
Go Joined RejectNegative Go Joined RejectNegative
-fgo-dump-<type> Dump Go frontend internal information -fgo-dump-<type> Dump Go frontend internal information
......
...@@ -46,7 +46,10 @@ enum ...@@ -46,7 +46,10 @@ enum
MAKE_MAP_OUT_OF_BOUNDS = 8, MAKE_MAP_OUT_OF_BOUNDS = 8,
/* Channel capacity out of bounds in make: negative or overflow. */ /* Channel capacity out of bounds in make: negative or overflow. */
MAKE_CHAN_OUT_OF_BOUNDS = 9 MAKE_CHAN_OUT_OF_BOUNDS = 9,
/* Integer division by zero. */
DIVISION_BY_ZERO = 10
}; };
extern void __go_runtime_error () __attribute__ ((noreturn)); extern void __go_runtime_error () __attribute__ ((noreturn));
...@@ -78,6 +81,9 @@ __go_runtime_error (int i) ...@@ -78,6 +81,9 @@ __go_runtime_error (int i)
case MAKE_CHAN_OUT_OF_BOUNDS: case MAKE_CHAN_OUT_OF_BOUNDS:
runtime_panicstring ("make chan len out of range"); runtime_panicstring ("make chan len out of range");
case DIVISION_BY_ZERO:
runtime_panicstring ("integer divide by zero");
default: default:
runtime_panicstring ("unknown runtime error"); runtime_panicstring ("unknown runtime error");
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment