Skip to content
This repository was archived by the owner on Jun 20, 2019. It is now read-only.

Commit deb51ec

Browse files
committed
Use CLEANUP_POINT_EXPR for auto-destructing temporaries.
1 parent 5b9bd9d commit deb51ec

File tree

8 files changed

+115
-105
lines changed

8 files changed

+115
-105
lines changed

gcc/d/ChangeLog

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
2017-12-19 Iain Buclaw <[email protected]>
2+
3+
* d-codegen.cc (build_target_expr): Update signature.
4+
(force_target_expr): New function.
5+
(build_address): Use force_target_expr to store temporary.
6+
(d_build_call): Likewise.
7+
* d-lang.cc (d_gimplify_expr): Likewise.
8+
* d-tree.h (language_function): Update type for vars_in_scope from
9+
vec<VarDeclaration*> to vec<tree>.
10+
(force_target_expr): Declare.
11+
* decl.cc (DeclVisitor::visit(VarDeclaration)): Put vars with scope
12+
destructors into a TARGET_EXPR, setting its cleanup.
13+
(declare_local_var): Don't push vars with scope destructors into the
14+
function binding level.
15+
* expr.cc (ExprVisitor::visit(DeclarationExp)): Don't handle scope
16+
destructors.
17+
(ExprVisitor::visit(CallExp)): Handle calling constructors using
18+
temporary objects.
19+
(build_dtor_list): Remove function.
20+
(build_expr_dtor): Put result into a CLEANUP_POINT_EXPR if any new
21+
temporaries needing destruction were added to scope.
22+
(build_return_dtor): Likewise.
23+
* toir.cc (add_stmt): Set CLEANUP_POINT_EXPR type as void.
24+
125
2017-12-19 Iain Buclaw <[email protected]>
226

327
* d-attribs.c (attr_noreturn_exclusions): New array.

gcc/d/d-codegen.cc

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -545,21 +545,20 @@ stabilize_expr (tree *valuep)
545545
}
546546
}
547547

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

