#include "rust-compile-block.h"
#include "rust-compile-implitem.h"
#include "rust-constexpr.h"
+#include "rust-gcc.h"
#include "fold-const.h"
#include "realmpfr.h"
return;
}
- translated
- = ctx->get_backend ()->arithmetic_or_logical_expression (op, lhs, rhs,
- expr.get_locus ());
+ if (ctx->in_fn () && !ctx->const_context_p ())
+ {
+ auto receiver_tmp = NULL_TREE;
+ auto receiver
+ = ctx->get_backend ()->temporary_variable (ctx->peek_fn ().fndecl,
+ NULL_TREE, TREE_TYPE (lhs),
+ lhs, true, expr.get_locus (),
+ &receiver_tmp);
+ auto check
+ = ctx->get_backend ()->arithmetic_or_logical_expression_checked (
+ op, lhs, rhs, expr.get_locus (), receiver);
+
+ ctx->add_statement (check);
+ translated = receiver->get_tree (expr.get_locus ());
+ }
+ else
+ {
+ translated = ctx->get_backend ()->arithmetic_or_logical_expression (
+ op, lhs, rhs, expr.get_locus ());
+ }
}
void
return;
}
- auto operator_expr
- = ctx->get_backend ()->arithmetic_or_logical_expression (op, lhs, rhs,
- expr.get_locus ());
- tree assignment
- = ctx->get_backend ()->assignment_statement (lhs, operator_expr,
- expr.get_locus ());
- ctx->add_statement (assignment);
+ if (ctx->in_fn () && !ctx->const_context_p ())
+ {
+ auto tmp = NULL_TREE;
+ auto receiver
+ = ctx->get_backend ()->temporary_variable (ctx->peek_fn ().fndecl,
+ NULL_TREE, TREE_TYPE (lhs),
+ lhs, true, expr.get_locus (),
+ &tmp);
+ auto check
+ = ctx->get_backend ()->arithmetic_or_logical_expression_checked (
+ op, lhs, rhs, expr.get_locus (), receiver);
+ ctx->add_statement (check);
+
+ translated = ctx->get_backend ()->assignment_statement (
+ lhs, receiver->get_tree (expr.get_locus ()), expr.get_locus ());
+ }
+ else
+ {
+ translated = ctx->get_backend ()->arithmetic_or_logical_expression (
+ op, lhs, rhs, expr.get_locus ());
+ }
}
void
return error_mark_node;
}
+ ctx->push_const_context ();
tree capacity_expr = CompileExpr::Compile (elems.get_num_copies_expr (), ctx);
+ ctx->pop_const_context ();
+
if (!TREE_CONSTANT (capacity_expr))
{
rust_error_at (expr_locus, "non const num copies %qT", array_type);
// Supported values of OP are enumerated in ArithmeticOrLogicalOperator.
virtual tree arithmetic_or_logical_expression (ArithmeticOrLogicalOperator op,
tree left, tree right,
- Location)
+ Location loc)
+ = 0;
+
+ // Return an expression for the operation LEFT OP RIGHT.
+ // Supported values of OP are enumerated in ArithmeticOrLogicalOperator.
+ // This function adds overflow checking and returns a list of statements to
+ // add to the current function context. The `receiver` variable refers to the
+ // variable which will contain the result of that operation.
+ virtual tree
+ arithmetic_or_logical_expression_checked (ArithmeticOrLogicalOperator op,
+ tree left, tree right, Location loc,
+ Bvariable *receiver)
= 0;
// Return an expression for the operation LEFT OP RIGHT.
// Supported values of OP are enumerated in ComparisonOperator.
virtual tree comparison_expression (ComparisonOperator op, tree left,
- tree right, Location)
+ tree right, Location loc)
= 0;
// Return an expression for the operation LEFT OP RIGHT.
// variable, and may not be very useful. This function should
// return a variable which can be referenced later and should set
// *PSTATEMENT to a statement which initializes the variable.
- virtual Bvariable *temporary_variable (tree, tree, tree, tree init,
- bool address_is_taken,
+ virtual Bvariable *temporary_variable (tree fndecl, tree bind_tree, tree type,
+ tree init, bool address_is_taken,
Location location, tree *pstatement)
= 0;
#include "rust-gcc.h"
#include "backend/rust-tree.h"
+#include "backend/rust-builtins.h"
// Get the tree of a variable for use as an expression. If this is a
// zero-sized global, create an expression that refers to the decl but
tree arithmetic_or_logical_expression (ArithmeticOrLogicalOperator op,
tree left, tree right, Location);
+ tree arithmetic_or_logical_expression_checked (ArithmeticOrLogicalOperator op,
+ tree left, tree right,
+ Location, Bvariable *receiver);
+
tree comparison_expression (ComparisonOperator op, tree left, tree right,
Location);
return new_tree;
}
-// Return an expression for the arithmetic or logical operation LEFT OP RIGHT.
tree
Gcc_backend::arithmetic_or_logical_expression (ArithmeticOrLogicalOperator op,
- tree left_tree, tree right_tree,
+ tree left, tree right,
Location location)
{
/* Check if either expression is an error, in which case we return an error
expression. */
- if (left_tree == error_mark_node || right_tree == error_mark_node)
+ if (left == error_mark_node || right == error_mark_node)
return error_mark_node;
/* We need to determine if we're doing floating point arithmetics of integer
arithmetics. */
- bool floating_point = is_floating_point (left_tree);
+ bool floating_point = is_floating_point (left);
+ auto ret = NULL_TREE;
/* For arithmetic or logical operators, the resulting type should be the same
as the lhs operand. */
- auto tree_type = TREE_TYPE (left_tree);
+ auto tree_type = TREE_TYPE (left);
auto original_type = tree_type;
+ auto loc = location.gcc_location ();
auto tree_code = operator_to_tree_code (op, floating_point);
/* For floating point operations we may need to extend the precision of type.
extended_type = excess_precision_type (tree_type);
if (extended_type != NULL_TREE)
{
- left_tree = convert (extended_type, left_tree);
- right_tree = convert (extended_type, right_tree);
+ left = convert (extended_type, left);
+ right = convert (extended_type, right);
tree_type = extended_type;
}
}
- /* Construct a new tree and build an expression from it. */
- auto new_tree = fold_build2_loc (location.gcc_location (), tree_code,
- tree_type, left_tree, right_tree);
- TREE_CONSTANT (new_tree)
- = TREE_CONSTANT (left_tree) && TREE_CONSTANT (right_tree);
+ ret = fold_build2_loc (loc, tree_code, tree_type, left, right);
+ TREE_CONSTANT (ret) = TREE_CONSTANT (left) & TREE_CONSTANT (right);
+ // TODO: How do we handle floating point?
if (floating_point && extended_type != NULL_TREE)
- new_tree = convert (original_type, new_tree);
- return new_tree;
+ ret = convert (original_type, ret);
+
+ return ret;
+}
+
+static bool
+is_overflowing_expr (ArithmeticOrLogicalOperator op)
+{
+ switch (op)
+ {
+ case ArithmeticOrLogicalOperator::ADD:
+ case ArithmeticOrLogicalOperator::SUBTRACT:
+ case ArithmeticOrLogicalOperator::MULTIPLY:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static std::pair<tree, tree>
+fetch_overflow_builtins (ArithmeticOrLogicalOperator op)
+{
+ auto builtin_ctx = Rust::Compile::BuiltinsContext::get ();
+
+ auto builtin = NULL_TREE;
+ auto abort = NULL_TREE;
+
+ switch (op)
+ {
+ case ArithmeticOrLogicalOperator::ADD:
+ builtin_ctx.lookup_simple_builtin ("add_overflow", &builtin);
+ break;
+ case ArithmeticOrLogicalOperator::SUBTRACT:
+ builtin_ctx.lookup_simple_builtin ("sub_overflow", &builtin);
+ break;
+ case ArithmeticOrLogicalOperator::MULTIPLY:
+ builtin_ctx.lookup_simple_builtin ("mul_overflow", &builtin);
+ break;
+ default:
+ gcc_unreachable ();
+ break;
+ };
+
+ builtin_ctx.lookup_simple_builtin ("abort", &abort);
+
+ rust_assert (abort);
+ rust_assert (builtin);
+
+ // FIXME: ARTHUR: This is really ugly. The builtin context should take care of
+ // that
+ TREE_SIDE_EFFECTS (abort) = 1;
+ TREE_READONLY (abort) = 0;
+
+ // FIXME: ARTHUR: Same here. Remove these!
+ TREE_SIDE_EFFECTS (builtin) = 1;
+ TREE_READONLY (builtin) = 0;
+
+ return {abort, builtin};
+}
+
+// Return an expression for the arithmetic or logical operation LEFT OP RIGHT
+// with overflow checking when possible
+tree
+Gcc_backend::arithmetic_or_logical_expression_checked (
+ ArithmeticOrLogicalOperator op, tree left, tree right, Location location,
+ Bvariable *receiver_var)
+{
+ /* Check if either expression is an error, in which case we return an error
+ expression. */
+ if (left == error_mark_node || right == error_mark_node)
+ return error_mark_node;
+
+ auto loc = location.gcc_location ();
+
+ // FIXME: Add `if (!debug_mode)`
+ // No overflow checks for floating point operations or divisions. In that
+ // case, simply assign the result of the operation to the receiver variable
+ if (is_floating_point (left) || !is_overflowing_expr (op))
+ return assignment_statement (
+ receiver_var->get_tree (location),
+ arithmetic_or_logical_expression (op, left, right, location), location);
+
+ auto receiver = receiver_var->get_tree (location);
+ TREE_ADDRESSABLE (receiver) = 1;
+ auto result_ref = build_fold_addr_expr_loc (loc, receiver);
+
+ auto builtins = fetch_overflow_builtins (op);
+ auto abort = builtins.first;
+ auto builtin = builtins.second;
+
+ auto abort_call = build_call_expr_loc (loc, abort, 0);
+
+ // FIXME: ARTHUR: Is that needed?
+ TREE_SIDE_EFFECTS (abort_call) = 1;
+ TREE_READONLY (abort_call) = 0;
+
+ auto builtin_call
+ = build_call_expr_loc (loc, builtin, 3, left, right, result_ref);
+ auto overflow_check
+ = build2_loc (loc, EQ_EXPR, boolean_type_node, builtin_call,
+ boolean_constant_expression (true));
+
+ auto if_block = build3_loc (loc, COND_EXPR, void_type_node, overflow_check,
+ abort_call, NULL_TREE);
+
+ // FIXME: ARTHUR: Needed?
+ TREE_SIDE_EFFECTS (if_block) = 1;
+ TREE_READONLY (if_block) = 0;
+
+ return if_block;
}
// Return an expression for the comparison operation LEFT OP RIGHT.