Skip to content
This repository was archived by the owner on Jun 20, 2019. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions gcc/d/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
2017-12-19 Iain Buclaw <ibuclaw@gdcproject.org>

* d-codegen.cc (build_target_expr): Update signature.
(force_target_expr): New function.
(build_address): Use force_target_expr to store temporary.
(d_build_call): Likewise.
* d-lang.cc (d_gimplify_expr): Likewise.
* d-tree.h (language_function): Update type for vars_in_scope from
vec<VarDeclaration*> to vec<tree>.
(force_target_expr): Declare.
* decl.cc (DeclVisitor::visit(VarDeclaration)): Put vars with scope
destructors into a TARGET_EXPR, setting its cleanup.
(declare_local_var): Don't push vars with scope destructors into the
function binding level.
* expr.cc (ExprVisitor::visit(DeclarationExp)): Don't handle scope
destructors.
(ExprVisitor::visit(CallExp)): Handle calling constructors using
temporary objects.
(build_dtor_list): Remove function.
(build_expr_dtor): Put result into a CLEANUP_POINT_EXPR if any new
temporaries needing destruction were added to scope.
(build_return_dtor): Likewise.
* toir.cc (add_stmt): Set CLEANUP_POINT_EXPR type as void.

2017-12-19 Iain Buclaw <ibuclaw@gdcproject.org>

* d-attribs.c (attr_noreturn_exclusions): New array.
Expand Down
27 changes: 18 additions & 9 deletions gcc/d/d-codegen.cc
Original file line number Diff line number Diff line change
Expand Up @@ -545,21 +545,20 @@ stabilize_expr (tree *valuep)
}
}

/* Return a TARGET_EXPR using EXP to initialize a new temporary. */
/* Return a TARGET_EXPR, initializing the DECL with EXP. */