550550
tree
551-
build_target_expr (tree exp)
551+
build_target_expr (tree decl, tree exp)
552552
{
553-
tree type = TREE_TYPE (exp);
554-
tree slot = create_temporary_var (type);
555-
tree result = build4 (TARGET_EXPR, type, slot, exp, NULL_TREE, NULL_TREE);
553+
tree type = TREE_TYPE (decl);
554+
tree result = build4 (TARGET_EXPR, type, decl, exp, NULL_TREE, NULL_TREE);
556555

557556
if (EXPR_HAS_LOCATION (exp))
558557
SET_EXPR_LOCATION (result, EXPR_LOCATION (exp));
559558

560-
/* If slot must always reside in memory. */
559+
/* If decl must always reside in memory. */
561560
if (TREE_ADDRESSABLE (type))
562-
d_mark_addressable (slot);
561+
d_mark_addressable (decl);
563562

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

571+
/* Like the above function, but initializes a new temporary. */
572+
573+
tree
574+
force_target_expr (tree exp)
575+
{
576+
tree decl = create_temporary_var (TREE_TYPE (exp));
577+
578+
return build_target_expr (decl, exp);
579+
}
580+
572581
/* Returns the address of the expression EXP. */
573582

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

611620
d_mark_addressable (exp);
612621
exp = build_fold_addr_expr_with_type_loc (input_location, exp, ptrtype);
@@ -2004,7 +2013,7 @@ d_build_call (TypeFunction *tf, tree callable, tree object,
20042013
&& TREE_ADDRESSABLE (TREE_TYPE (result)))
20052014
{
20062015
CALL_EXPR_RETURN_SLOT_OPT (result) = true;
2007-
result = build_target_expr (result);
2016+
result = force_target_expr (result);
20082017
}
20092018

20102019
return compound_expr (saved_args, result);

gcc/d/d-lang.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -851,7 +851,7 @@ d_gimplify_expr (tree *expr_p, gimple_seq *pre_p,
851851
/* Constructors are not lvalues, so make them one. */
852852
if (TREE_CODE (op0) == CONSTRUCTOR)
853853
{
854-
TREE_OPERAND (*expr_p, 0) = build_target_expr (op0);
854+
TREE_OPERAND (*expr_p, 0) = force_target_expr (op0);
855855
ret = GS_OK;
856856
}
857857
break;

gcc/d/d-tree.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ struct GTY(()) language_function
233233
vec<tree, va_gc> *stmt_list;
234234

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

238238
/* Table of all used or defined labels in the function. */
239239
hash_map<Statement *, d_label_entry> *labels;
@@ -502,7 +502,8 @@ extern void extract_from_method_call (tree, tree &, tree &);
502502
extern tree build_vindex_ref (tree, tree, size_t);
503503
extern tree d_save_expr (tree);
504504
extern tree stabilize_expr (tree *);
505-
extern tree build_target_expr (tree);
505+
extern tree build_target_expr (tree, tree);
506+
extern tree force_target_expr (tree);
506507
extern tree build_address (tree);
507508
extern tree d_mark_addressable (tree);
508509
extern tree d_mark_used (tree);

gcc/d/decl.cc

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,22 @@ class DeclVisitor : public Visitor
627627
ExpInitializer *vinit = d->_init->isExpInitializer ();
628628
Expression *ie = initializerToExpression (vinit);
629629
tree exp = build_expr (ie);
630+
631+
/* Maybe put variable on list of things needing destruction. */
632+
if (d->needsScopeDtor ())
633+
{
634+
/* Its a temporary, add the corresponding cleanup. */
635+
tree decl = get_symbol_decl (d);
636+
vec_safe_push (d_function_chain->vars_in_scope, decl);
637+
638+
if (TREE_CODE (exp) == INIT_EXPR
639+
|| TREE_CODE (exp) == MODIFY_EXPR)
640+
exp = TREE_OPERAND (exp, 1);
641+
642+
exp = build_target_expr (decl, exp);
643+
TARGET_EXPR_CLEANUP (exp) = build_expr (d->edtor);
644+
}
645+
630646
add_stmt (exp);
631647
}
632648
else if (d->size (d->loc) != 0)
@@ -1295,7 +1311,12 @@ declare_local_var (VarDeclaration *var)
12951311

12961312
gcc_assert (!TREE_STATIC (decl));
12971313

1298-
d_pushdecl (decl);
1314+
/* If this is a variable used for automatic scope dtor, don't add it to the
1315+
current binding level, as its really a temporary used in a TARGET_EXPR.
1316+
See build_decl_tree visitor for VarDeclaration. */
1317+
if (!var->needsScopeDtor ())
1318+
d_pushdecl (decl);
1319+
12991320
DECL_CONTEXT (decl) = current_function_decl;
13001321

13011322
/* Compiler generated symbols. */

gcc/d/expr.cc

Lines changed: 41 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1695,6 +1695,7 @@ class ExprVisitor : public Visitor
16951695

16961696
tree callee = NULL_TREE;
16971697
tree object = NULL_TREE;
1698+
tree cleanup = NULL_TREE;
16981699
TypeFunction *tf = NULL;
16991700

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

1736+
/* When constructing temporaries, if the constructor throws,
1737+
then the object is destructed even though it is not a fully
1738+
constructed object yet. And so this call will need to be
1739+
moved inside the TARGET_EXPR_INITIAL slot. */
1740+
if (fd->isCtorDeclaration ()
1741+
&& TREE_CODE (thisexp) == COMPOUND_EXPR
1742+
&& TREE_CODE (TREE_OPERAND (thisexp, 0)) == TARGET_EXPR
1743+
&& TARGET_EXPR_CLEANUP (TREE_OPERAND (thisexp, 0)))
1744+
{
1745+
cleanup = TREE_OPERAND (thisexp, 0);
1746+
thisexp = TREE_OPERAND (thisexp, 1);
1747+
}
1748+
17351749
/* Want reference to 'this' object. */
17361750
if (!POINTER_TYPE_P (TREE_TYPE (thisexp)))
17371751
thisexp = build_address (thisexp);
@@ -1820,6 +1834,20 @@ class ExprVisitor : public Visitor
18201834
if (e->type->isTypeBasic ())
18211835
exp = d_convert (build_ctype (e->type), exp);
18221836

1837+
/* If this call was found to be a constructor for a temporary with a
1838+
cleanup, then move the call inside the TARGET_EXPR. The original
1839+
initializer is turned into an assignment, to keep its side effect. */
1840+
if (cleanup != NULL_TREE)
1841+
{
1842+
tree init = TARGET_EXPR_INITIAL (cleanup);
1843+
tree slot = TARGET_EXPR_SLOT (cleanup);
1844+
d_mark_addressable (slot);
1845+
init = build_assign (INIT_EXPR, slot, init);
1846+
1847+
TARGET_EXPR_INITIAL (cleanup) = compound_expr (init, exp);
1848+
exp = cleanup;
1849+
}
1850+
18231851
this->result_ = exp;
18241852
}
18251853

