diff --git a/gcc/d/ChangeLog b/gcc/d/ChangeLog index 85f3864d6..3dc370cbf 100644 --- a/gcc/d/ChangeLog +++ b/gcc/d/ChangeLog @@ -1,3 +1,27 @@ +2017-12-19 Iain Buclaw + + * 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 to vec. + (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 * d-attribs.c (attr_noreturn_exclusions): New array. diff --git a/gcc/d/d-codegen.cc b/gcc/d/d-codegen.cc index f1ac34b31..a090ffb49 100644 --- a/gcc/d/d-codegen.cc +++ b/gcc/d/d-codegen.cc @@ -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 @@ -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 @@ -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); @@ -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); diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc index 153065c4b..46f5b0600 100644 --- a/gcc/d/d-lang.cc +++ b/gcc/d/d-lang.cc @@ -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; diff --git a/gcc/d/d-tree.h b/gcc/d/d-tree.h index 62f2ba5f8..08aedceae 100644 --- a/gcc/d/d-tree.h +++ b/gcc/d/d-tree.h @@ -233,7 +233,7 @@ struct GTY(()) language_function vec *stmt_list; /* Variables that are in scope that will need destruction later. */ - vec GTY((skip)) vars_in_scope; + vec *vars_in_scope; /* Table of all used or defined labels in the function. */ hash_map *labels; @@ -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); diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc index d11896ede..e667922bb 100644 --- a/gcc/d/decl.cc +++ b/gcc/d/decl.cc @@ -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) @@ -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. */ diff --git a/gcc/d/expr.cc b/gcc/d/expr.cc index 1f67936a2..7ee8864c9 100644 --- a/gcc/d/expr.cc +++ b/gcc/d/expr.cc @@ -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. */ @@ -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); @@ -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; } @@ -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 @@ -3039,29 +3054,6 @@ 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 @@ -3069,56 +3061,13 @@ 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; @@ -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) @@ -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; } diff --git a/gcc/d/toir.cc b/gcc/d/toir.cc index b66d23a8d..51a9611ce 100644 --- a/gcc/d/toir.cc +++ b/gcc/d/toir.cc @@ -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)) diff --git a/gcc/testsuite/gdc.test/runnable/test14903.d b/gcc/testsuite/gdc.test/runnable/test14903.d index 6b5c6d5fe..fd35d9ce5 100644 --- a/gcc/testsuite/gdc.test/runnable/test14903.d +++ b/gcc/testsuite/gdc.test/runnable/test14903.d @@ -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 }