tree
build_target_expr (tree exp)
build_target_expr (tree decl, tree exp)
{
tree type = TREE_TYPE (exp);
tree slot = create_temporary_var (type);
tree result = build4 (TARGET_EXPR, type, slot, exp, NULL_TREE, NULL_TREE);
tree type = TREE_TYPE (decl);
tree result = build4 (TARGET_EXPR, type, decl, exp, NULL_TREE, NULL_TREE);

if (EXPR_HAS_LOCATION (exp))
SET_EXPR_LOCATION (result, EXPR_LOCATION (exp));

/* If slot must always reside in memory. */
/* If decl must always reside in memory. */
if (TREE_ADDRESSABLE (type))
d_mark_addressable (slot);
d_mark_addressable (decl);

/* Always set TREE_SIDE_EFFECTS so that expand_expr does not ignore the
TARGET_EXPR. If there really turn out to be no side effects, then the
Expand All @@ -569,6 +568,16 @@ build_target_expr (tree exp)
return result;
}

/* Like the above function, but initializes a new temporary. */

tree
force_target_expr (tree exp)
{
tree decl = create_temporary_var (TREE_TYPE (exp));

return build_target_expr (decl, exp);
}

/* Returns the address of the expression EXP. */

tree
Expand Down Expand Up @@ -606,7 +615,7 @@ build_address (tree exp)
/* Some expression lowering may request an address of a compile-time constant.
Make sure it is assigned to a location we can reference. */
if (CONSTANT_CLASS_P (exp) && TREE_CODE (exp) != STRING_CST)
exp = build_target_expr (exp);
exp = force_target_expr (exp);

d_mark_addressable (exp);
exp = build_fold_addr_expr_with_type_loc (input_location, exp, ptrtype);
Expand Down Expand Up @@ -2004,7 +2013,7 @@ d_build_call (TypeFunction *tf, tree callable, tree object,
&& TREE_ADDRESSABLE (TREE_TYPE (result)))
{
CALL_EXPR_RETURN_SLOT_OPT (result) = true;
result = build_target_expr (result);
result = force_target_expr (result);
}

return compound_expr (saved_args, result);
Expand Down
2 changes: 1 addition & 1 deletion gcc/d/d-lang.cc
Original file line number Diff line number Diff line change
Expand Up @@ -851,7 +851,7 @@ d_gimplify_expr (tree *expr_p, gimple_seq *pre_p,
/* Constructors are not lvalues, so make them one. */
if (TREE_CODE (op0) == CONSTRUCTOR)
{
TREE_OPERAND (*expr_p, 0) = build_target_expr (op0);
TREE_OPERAND (*expr_p, 0) = force_target_expr (op0);
ret = GS_OK;
}
break;
Expand Down
5 changes: 3 additions & 2 deletions gcc/d/d-tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ struct GTY(()) language_function
vec<tree, va_gc> *stmt_list;

/* Variables that are in scope that will need destruction later. */
vec<VarDeclaration *> GTY((skip)) vars_in_scope;
vec<tree, va_gc> *vars_in_scope;

/* Table of all used or defined labels in the function. */
hash_map<Statement *, d_label_entry> *labels;
Expand Down Expand Up @@ -502,7 +502,8 @@ extern void extract_from_method_call (tree, tree &, tree &);
extern tree build_vindex_ref (tree, tree, size_t);
extern tree d_save_expr (tree);
extern tree stabilize_expr (tree *);
extern tree build_target_expr (tree);
extern tree build_target_expr (tree, tree);
extern tree force_target_expr (tree);
extern tree build_address (tree);
extern tree d_mark_addressable (tree);
extern tree d_mark_used (tree);
Expand Down
23 changes: 22 additions & 1 deletion gcc/d/decl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,22 @@ class DeclVisitor : public Visitor
ExpInitializer *vinit = d->_init->isExpInitializer ();
Expression *ie = initializerToExpression (vinit);
tree exp = build_expr (ie);

/* Maybe put variable on list of things needing destruction. */
if (d->needsScopeDtor ())
{
/* Its a temporary, add the corresponding cleanup. */
tree decl = get_symbol_decl (d);
vec_safe_push (d_function_chain->vars_in_scope, decl);

if (TREE_CODE (exp) == INIT_EXPR
|| TREE_CODE (exp) == MODIFY_EXPR)
exp = TREE_OPERAND (exp, 1);

exp = build_target_expr (decl, exp);
TARGET_EXPR_CLEANUP (exp) = build_expr (d->edtor);
}

add_stmt (exp);
}
else if (d->size (d->loc) != 0)
Expand Down Expand Up @@ -1295,7 +1311,12 @@ declare_local_var (VarDeclaration *var)

gcc_assert (!TREE_STATIC (decl));

d_pushdecl (decl);
/* If this is a variable used for automatic scope dtor, don't add it to the
current binding level, as its really a temporary used in a TARGET_EXPR.
See build_decl_tree visitor for VarDeclaration. */
if (!var->needsScopeDtor ())
d_pushdecl (decl);

DECL_CONTEXT (decl) = current_function_decl;

/* Compiler generated symbols. */
Expand Down
132 changes: 41 additions & 91 deletions gcc/d/expr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1695,6 +1695,7 @@ class ExprVisitor : public Visitor

tree callee = NULL_TREE;
tree object = NULL_TREE;
tree cleanup = NULL_TREE;
TypeFunction *tf = NULL;

/* Calls to delegates can sometimes look like this. */
Expand Down Expand Up @@ -1732,6 +1733,19 @@ class ExprVisitor : public Visitor
{
tree thisexp = build_expr (dve->e1);

/* When constructing temporaries, if the constructor throws,
then the object is destructed even though it is not a fully
constructed object yet. And so this call will need to be
moved inside the TARGET_EXPR_INITIAL slot. */
if (fd->isCtorDeclaration ()
&& TREE_CODE (thisexp) == COMPOUND_EXPR
&& TREE_CODE (TREE_OPERAND (thisexp, 0)) == TARGET_EXPR
&& TARGET_EXPR_CLEANUP (TREE_OPERAND (thisexp, 0)))
{
cleanup = TREE_OPERAND (thisexp, 0);
thisexp = TREE_OPERAND (thisexp, 1);
}

/* Want reference to 'this' object. */
if (!POINTER_TYPE_P (TREE_TYPE (thisexp)))
thisexp = build_address (thisexp);
Expand Down Expand Up @@ -1820,6 +1834,20 @@ class ExprVisitor : public Visitor
if (e->type->isTypeBasic ())
exp = d_convert (build_ctype (e->type), exp);

/* If this call was found to be a constructor for a temporary with a
cleanup, then move the call inside the TARGET_EXPR. The original
initializer is turned into an assignment, to keep its side effect. */
if (cleanup != NULL_TREE)
{
tree init = TARGET_EXPR_INITIAL (cleanup);
tree slot = TARGET_EXPR_SLOT (cleanup);
d_mark_addressable (slot);
init = build_assign (INIT_EXPR, slot, init);

TARGET_EXPR_INITIAL (cleanup) = compound_expr (init, exp);
exp = cleanup;
}

this->result_ = exp;
}

Expand Down Expand Up @@ -2013,22 +2041,9 @@ class ExprVisitor : public Visitor
can cause an empty STMT_LIST here. This can causes problems
during gimplification. */
if (TREE_CODE (result) == STATEMENT_LIST && !STATEMENT_LIST_HEAD (result))
this->result_ = build_empty_stmt (input_location);
else
this->result_ = result;

/* Maybe put variable on list of things needing destruction. */
VarDeclaration *vd = e->declaration->isVarDeclaration ();
if (vd != NULL)
{
if (!vd->isStatic () && !(vd->storage_class & STCmanifest)
&& !(vd->storage_class & (STCextern | STCtls | STCgshared)))
{
if (vd->needsScopeDtor ())
d_function_chain->vars_in_scope.safe_push (vd);
}
}
result = build_empty_stmt (input_location);

this->result_ = result;
}

/* Build a typeid expression. Returns an instance of class TypeInfo
Expand Down Expand Up @@ -3039,86 +3054,20 @@ build_expr (Expression *e, bool const_p)
return expr;
}

/* Build an expression that calls the destructors on all the variables
going out of the scope between STARTI and ENDI. All destructors are
executed in reverse order. */