@@ -2013,22 +2041,9 @@ class ExprVisitor : public Visitor
20132041
can cause an empty STMT_LIST here. This can causes problems
20142042
during gimplification. */
20152043
if (TREE_CODE (result) == STATEMENT_LIST && !STATEMENT_LIST_HEAD (result))
2016-
this->result_ = build_empty_stmt (input_location);
2017-
else
2018-
this->result_ = result;
2019-
2020-
/* Maybe put variable on list of things needing destruction. */
2021-
VarDeclaration *vd = e->declaration->isVarDeclaration ();
2022-
if (vd != NULL)
2023-
{
2024-
if (!vd->isStatic () && !(vd->storage_class & STCmanifest)
2025-
&& !(vd->storage_class & (STCextern | STCtls | STCgshared)))
2026-
{
2027-
if (vd->needsScopeDtor ())
2028-
d_function_chain->vars_in_scope.safe_push (vd);
2029-
}
2030-
}
2044+
result = build_empty_stmt (input_location);
20312045

2046+
this->result_ = result;
20322047
}
20332048

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

3042-
/* Build an expression that calls the destructors on all the variables
3043-
going out of the scope between STARTI and ENDI. All destructors are
3044-
executed in reverse order. */
3045-
3046-
static tree
3047-
build_dtor_list (size_t starti, size_t endi)
3048-
{
3049-
tree dtors = NULL_TREE;
3050-
3051-
for (size_t i = starti; i != endi; ++i)
3052-
{
3053-
VarDeclaration *vd = d_function_chain->vars_in_scope[i];
3054-
if (vd)
3055-
{
3056-
d_function_chain->vars_in_scope[i] = NULL;
3057-
tree t = build_expr (vd->edtor);
3058-
dtors = compound_expr (t, dtors);
3059-
}
3060-
}
3061-
3062-
return dtors;
3063-
}
3064-
30653057
/* Same as build_expr, but also calls destructors on any temporaries. */
30663058

