216 lines
5.6 KiB
C++
216 lines
5.6 KiB
C++
/* D-specific tree lowering bits; see also gimple.c.
|
|
Copyright (C) 2020-2021 Free Software Foundation, Inc.
|
|
|
|
GCC is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
GCC is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GCC; see the file COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
|
|
#include "dmd/globals.h"
|
|
|
|
#include "tree.h"
|
|
#include "gimple-expr.h"
|
|
#include "gimplify.h"
|
|
|
|
#include "d-tree.h"
|
|
|
|
|
|
/* Return TRUE if an operand OP of a given TYPE being copied has no data.
|
|
The middle-end does a similar check with zero sized types. */
|
|
|
|
static bool
|
|
empty_modify_p (tree type, tree op)
|
|
{
|
|
tree_code code = TREE_CODE (op);
|
|
switch (code)
|
|
{
|
|
case COMPOUND_EXPR:
|
|
return empty_modify_p (type, TREE_OPERAND (op, 1));
|
|
|
|
case CONSTRUCTOR:
|
|
/* Non-empty construcors are valid. */
|
|
if (CONSTRUCTOR_NELTS (op) != 0 || TREE_CLOBBER_P (op))
|
|
return false;
|
|
break;
|
|
|
|
case CALL_EXPR:
|
|
/* Leave nrvo alone because it isn't a copy. */
|
|
if (CALL_EXPR_RETURN_SLOT_OPT (op))
|
|
return false;
|
|
break;
|
|
|
|
default:
|
|
/* If the operand doesn't have a simple form. */
|
|
if (!is_gimple_lvalue (op) && !INDIRECT_REF_P (op))
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
return empty_aggregate_p (type);
|
|
}
|
|
|
|
/* Gimplify assignment from an INIT_EXPR or MODIFY_EXPR. */
|
|
|
|
static gimplify_status
|
|
d_gimplify_modify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
|
|
{
|
|
tree op0 = TREE_OPERAND (*expr_p, 0);
|
|
tree op1 = TREE_OPERAND (*expr_p, 1);
|
|
|
|
if (error_operand_p (op0) || error_operand_p (op1))
|
|
return GS_UNHANDLED;
|
|
|
|
/* Remove any copies of empty aggregates. */
|
|
if (empty_modify_p (TREE_TYPE (op0), op1))
|
|
{
|
|
gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
|
|
is_gimple_lvalue, fb_lvalue);
|
|
|
|
if (TREE_SIDE_EFFECTS (op1))
|
|
gimplify_and_add (op1, pre_p);
|
|
|
|
*expr_p = TREE_OPERAND (*expr_p, 0);
|
|
return GS_OK;
|
|
}
|
|
|
|
/* If the back end isn't clever enough to know that the lhs and rhs
|
|
types are the same, add an explicit conversion. */
|
|
if ((AGGREGATE_TYPE_P (TREE_TYPE (op0)) || AGGREGATE_TYPE_P (TREE_TYPE (op1)))
|
|
&& !useless_type_conversion_p (TREE_TYPE (op1), TREE_TYPE (op0)))
|
|
{
|
|
TREE_OPERAND (*expr_p, 1) = build1 (VIEW_CONVERT_EXPR,
|
|
TREE_TYPE (op0), op1);
|
|
return GS_OK;
|
|
}
|
|
|
|
return GS_UNHANDLED;
|
|
}
|
|
|
|
/* Gimplify an ADDR_EXPR node. */
|
|
|
|
static gimplify_status
|
|
d_gimplify_addr_expr (tree *expr_p)
|
|
{
|
|
tree op0 = TREE_OPERAND (*expr_p, 0);
|
|
/* Constructors are not lvalues, so make them one. */
|
|
if (TREE_CODE (op0) == CONSTRUCTOR)
|
|
{
|
|
TREE_OPERAND (*expr_p, 0) = force_target_expr (op0);
|
|
return GS_OK;
|
|
}
|
|
|
|
return GS_UNHANDLED;
|
|
}
|
|
|
|
/* Gimplify a CALL_EXPR node. */
|
|
|
|
static gimplify_status
|
|
d_gimplify_call_expr (tree *expr_p, gimple_seq *pre_p)
|
|
{
|
|
if (CALL_EXPR_ARGS_ORDERED (*expr_p))
|
|
{
|
|
/* Strictly evaluate all arguments from left to right. */
|
|
int nargs = call_expr_nargs (*expr_p);
|
|
location_t loc = EXPR_LOC_OR_LOC (*expr_p, input_location);
|
|
|
|
/* No need to enforce evaluation order if only one argument. */
|
|
if (nargs < 2)
|
|
return GS_UNHANDLED;
|
|
|
|
/* Or if all arguments are already free of side-effects. */
|
|
bool has_side_effects = false;
|
|
for (int i = 0; i < nargs; i++)
|
|
{
|
|
if (TREE_SIDE_EFFECTS (CALL_EXPR_ARG (*expr_p, i)))
|
|
{
|
|
has_side_effects = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!has_side_effects)
|
|
return GS_UNHANDLED;
|
|
|
|
/* Leave the last argument for gimplify_call_expr. */
|
|
for (int i = 0; i < nargs - 1; i++)
|
|
{
|
|
tree new_arg = CALL_EXPR_ARG (*expr_p, i);
|
|
|
|
/* If argument has a side-effect, gimplify_arg will handle it. */
|
|
if (gimplify_arg (&new_arg, pre_p, loc) == GS_ERROR)
|
|
return GS_ERROR;
|
|
|
|
/* Even if an argument itself doesn't have any side-effects, it
|
|
might be altered by another argument in the list. */
|
|
if (new_arg == CALL_EXPR_ARG (*expr_p, i)
|
|
&& !really_constant_p (new_arg))
|
|
new_arg = get_formal_tmp_var (new_arg, pre_p);
|
|
|
|
CALL_EXPR_ARG (*expr_p, i) = new_arg;
|
|
}
|
|
|
|
return GS_OK;
|
|
}
|
|
|
|
return GS_UNHANDLED;
|
|
}
|
|
|
|
/* Gimplify an UNSIGNED_RSHIFT_EXPR node. */
|
|
|
|
static gimplify_status
|
|
d_gimplify_unsigned_rshift_expr (tree *expr_p)
|
|
{
|
|
/* Convert op0 to an unsigned type. */
|
|
tree op0 = TREE_OPERAND (*expr_p, 0);
|
|
tree op1 = TREE_OPERAND (*expr_p, 1);
|
|
tree type = d_unsigned_type (TREE_TYPE (op0));
|
|
|
|
*expr_p = convert (TREE_TYPE (*expr_p),
|
|
build2 (RSHIFT_EXPR, type, convert (type, op0), op1));
|
|
return GS_OK;
|
|
}
|
|
|
|
/* Implements the lang_hooks.gimplify_expr routine for language D.
|
|
Do gimplification of D specific expression trees in EXPR_P. */
|
|
|
|
int
|
|
d_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
|
|
{
|
|
switch (TREE_CODE (*expr_p))
|
|
{
|
|
case INIT_EXPR:
|
|
case MODIFY_EXPR:
|
|
return d_gimplify_modify_expr (expr_p, pre_p, post_p);
|
|
|
|
case ADDR_EXPR:
|
|
return d_gimplify_addr_expr (expr_p);
|
|
|
|
case CALL_EXPR:
|
|
return d_gimplify_call_expr (expr_p, pre_p);
|
|
|
|
case UNSIGNED_RSHIFT_EXPR:
|
|
return d_gimplify_unsigned_rshift_expr (expr_p);
|
|
|
|
case FLOAT_MOD_EXPR:
|
|
gcc_unreachable ();
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return GS_UNHANDLED;
|
|
}
|