static tree
build_dtor_list (size_t starti, size_t endi)
{
tree dtors = NULL_TREE;

for (size_t i = starti; i != endi; ++i)
{
VarDeclaration *vd = d_function_chain->vars_in_scope[i];
if (vd)
{
d_function_chain->vars_in_scope[i] = NULL;
tree t = build_expr (vd->edtor);
dtors = compound_expr (t, dtors);
}
}

return dtors;
}

/* Same as build_expr, but also calls destructors on any temporaries. */

tree
build_expr_dtor (Expression *e)
{
/* Codegen can be improved by determining if no exceptions can be thrown
between the ctor and dtor, and eliminating the ctor and dtor. */
size_t starti = d_function_chain->vars_in_scope.length ();
size_t saved_vars = vec_safe_length (d_function_chain->vars_in_scope);
tree result = build_expr (e);
size_t endi = d_function_chain->vars_in_scope.length ();

tree dtors = build_dtor_list (starti, endi);

if (dtors != NULL_TREE)
if (saved_vars != vec_safe_length (d_function_chain->vars_in_scope))
{
/* Split comma expressions, so that only the result is maybe saved. */
tree expr = stabilize_expr (&result);

/* When constructing temporaries, if the constructor throws, then
we don't want to run the destructor on the incomplete object. */
CallExp *ce = (e->op == TOKcall) ? ((CallExp *) e) : NULL;
if (ce != NULL && ce->e1->op == TOKdotvar
&& ((DotVarExp *) ce->e1)->var->isCtorDeclaration ())
{
/* Extract the object from the ctor call, as it will be the same
value as the returned result, just maybe without the side effects.
Rewriting: ctor (&e1) => (ctor (&e1), e1) */
expr = compound_expr (expr, result);

if (INDIRECT_REF_P (result))
result = build_deref (CALL_EXPR_ARG (TREE_OPERAND (result, 0), 0));
else
result = CALL_EXPR_ARG (result, 0);

return compound_expr (compound_expr (expr, dtors), result);
}

/* Extract the LHS from the assignment expression.
Rewriting: (e1 = e2) => ((e1 = e2), e1) */
if (TREE_CODE (result) == INIT_EXPR || TREE_CODE (result) == MODIFY_EXPR)
{
expr = compound_expr (expr, result);
result = TREE_OPERAND (result, 0);
}

/* If the result has side-effects, save the entire expression. */
if (TREE_SIDE_EFFECTS (result))
{
/* Wrap expr and dtors in a try/finally expression. */
result = d_save_expr (result);
expr = build2 (TRY_FINALLY_EXPR, void_type_node,
compound_expr (expr, result), dtors);
}
else
expr = compound_expr (expr, dtors);

return compound_expr (expr, result);
result = fold_build_cleanup_point_expr (TREE_TYPE (result), result);
vec_safe_truncate (d_function_chain->vars_in_scope, saved_vars);
}

return result;
Expand All @@ -3129,13 +3078,11 @@ build_expr_dtor (Expression *e)
tree
build_return_dtor (Expression *e, Type *type, TypeFunction *tf)
{
size_t starti = d_function_chain->vars_in_scope.length ();
size_t saved_vars = vec_safe_length (d_function_chain->vars_in_scope);
tree result = build_expr (e);
size_t endi = d_function_chain->vars_in_scope.length ();

/* Convert for initialising the DECL_RESULT. */
result = convert_expr (result, e->type, type);
tree dtors = build_dtor_list (starti, endi);

/* If we are returning a reference, take the address. */
if (tf->isref)
Expand All @@ -3149,9 +3096,12 @@ build_return_dtor (Expression *e, Type *type, TypeFunction *tf)
result = build_assign (INIT_EXPR, decl, result);
result = compound_expr (expr, return_expr (result));

/* Nest the return expression inside the try/finally expression. */
if (dtors != NULL_TREE)
return build2 (TRY_FINALLY_EXPR, void_type_node, result, dtors);
/* May nest the return expression inside the try/finally expression. */
if (saved_vars != vec_safe_length (d_function_chain->vars_in_scope))
{
result = fold_build_cleanup_point_expr (TREE_TYPE (result), result);
vec_safe_truncate (d_function_chain->vars_in_scope, saved_vars);
}

return result;
}
Expand Down
5 changes: 5 additions & 0 deletions gcc/d/toir.cc
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,11 @@ add_stmt (tree t)
}
else
{
/* Force the type to be void so we don't need to create a temporary
variable to hold the inner expression. */
if (TREE_CODE (t) == CLEANUP_POINT_EXPR)
TREE_TYPE (t) = void_type_node;

/* Append the expression to the statement list.
Make sure it has a proper location. */
if (EXPR_P (t) && !EXPR_HAS_LOCATION (t))
Expand Down
2 changes: 1 addition & 1 deletion gcc/testsuite/gdc.test/runnable/test14903.d
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ void dtorsTest() {
assert(0);
} catch (Exception) {}
assert(counter == 1);
//assert(StructWithDtor.numDtor == 1); // XGDC
assert(StructWithDtor.numDtor == 1);

// TODO: test exception chaining with throwing dtors
}
Expand Down