30673059
tree
30683060
build_expr_dtor (Expression *e)
30693061
{
30703062
/* Codegen can be improved by determining if no exceptions can be thrown
30713063
between the ctor and dtor, and eliminating the ctor and dtor. */
3072-
size_t starti = d_function_chain->vars_in_scope.length ();
3064+
size_t saved_vars = vec_safe_length (d_function_chain->vars_in_scope);
30733065
tree result = build_expr (e);
3074-
size_t endi = d_function_chain->vars_in_scope.length ();
30753066

3076-
tree dtors = build_dtor_list (starti, endi);
3077-
3078-
if (dtors != NULL_TREE)
3067+
if (saved_vars != vec_safe_length (d_function_chain->vars_in_scope))
30793068
{
3080-
/* Split comma expressions, so that only the result is maybe saved. */
3081-
tree expr = stabilize_expr (&result);
3082-
3083-
/* When constructing temporaries, if the constructor throws, then
3084-
we don't want to run the destructor on the incomplete object. */
3085-
CallExp *ce = (e->op == TOKcall) ? ((CallExp *) e) : NULL;
3086-
if (ce != NULL && ce->e1->op == TOKdotvar
3087-
&& ((DotVarExp *) ce->e1)->var->isCtorDeclaration ())
3088-
{
3089-
/* Extract the object from the ctor call, as it will be the same
3090-
value as the returned result, just maybe without the side effects.
3091-
Rewriting: ctor (&e1) => (ctor (&e1), e1) */
3092-
expr = compound_expr (expr, result);
3093-
3094-
if (INDIRECT_REF_P (result))
3095-
result = build_deref (CALL_EXPR_ARG (TREE_OPERAND (result, 0), 0));
3096-
else
3097-
result = CALL_EXPR_ARG (result, 0);
3098-
3099-
return compound_expr (compound_expr (expr, dtors), result);
3100-
}
3101-
3102-
/* Extract the LHS from the assignment expression.
3103-
Rewriting: (e1 = e2) => ((e1 = e2), e1) */
3104-
if (TREE_CODE (result) == INIT_EXPR || TREE_CODE (result) == MODIFY_EXPR)
3105-
{
3106-
expr = compound_expr (expr, result);
3107-
result = TREE_OPERAND (result, 0);
3108-
}
3109-
3110-
/* If the result has side-effects, save the entire expression. */
3111-
if (TREE_SIDE_EFFECTS (result))
3112-
{
3113-
/* Wrap expr and dtors in a try/finally expression. */
3114-
result = d_save_expr (result);
3115-
expr = build2 (TRY_FINALLY_EXPR, void_type_node,
3116-
compound_expr (expr, result), dtors);
3117-
}
3118-
else
3119-
expr = compound_expr (expr, dtors);
3120-
3121-
return compound_expr (expr, result);
3069+
result = fold_build_cleanup_point_expr (TREE_TYPE (result), result);
3070+
vec_safe_truncate (d_function_chain->vars_in_scope, saved_vars);
31223071
}
31233072

31243073
return result;
@@ -3129,13 +3078,11 @@ build_expr_dtor (Expression *e)
31293078
tree
31303079
build_return_dtor (Expression *e, Type *type, TypeFunction *tf)
31313080
{
3132-
size_t starti = d_function_chain->vars_in_scope.length ();
3081+
size_t saved_vars = vec_safe_length (d_function_chain->vars_in_scope);
31333082
tree result = build_expr (e);
3134-
size_t endi = d_function_chain->vars_in_scope.length ();
31353083

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

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

3152-
/* Nest the return expression inside the try/finally expression. */
3153-
if (dtors != NULL_TREE)
3154-
return build2 (TRY_FINALLY_EXPR, void_type_node, result, dtors);
3099+
/* May nest the return expression inside the try/finally expression. */
3100+
if (saved_vars != vec_safe_length (d_function_chain->vars_in_scope))
3101+
{
3102+
result = fold_build_cleanup_point_expr (TREE_TYPE (result), result);
3103+
vec_safe_truncate (d_function_chain->vars_in_scope, saved_vars);
3104+
}
31553105

31563106
return result;
31573107
}

gcc/d/toir.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,11 @@ add_stmt (tree t)
205205
}
206206
else
207207
{
208+
/* Force the type to be void so we don't need to create a temporary
209+
variable to hold the inner expression. */
210+
if (TREE_CODE (t) == CLEANUP_POINT_EXPR)
211+
TREE_TYPE (t) = void_type_node;
212+
208213
/* Append the expression to the statement list.
209214
Make sure it has a proper location. */
210215
if (EXPR_P (t) && !EXPR_HAS_LOCATION (t))

gcc/testsuite/gdc.test/runnable/test14903.d

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ void dtorsTest() {
7575
assert(0);
7676
} catch (Exception) {}
7777
assert(counter == 1);
78-
//assert(StructWithDtor.numDtor == 1); // XGDC
78+
assert(StructWithDtor.numDtor == 1);
7979

8080
// TODO: test exception chaining with throwing dtors
8181
}

0 commit comments

Comments
 (0)