/* expr.cc -- Lower D frontend expressions to GCC trees. Copyright (C) 2015-2022 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 . */ #include "config.h" #include "system.h" #include "coretypes.h" #include "dmd/aggregate.h" #include "dmd/ctfe.h" #include "dmd/declaration.h" #include "dmd/enum.h" #include "dmd/expression.h" #include "dmd/identifier.h" #include "dmd/init.h" #include "dmd/module.h" #include "dmd/mtype.h" #include "dmd/template.h" #include "tree.h" #include "fold-const.h" #include "diagnostic.h" #include "langhooks.h" #include "tm.h" #include "function.h" #include "toplev.h" #include "varasm.h" #include "predict.h" #include "stor-layout.h" #include "d-tree.h" /* Determine if type T is a struct that has a postblit. */ static bool needs_postblit (Type *t) { t = t->baseElemOf (); if (TypeStruct *ts = t->isTypeStruct ()) { if (ts->sym->postblit) return true; } return false; } /* Determine if type T is a struct that has a destructor. */ static bool needs_dtor (Type *t) { t = t->baseElemOf (); if (TypeStruct *ts = t->isTypeStruct ()) { if (ts->sym->dtor) return true; } return false; } /* Determine if expression E is a suitable lvalue. */ static bool lvalue_p (Expression *e) { SliceExp *se = e->isSliceExp (); if (se != NULL && se->e1->isLvalue ()) return true; CastExp *ce = e->isCastExp (); if (ce != NULL && ce->e1->isLvalue ()) return true; return (e->op != EXP::slice && e->isLvalue ()); } /* Build an expression of code CODE, data type TYPE, and operands ARG0 and ARG1. Perform relevant conversions needed for correct code operations. */ static tree binary_op (tree_code code, tree type, tree arg0, tree arg1) { tree t0 = TREE_TYPE (arg0); tree t1 = TREE_TYPE (arg1); tree ret = NULL_TREE; /* Deal with float mod expressions immediately. */ if (code == FLOAT_MOD_EXPR) return build_float_modulus (type, arg0, arg1); if (POINTER_TYPE_P (t0) && INTEGRAL_TYPE_P (t1)) return build_nop (type, build_offset_op (code, arg0, arg1)); if (INTEGRAL_TYPE_P (t0) && POINTER_TYPE_P (t1)) return build_nop (type, build_offset_op (code, arg1, arg0)); if (POINTER_TYPE_P (t0) && POINTER_TYPE_P (t1)) { gcc_assert (code == MINUS_EXPR); tree ptrtype = lang_hooks.types.type_for_mode (ptr_mode, 0); /* POINTER_DIFF_EXPR requires a signed integer type of the same size as pointers. If some platform cannot provide that, or has a larger ptrdiff_type to support differences larger than half the address space, cast the pointers to some larger integer type and do the computations in that type. */ if (TYPE_PRECISION (ptrtype) > TYPE_PRECISION (t0)) ret = fold_build2 (MINUS_EXPR, ptrtype, d_convert (ptrtype, arg0), d_convert (ptrtype, arg1)); else ret = fold_build2 (POINTER_DIFF_EXPR, ptrtype, arg0, arg1); } else { /* If the operation needs excess precision. */ tree eptype = excess_precision_type (type); if (eptype != NULL_TREE) { arg0 = d_convert (eptype, arg0); arg1 = d_convert (eptype, arg1); } else { /* Front-end does not do this conversion and GCC does not always do it right. */ if (COMPLEX_FLOAT_TYPE_P (t0) && !COMPLEX_FLOAT_TYPE_P (t1)) arg1 = d_convert (t0, arg1); else if (COMPLEX_FLOAT_TYPE_P (t1) && !COMPLEX_FLOAT_TYPE_P (t0)) arg0 = d_convert (t1, arg0); eptype = type; } ret = build2 (code, eptype, arg0, arg1); } return d_convert (type, ret); } /* Build a binary expression of code CODE, assigning the result into E1. */ static tree binop_assignment (tree_code code, Expression *e1, Expression *e2) { /* Skip casts for lhs assignment. */ Expression *e1b = e1; while (e1b->op == EXP::cast_) { CastExp *ce = e1b->isCastExp (); gcc_assert (same_type_p (ce->type, ce->to)); e1b = ce->e1; } /* Stabilize LHS for assignment. */ tree lhs = build_expr (e1b); tree lexpr = stabilize_expr (&lhs); /* The LHS expression could be an assignment, to which its operation gets lost during gimplification. */ if (TREE_CODE (lhs) == MODIFY_EXPR) { /* If LHS has side effects, call stabilize_reference on it, so it can be evaluated multiple times. */ if (TREE_SIDE_EFFECTS (TREE_OPERAND (lhs, 0))) lhs = build_assign (MODIFY_EXPR, stabilize_reference (TREE_OPERAND (lhs, 0)), TREE_OPERAND (lhs, 1)); lexpr = compound_expr (lexpr, lhs); lhs = TREE_OPERAND (lhs, 0); } lhs = stabilize_reference (lhs); /* Save RHS, to ensure that the expression is evaluated before LHS. */ tree rhs = build_expr (e2); tree rexpr = d_save_expr (rhs); rhs = binary_op (code, build_ctype (e1->type), convert_expr (lhs, e1b->type, e1->type), rexpr); if (TREE_SIDE_EFFECTS (rhs)) rhs = compound_expr (rexpr, rhs); tree expr = modify_expr (lhs, convert_expr (rhs, e1->type, e1b->type)); return compound_expr (lexpr, expr); } /* Implements the visitor interface to build the GCC trees of all Expression AST classes emitted from the D Front-end. All visit methods accept one parameter E, which holds the frontend AST of the expression to compile. They also don't return any value, instead generated code is cached in RESULT_ and returned from the caller. */ class ExprVisitor : public Visitor { using Visitor::visit; tree result_; bool constp_; bool literalp_; public: ExprVisitor (bool constp, bool literalp) { this->result_ = NULL_TREE; this->constp_ = constp; this->literalp_ = literalp; } tree result (void) { return this->result_; } /* Visitor interfaces, each Expression class should have overridden the default. */ void visit (Expression *) final override { gcc_unreachable (); } /* Build a conditional expression. If either the second or third expression is void, then the resulting type is void. Otherwise they are implicitly converted to a common type. */ void visit (CondExp *e) final override { tree cond = convert_for_condition (build_expr (e->econd), e->econd->type); tree t1 = build_expr (e->e1); tree t2 = build_expr (e->e2); if (e->type->ty != TY::Tvoid) { t1 = convert_expr (t1, e->e1->type, e->type); t2 = convert_expr (t2, e->e2->type, e->type); } this->result_ = build_condition (build_ctype (e->type), cond, t1, t2); } /* Build an identity comparison expression. Operands go through the usual conversions to bring them to a common type before comparison. The result type is bool. */ void visit (IdentityExp *e) final override { tree_code code = (e->op == EXP::identity) ? EQ_EXPR : NE_EXPR; Type *tb1 = e->e1->type->toBasetype (); Type *tb2 = e->e2->type->toBasetype (); if ((tb1->ty == TY::Tsarray || tb1->ty == TY::Tarray) && (tb2->ty == TY::Tsarray || tb2->ty == TY::Tarray)) { /* For static and dynamic arrays, identity is defined as referring to the same array elements and the same number of elements. */ tree t1 = d_array_convert (e->e1); tree t2 = d_array_convert (e->e2); this->result_ = d_convert (build_ctype (e->type), build_boolop (code, t1, t2)); } else if (tb1->isfloating () && tb1->ty != TY::Tvector) { /* For floating-point values, identity is defined as the bits in the operands being identical. */ tree t1 = d_save_expr (build_expr (e->e1)); tree t2 = d_save_expr (build_expr (e->e2)); if (!tb1->iscomplex ()) this->result_ = build_float_identity (code, t1, t2); else { /* Compare the real and imaginary parts separately. */ tree req = build_float_identity (code, real_part (t1), real_part (t2)); tree ieq = build_float_identity (code, imaginary_part (t1), imaginary_part (t2)); if (code == EQ_EXPR) this->result_ = build_boolop (TRUTH_ANDIF_EXPR, req, ieq); else this->result_ = build_boolop (TRUTH_ORIF_EXPR, req, ieq); } } else if (TypeStruct *ts = tb1->isTypeStruct ()) { /* For struct objects, identity is defined as bits in operands being identical also. Alignment holes in structs are ignored. */ tree t1 = build_expr (e->e1); tree t2 = build_expr (e->e2); gcc_assert (same_type_p (tb1, tb2)); this->result_ = build_struct_comparison (code, ts->sym, t1, t2); } else { /* For operands of other types, identity is defined as being the same as equality expressions. */ tree t1 = build_expr (e->e1); tree t2 = build_expr (e->e2); this->result_ = d_convert (build_ctype (e->type), build_boolop (code, t1, t2)); } } /* Build an equality expression, which compare the two operands for either equality or inequality. Operands go through the usual conversions to bring them to a common type before comparison. The result type is bool. */ void visit (EqualExp *e) final override { Type *tb1 = e->e1->type->toBasetype (); Type *tb2 = e->e2->type->toBasetype (); tree_code code = (e->op == EXP::equal) ? EQ_EXPR : NE_EXPR; if ((tb1->ty == TY::Tsarray || tb1->ty == TY::Tarray) && (tb2->ty == TY::Tsarray || tb2->ty == TY::Tarray)) { /* For static and dynamic arrays, equality is defined as the lengths of the arrays matching, and all the elements are equal. */ Type *t1elem = tb1->nextOf ()->toBasetype (); Type *t2elem = tb1->nextOf ()->toBasetype (); /* Check if comparisons of arrays can be optimized using memcmp. This will inline EQ expressions as: e1.length == e2.length && memcmp(e1.ptr, e2.ptr, size) == 0; Or when generating a NE expression: e1.length != e2.length || memcmp(e1.ptr, e2.ptr, size) != 0; */ if ((t1elem->isintegral () || t1elem->ty == TY::Tvoid || (t1elem->ty == TY::Tstruct && !t1elem->isTypeStruct ()->sym->xeq)) && t1elem->ty == t2elem->ty) { tree t1 = d_array_convert (e->e1); tree t2 = d_array_convert (e->e2); tree result; /* Make temporaries to prevent multiple evaluations. */ tree t1saved = d_save_expr (t1); tree t2saved = d_save_expr (t2); /* Length of arrays, for comparisons done before calling memcmp. */ tree t1len = d_array_length (t1saved); tree t2len = d_array_length (t2saved); /* Reference to array data. */ tree t1ptr = d_array_ptr (t1saved); tree t2ptr = d_array_ptr (t2saved); /* Compare arrays using memcmp if possible, otherwise for structs, each field is compared inline. */ if (t1elem->ty != TY::Tstruct || identity_compare_p (t1elem->isTypeStruct ()->sym)) { tree size = size_mult_expr (t1len, size_int (t1elem->size ())); result = build_memcmp_call (t1ptr, t2ptr, size); result = build_boolop (code, result, integer_zero_node); } else { StructDeclaration *sd = t1elem->isTypeStruct ()->sym; result = build_array_struct_comparison (code, sd, t1len, t1ptr, t2ptr); } /* Check array length first before passing to memcmp. For equality expressions, this becomes: (e1.length == 0 || memcmp); Otherwise for inequality: (e1.length != 0 && memcmp); */ tree tsizecmp = build_boolop (code, t1len, size_zero_node); if (e->op == EXP::equal) result = build_boolop (TRUTH_ORIF_EXPR, tsizecmp, result); else result = build_boolop (TRUTH_ANDIF_EXPR, tsizecmp, result); /* Finally, check if lengths of both arrays match if dynamic. The frontend should have already guaranteed that static arrays have same size. */ if (tb1->ty == TY::Tsarray && tb2->ty == TY::Tsarray) gcc_assert (tb1->size () == tb2->size ()); else { tree tlencmp = build_boolop (code, t1len, t2len); if (e->op == EXP::equal) result = build_boolop (TRUTH_ANDIF_EXPR, tlencmp, result); else result = build_boolop (TRUTH_ORIF_EXPR, tlencmp, result); } /* Ensure left-to-right order of evaluation. */ if (TREE_SIDE_EFFECTS (t2)) result = compound_expr (t2saved, result); if (TREE_SIDE_EFFECTS (t1)) result = compound_expr (t1saved, result); this->result_ = result; } else { /* Use _adEq2() to compare each element. */ Type *t1array = t1elem->arrayOf (); tree result = build_libcall (LIBCALL_ADEQ2, e->type, 3, d_array_convert (e->e1), d_array_convert (e->e2), build_typeinfo (e, t1array)); if (e->op == EXP::notEqual) result = build1 (TRUTH_NOT_EXPR, build_ctype (e->type), result); this->result_ = result; } } else if (TypeStruct *ts = tb1->isTypeStruct ()) { /* Equality for struct objects means the logical product of all equality results of the corresponding object fields. */ tree t1 = build_expr (e->e1); tree t2 = build_expr (e->e2); gcc_assert (same_type_p (tb1, tb2)); this->result_ = build_struct_comparison (code, ts->sym, t1, t2); } else if (tb1->ty == TY::Taarray && tb2->ty == TY::Taarray) { /* Use _aaEqual() for associative arrays. */ tree result = build_libcall (LIBCALL_AAEQUAL, e->type, 3, build_typeinfo (e, tb1), build_expr (e->e1), build_expr (e->e2)); if (e->op == EXP::notEqual) result = build1 (TRUTH_NOT_EXPR, build_ctype (e->type), result); this->result_ = result; } else { /* For operands of other types, equality is defined as the bit pattern of the type matches exactly. */ tree t1 = build_expr (e->e1); tree t2 = build_expr (e->e2); this->result_ = d_convert (build_ctype (e->type), build_boolop (code, t1, t2)); } } /* Build an `in' expression. This is a condition to see if an element exists in an associative array. The result is a pointer to the element, or null if false. */ void visit (InExp *e) final override { Type *tb2 = e->e2->type->toBasetype (); Type *tkey = tb2->isTypeAArray ()->index->toBasetype (); tree key = convert_expr (build_expr (e->e1), e->e1->type, tkey); /* Build a call to _aaInX(). */ this->result_ = build_libcall (LIBCALL_AAINX, e->type, 3, build_expr (e->e2), build_typeinfo (e, tkey), build_address (key)); } /* Build a relational expression. The result type is bool. */ void visit (CmpExp *e) final override { Type *tb1 = e->e1->type->toBasetype (); Type *tb2 = e->e2->type->toBasetype (); tree result; tree_code code; switch (e->op) { case EXP::lessOrEqual: code = LE_EXPR; break; case EXP::lessThan: code = LT_EXPR; break; case EXP::greaterOrEqual: code = GE_EXPR; break; case EXP::greaterThan: code = GT_EXPR; break; default: gcc_unreachable (); } /* For static and dynamic arrays, the relational op is turned into a library call. It is not lowered during codegen. */ if ((tb1->ty == TY::Tsarray || tb1->ty == TY::Tarray) && (tb2->ty == TY::Tsarray || tb2->ty == TY::Tarray)) { error ("cannot handle comparison of type %<%s == %s%>", tb1->toChars (), tb2->toChars ()); gcc_unreachable (); } /* Simple comparison. */ result = build_boolop (code, build_expr (e->e1), build_expr (e->e2)); this->result_ = d_convert (build_ctype (e->type), result); } /* Build a logical `and if' or `or if' expression. If the right operand expression is void, then the resulting type is void. Otherwise the result is bool. */ void visit (LogicalExp *e) final override { tree_code code = (e->op == EXP::andAnd) ? TRUTH_ANDIF_EXPR : TRUTH_ORIF_EXPR; if (e->e2->type->toBasetype ()->ty != TY::Tvoid) { tree t1 = build_expr (e->e1); tree t2 = build_expr (e->e2); t1 = convert_for_condition (t1, e->e1->type); t2 = convert_for_condition (t2, e->e2->type); this->result_ = d_convert (build_ctype (e->type), build_boolop (code, t1, t2)); } else { tree t1 = convert_for_condition (build_expr (e->e1), e->e1->type); tree t2 = build_expr_dtor (e->e2); /* Invert condition for logical or if expression. */ if (e->op == EXP::orOr) t1 = build1 (TRUTH_NOT_EXPR, d_bool_type, t1); this->result_ = build_condition (build_ctype (e->type), t1, t2, void_node); } } /* Build a binary operand expression. Operands go through usual arithmetic conversions to bring them to a common type before evaluating. */ void visit (BinExp *e) final override { tree_code code; switch (e->op) { case EXP::add: case EXP::min: if ((e->e1->type->isreal () && e->e2->type->isimaginary ()) || (e->e1->type->isimaginary () && e->e2->type->isreal ())) { /* If the result is complex, then we can shortcut binary_op. Frontend should have already validated types and sizes. */ tree t1 = build_expr (e->e1); tree t2 = build_expr (e->e2); if (e->op == EXP::min) t2 = build1 (NEGATE_EXPR, TREE_TYPE (t2), t2); if (e->e1->type->isreal ()) this->result_ = complex_expr (build_ctype (e->type), t1, t2); else this->result_ = complex_expr (build_ctype (e->type), t2, t1); return; } else code = (e->op == EXP::add) ? PLUS_EXPR : MINUS_EXPR; break; case EXP::mul: code = MULT_EXPR; break; case EXP::div: /* Determine if the div expression is a lowered pointer diff operation. The front-end rewrites `(p1 - p2)' into `(p1 - p2) / stride'. */ if (MinExp *me = e->e1->isMinExp ()) { if (me->e1->type->ty == TY::Tpointer && me->e2->type->ty == TY::Tpointer && e->e2->op == EXP::int64) { code = EXACT_DIV_EXPR; break; } } code = e->e1->type->isintegral () ? TRUNC_DIV_EXPR : RDIV_EXPR; break; case EXP::mod: code = e->e1->type->isfloating () ? FLOAT_MOD_EXPR : TRUNC_MOD_EXPR; break; case EXP::and_: code = BIT_AND_EXPR; break; case EXP::or_: code = BIT_IOR_EXPR; break; case EXP::xor_: code = BIT_XOR_EXPR; break; case EXP::leftShift: code = LSHIFT_EXPR; break; case EXP::rightShift: code = RSHIFT_EXPR; break; case EXP::unsignedRightShift: code = UNSIGNED_RSHIFT_EXPR; break; default: gcc_unreachable (); } this->result_ = binary_op (code, build_ctype (e->type), build_expr (e->e1), build_expr (e->e2)); } /* Build a concat expression, which concatenates two or more arrays of the same type, producing a dynamic array with the result. If one operand is an element type, that element is converted to an array of length 1. */ void visit (CatExp *e) final override { Type *tb1 = e->e1->type->toBasetype (); Type *tb2 = e->e2->type->toBasetype (); Type *etype; if (tb1->ty == TY::Tarray || tb1->ty == TY::Tsarray) etype = tb1->nextOf (); else etype = tb2->nextOf (); tree result; if (e->e1->op == EXP::concatenate) { /* Flatten multiple concatenations to an array. So the expression ((a ~ b) ~ c) becomes [a, b, c] */ int ndims = 2; for (Expression *ex = e->e1; ex->op == EXP::concatenate;) { if (ex->op == EXP::concatenate) { ex = ex->isCatExp ()->e1; ndims++; } } /* Store all concatenation args to a temporary byte[][ndims] array. */ Type *targselem = Type::tint8->arrayOf (); tree var = build_local_temp (make_array_type (targselem, ndims)); /* Loop through each concatenation from right to left. */ vec *elms = NULL; CatExp *ce = e; int dim = ndims - 1; for (Expression *oe = ce->e2; oe != NULL; (ce->e1->op != EXP::concatenate ? (oe = ce->e1) : (ce = ce->e1->isCatExp (), oe = ce->e2))) { tree arg = d_array_convert (etype, oe); tree index = size_int (dim); CONSTRUCTOR_APPEND_ELT (elms, index, d_save_expr (arg)); /* Finished pushing all arrays. */ if (oe == ce->e1) break; dim -= 1; } /* Check there is no logic bug in constructing byte[][] of arrays. */ gcc_assert (dim == 0); tree init = build_constructor (TREE_TYPE (var), elms); var = compound_expr (modify_expr (var, init), var); tree arrs = d_array_value (build_ctype (targselem->arrayOf ()), size_int (ndims), build_address (var)); result = build_libcall (LIBCALL_ARRAYCATNTX, e->type, 2, build_typeinfo (e, e->type), arrs); } else { /* Handle single concatenation (a ~ b). */ result = build_libcall (LIBCALL_ARRAYCATT, e->type, 3, build_typeinfo (e, e->type), d_array_convert (etype, e->e1), d_array_convert (etype, e->e2)); } this->result_ = result; } /* Build an assignment operator expression. The right operand is implicitly converted to the type of the left operand, and assigned to it. */ void visit (BinAssignExp *e) final override { tree_code code; Expression *e1b = e->e1; switch (e->op) { case EXP::addAssign: code = PLUS_EXPR; break; case EXP::minAssign: code = MINUS_EXPR; break; case EXP::mulAssign: code = MULT_EXPR; break; case EXP::divAssign: code = e->e1->type->isintegral () ? TRUNC_DIV_EXPR : RDIV_EXPR; break; case EXP::modAssign: code = e->e1->type->isfloating () ? FLOAT_MOD_EXPR : TRUNC_MOD_EXPR; break; case EXP::andAssign: code = BIT_AND_EXPR; break; case EXP::orAssign: code = BIT_IOR_EXPR; break; case EXP::xorAssign: code = BIT_XOR_EXPR; break; case EXP::powAssign: gcc_unreachable (); case EXP::leftShiftAssign: code = LSHIFT_EXPR; break; case EXP::rightShiftAssign: case EXP::unsignedRightShiftAssign: /* Use the original lhs type before it was promoted. The left operand of `>>>=' does not undergo integral promotions before shifting. Strip off casts just incase anyway. */ while (e1b->op == EXP::cast_) { CastExp *ce = e1b->isCastExp (); gcc_assert (same_type_p (ce->type, ce->to)); e1b = ce->e1; } code = (e->op == EXP::rightShiftAssign) ? RSHIFT_EXPR : UNSIGNED_RSHIFT_EXPR; break; default: gcc_unreachable (); } tree exp = binop_assignment (code, e1b, e->e2); this->result_ = convert_expr (exp, e1b->type, e->type); } /* Build a concat assignment expression. The right operand is appended to the left operand. */ void visit (CatAssignExp *e) final override { Type *tb1 = e->e1->type->toBasetype (); Type *tb2 = e->e2->type->toBasetype (); Type *etype = tb1->nextOf ()->toBasetype (); /* Save the address of `e1', so it can be evaluated first. As all D run-time library functions for concat assignments update `e1' in-place and then return its value, the saved address can also be used as the result of this expression as well. */ tree lhs = build_expr (e->e1); tree lexpr = stabilize_expr (&lhs); tree ptr = d_save_expr (build_address (lhs)); tree result = NULL_TREE; if (tb1->ty == TY::Tarray && tb2->ty == TY::Tdchar && (etype->ty == TY::Tchar || etype->ty == TY::Twchar)) { /* Append a dchar to a char[] or wchar[]: The assignment is handled by the D run-time library, so only need to call `_d_arrayappend[cw]d(&e1, e2)' */ libcall_fn libcall = (etype->ty == TY::Tchar) ? LIBCALL_ARRAYAPPENDCD : LIBCALL_ARRAYAPPENDWD; result = build_libcall (libcall, e->type, 2, ptr, build_expr (e->e2)); } else { /* Appending an element or array to another array has already been handled by the front-end. */ gcc_assert (tb1->ty == TY::Tarray || tb2->ty == TY::Tsarray); gcc_unreachable (); } /* Construct in order: ptr = &e1, _d_arrayappend(ptr, e2), *ptr; */ result = compound_expr (compound_expr (lexpr, ptr), result); this->result_ = compound_expr (result, build_deref (ptr)); } /* Build an assignment expression. The right operand is implicitly converted to the type of the left operand, and assigned to it. */ void visit (AssignExp *e) final override { /* First, handle special assignment semantics. */ /* Look for array.length = n; */ if (e->e1->op == EXP::arrayLength) { /* This case should have been rewritten to `_d_arraysetlengthT` in the semantic phase. */ gcc_unreachable (); } /* Look for exp = noreturn; */ if (e->e2->type->isTypeNoreturn ()) { /* If the RHS is a `noreturn' expression, there is no point generating any code for the assignment, just evaluate side effects. */ tree t1 = build_expr (e->e1); tree t2 = build_expr (e->e2); this->result_ = compound_expr (t1, t2); return; } /* Look for array[] = n; */ if (e->e1->op == EXP::slice) { SliceExp *se = e->e1->isSliceExp (); Type *stype = se->e1->type->toBasetype (); Type *etype = stype->nextOf ()->toBasetype (); /* Determine if we need to run postblit or dtor. */ bool postblit = needs_postblit (etype) && lvalue_p (e->e2); bool destructor = needs_dtor (etype); if (e->memset == MemorySet::blockAssign) { /* Set a range of elements to one value. */ tree t1 = build_expr (e->e1); tree t2 = build_expr (e->e2); tree result; /* Extract any array bounds checks from the slice expression. */ tree init = stabilize_expr (&t1); t1 = d_save_expr (t1); if ((postblit || destructor) && e->op != EXP::blit) { /* Need to call postblit/destructor as part of assignment. Construction has already been handled by the front-end. */ gcc_assert (e->op != EXP::construct); /* So we can call postblits on const/immutable objects. */ Type *tm = etype->unSharedOf ()->mutableOf (); tree ti = build_typeinfo (e, tm); /* Generate: _d_arraysetassign (t1.ptr, &t2, t1.length, ti); */ result = build_libcall (LIBCALL_ARRAYSETASSIGN, Type::tvoid, 4, d_array_ptr (t1), build_address (t2), d_array_length (t1), ti); } else if (integer_zerop (t2)) { tree size = size_mult_expr (d_array_length (t1), size_int (etype->size ())); result = build_memset_call (d_array_ptr (t1), size); } else result = build_array_set (d_array_ptr (t1), d_array_length (t1), t2); result = compound_expr (init, result); this->result_ = compound_expr (result, t1); } else { /* Perform a memcpy operation. */ gcc_assert (e->e2->type->ty != TY::Tpointer); if (!postblit && !destructor) { tree t1 = d_save_expr (d_array_convert (e->e1)); tree t2 = d_save_expr (d_array_convert (e->e2)); /* References to array data. */ tree t1ptr = d_array_ptr (t1); tree t1len = d_array_length (t1); tree t2ptr = d_array_ptr (t2); /* Generate: memcpy(to, from, size) */ tree size = size_mult_expr (t1len, size_int (etype->size ())); tree result = build_memcpy_call (t1ptr, t2ptr, size); /* Insert check that array lengths match and do not overlap. */ if (array_bounds_check ()) { /* tlencmp = (t1len == t2len) */ tree t2len = d_array_length (t2); tree tlencmp = build_boolop (EQ_EXPR, t1len, t2len); /* toverlap = (t1ptr + size <= t2ptr || t2ptr + size <= t1ptr) */ tree t1ptrcmp = build_boolop (LE_EXPR, build_offset (t1ptr, size), t2ptr); tree t2ptrcmp = build_boolop (LE_EXPR, build_offset (t2ptr, size), t1ptr); tree toverlap = build_boolop (TRUTH_ORIF_EXPR, t1ptrcmp, t2ptrcmp); /* (tlencmp && toverlap) ? memcpy() : _d_arraybounds() */ tree tassert = build_array_bounds_call (e->loc); tree tboundscheck = build_boolop (TRUTH_ANDIF_EXPR, tlencmp, toverlap); result = build_condition (void_type_node, tboundscheck, result, tassert); } this->result_ = compound_expr (result, t1); } else if ((postblit || destructor) && e->op != EXP::blit && e->op != EXP::construct) { /* Assigning to a non-trivially copyable array has already been handled by the front-end. */ gcc_unreachable (); } else { /* Generate: _d_arraycopy() */ this->result_ = build_libcall (LIBCALL_ARRAYCOPY, e->type, 3, size_int (etype->size ()), d_array_convert (e->e2), d_array_convert (e->e1)); } } return; } /* Look for reference initializations. */ if (e->memset == MemorySet::referenceInit) { gcc_assert (e->op == EXP::construct || e->op == EXP::blit); gcc_assert (e->e1->op == EXP::variable); Declaration *decl = e->e1->isVarExp ()->var; if (decl->storage_class & (STCout | STCref)) { tree t2 = convert_for_assignment (build_expr (e->e2), e->e2->type, e->e1->type); tree t1 = build_expr (e->e1); /* Want reference to lhs, not indirect ref. */ t1 = TREE_OPERAND (t1, 0); t2 = build_address (t2); this->result_ = indirect_ref (build_ctype (e->type), build_assign (INIT_EXPR, t1, t2)); return; } } /* Other types of assignments that may require post construction. */ Type *tb1 = e->e1->type->toBasetype (); tree_code modifycode = (e->op == EXP::construct) ? INIT_EXPR : MODIFY_EXPR; /* Look for struct assignment. */ if (tb1->ty == TY::Tstruct) { tree t1 = build_expr (e->e1); tree t2 = convert_for_assignment (build_expr (e->e2, false, true), e->e2->type, e->e1->type); StructDeclaration *sd = tb1->isTypeStruct ()->sym; /* Look for struct = 0. */ if (e->e2->op == EXP::int64) { /* Use memset to fill struct. */ gcc_assert (e->op == EXP::blit); tree result = build_memset_call (t1); /* Maybe set-up hidden pointer to outer scope context. */ if (sd->isNested ()) { tree field = get_symbol_decl (sd->vthis); tree value = build_vthis (sd); tree vthis_exp = modify_expr (component_ref (t1, field), value); result = compound_expr (result, vthis_exp); } this->result_ = compound_expr (result, t1); } else { /* Simple struct literal assignment. */ tree init = NULL_TREE; /* Fill any alignment holes in the struct using memset. */ if ((e->op == EXP::construct || (e->e2->op == EXP::structLiteral && e->op == EXP::blit)) && (sd->isUnionDeclaration () || !identity_compare_p (sd))) { t1 = stabilize_reference (t1); init = build_memset_call (t1); } /* Elide generating assignment if init is all zeroes. */ if (init != NULL_TREE && initializer_zerop (t2)) this->result_ = compound_expr (init, t1); else { tree result = build_assign (modifycode, t1, t2); this->result_ = compound_expr (init, result); } } return; } /* Look for static array assignment. */ if (tb1->ty == TY::Tsarray) { /* Look for array = 0. */ if (e->e2->op == EXP::int64) { /* Use memset to fill the array. */ gcc_assert (e->op == EXP::blit); this->result_ = build_memset_call (build_expr (e->e1)); return; } Type *etype = tb1->nextOf (); gcc_assert (e->e2->type->toBasetype ()->ty == TY::Tsarray); /* Determine if we need to run postblit. */ const bool postblit = needs_postblit (etype); const bool destructor = needs_dtor (etype); const bool lvalue = lvalue_p (e->e2); /* Optimize static array assignment with array literal. Even if the elements in rhs are all rvalues and don't have to call postblits, this assignment should call dtors on old assigned elements. */ if ((!postblit && !destructor) || (e->op == EXP::construct && e->e2->op == EXP::arrayLiteral) || (e->op == EXP::construct && !lvalue && postblit) || (e->op == EXP::blit || e->e1->type->size () == 0)) { tree t1 = build_expr (e->e1); tree t2 = convert_for_assignment (build_expr (e->e2), e->e2->type, e->e1->type); this->result_ = build_assign (modifycode, t1, t2); return; } /* All other kinds of lvalue or rvalue static array assignment. Array construction has already been handled by the front-end. */ gcc_assert (e->op != EXP::construct); gcc_unreachable (); } /* Simple assignment. */ tree t1 = build_expr (e->e1); tree t2 = convert_for_assignment (build_expr (e->e2), e->e2->type, e->e1->type); this->result_ = build_assign (modifycode, t1, t2); } /* Build a throw expression. */ void visit (ThrowExp *e) final override { tree arg = build_expr_dtor (e->e1); this->result_ = build_libcall (LIBCALL_THROW, Type::tvoid, 1, arg); } /* Build a postfix expression. */ void visit (PostExp *e) final override { tree result; if (e->op == EXP::plusPlus) { result = build2 (POSTINCREMENT_EXPR, build_ctype (e->type), build_expr (e->e1), build_expr (e->e2)); } else if (e->op == EXP::minusMinus) { result = build2 (POSTDECREMENT_EXPR, build_ctype (e->type), build_expr (e->e1), build_expr (e->e2)); } else gcc_unreachable (); TREE_SIDE_EFFECTS (result) = 1; this->result_ = result; } /* Build an index expression. */ void visit (IndexExp *e) final override { Type *tb1 = e->e1->type->toBasetype (); if (tb1->ty == TY::Taarray) { /* Get the key for the associative array. */ Type *tkey = tb1->isTypeAArray ()->index->toBasetype (); tree key = convert_expr (build_expr (e->e2), e->e2->type, tkey); libcall_fn libcall; tree tinfo, ptr; if (e->modifiable) { libcall = LIBCALL_AAGETY; ptr = build_address (build_expr (e->e1)); tinfo = build_typeinfo (e, tb1->unSharedOf ()->mutableOf ()); } else { libcall = LIBCALL_AAGETRVALUEX; ptr = build_expr (e->e1); tinfo = build_typeinfo (e, tkey); } /* Index the associative array. */ tree result = build_libcall (libcall, e->type->pointerTo (), 4, ptr, tinfo, size_int (tb1->nextOf ()->size ()), build_address (key)); if (!e->indexIsInBounds && array_bounds_check ()) { tree tassert = build_array_bounds_call (e->loc); result = d_save_expr (result); result = build_condition (TREE_TYPE (result), d_truthvalue_conversion (result), result, tassert); } this->result_ = indirect_ref (build_ctype (e->type), result); } else { /* Get the array and length for static and dynamic arrays. */ tree array = d_save_expr (build_expr (e->e1)); tree length = NULL_TREE; if (tb1->ty != TY::Tpointer) length = get_array_length (array, tb1); else gcc_assert (e->lengthVar == NULL); /* The __dollar variable just becomes a placeholder for the actual length. */ if (e->lengthVar) e->lengthVar->csym = length; /* Generate the index. */ tree index = build_expr (e->e2); /* If it's a static array and the index is constant, the front end has already checked the bounds. */ if (tb1->ty != TY::Tpointer) index = build_bounds_index_condition (e, index, length); /* Convert vectors to their underlying array type. */ if (VECTOR_TYPE_P (TREE_TYPE (array))) { tree array_type = build_array_type_nelts (TREE_TYPE (TREE_TYPE (array)), TYPE_VECTOR_SUBPARTS (TREE_TYPE (array))); d_mark_addressable (array, false); array = build1 (VIEW_CONVERT_EXPR, array_type, array); } if (TREE_CODE (TREE_TYPE (array)) == ARRAY_TYPE) { /* Generate `array[index]'. When the index is non-constant, we must mark the array as addressable because we'll need to do pointer arithmetic on its address. */ if (TREE_CODE (index) != INTEGER_CST) d_mark_addressable (array); this->result_ = build4 (ARRAY_REF, TREE_TYPE (TREE_TYPE (array)), array, index, NULL_TREE, NULL_TREE); } else { /* Generate `array.ptr[index]'. */ tree ptr = convert_expr (array, tb1, tb1->nextOf ()->pointerTo ()); ptr = void_okay_p (ptr); this->result_ = indirect_ref (TREE_TYPE (TREE_TYPE (ptr)), build_pointer_index (ptr, index)); } } } /* Build a comma expression. The type is the type of the right operand. */ void visit (CommaExp *e) final override { tree t1 = build_expr (e->e1); tree t2 = build_expr (e->e2); tree type = e->type ? build_ctype (e->type) : void_type_node; this->result_ = build2 (COMPOUND_EXPR, type, t1, t2); } /* Build an array length expression. Returns the number of elements in the array. The result is of type size_t. */ void visit (ArrayLengthExp *e) final override { if (e->e1->type->toBasetype ()->ty == TY::Tarray) this->result_ = d_array_length (build_expr (e->e1)); else { /* Static arrays have already been handled by the front-end. */ error ("unexpected type for array length: %qs", e->type->toChars ()); this->result_ = error_mark_node; } } /* Build a delegate pointer expression. This will return the frame pointer value as a type void*. */ void visit (DelegatePtrExp *e) final override { tree t1 = build_expr (e->e1); this->result_ = delegate_object (t1); } /* Build a delegate function pointer expression. This will return the function pointer value as a function type. */ void visit (DelegateFuncptrExp *e) final override { tree t1 = build_expr (e->e1); this->result_ = delegate_method (t1); } /* Build a slice expression. */ void visit (SliceExp *e) final override { Type *tb = e->type->toBasetype (); Type *tb1 = e->e1->type->toBasetype (); gcc_assert (tb->ty == TY::Tarray || tb->ty == TY::Tsarray); /* Use convert-to-dynamic-array code if possible. */ if (!e->lwr) { tree result = build_expr (e->e1); if (e->e1->type->toBasetype ()->ty == TY::Tsarray) result = convert_expr (result, e->e1->type, e->type); this->result_ = result; return; } else gcc_assert (e->upr != NULL); /* Get the data pointer and length for static and dynamic arrays. */ tree array = d_save_expr (build_expr (e->e1)); tree ptr = convert_expr (array, tb1, tb1->nextOf ()->pointerTo ()); tree length = NULL_TREE; /* Our array is already a SAVE_EXPR if necessary, so we don't make length a SAVE_EXPR which is, at most, a COMPONENT_REF on top of array. */ if (tb1->ty != TY::Tpointer) length = get_array_length (array, tb1); else gcc_assert (e->lengthVar == NULL); /* The __dollar variable just becomes a placeholder for the actual length. */ if (e->lengthVar) e->lengthVar->csym = length; /* Generate upper and lower bounds. */ tree lwr_tree = d_save_expr (build_expr (e->lwr)); tree upr_tree = d_save_expr (build_expr (e->upr)); /* If the upper bound has any side effects, then the lower bound should be copied to a temporary always. */ if (TREE_CODE (upr_tree) == SAVE_EXPR && TREE_CODE (lwr_tree) != SAVE_EXPR) lwr_tree = save_expr (lwr_tree); /* Adjust the .ptr offset. */ if (!integer_zerop (lwr_tree)) { tree ptrtype = TREE_TYPE (ptr); ptr = build_pointer_index (void_okay_p (ptr), lwr_tree); ptr = build_nop (ptrtype, ptr); } /* Nothing more to do for static arrays, their bounds checking has been done at compile-time. */ if (tb->ty == TY::Tsarray) { this->result_ = indirect_ref (build_ctype (e->type), ptr); return; } else gcc_assert (tb->ty == TY::Tarray); /* Generate bounds checking code. */ tree newlength = build_bounds_slice_condition (e, lwr_tree, upr_tree, length); tree result = d_array_value (build_ctype (e->type), newlength, ptr); this->result_ = compound_expr (array, result); } /* Build a cast expression, which converts the given unary expression to the type of result. */ void visit (CastExp *e) final override { Type *ebtype = e->e1->type->toBasetype (); Type *tbtype = e->to->toBasetype (); tree result = build_expr (e->e1, this->constp_, this->literalp_); /* Just evaluate e1 if it has any side effects. */ if (tbtype->ty == TY::Tvoid) this->result_ = build_nop (build_ctype (tbtype), result); else this->result_ = convert_for_rvalue (result, ebtype, tbtype); } /* Build a delete expression. */ void visit (DeleteExp *e) final override { tree t1 = build_expr (e->e1); Type *tb1 = e->e1->type->toBasetype (); if (tb1->ty == TY::Tclass) { /* For class object references, if there is a destructor for that class, the destructor is called for the object instance. */ gcc_assert (e->e1->op == EXP::variable); VarDeclaration *v = e->e1->isVarExp ()->var->isVarDeclaration (); gcc_assert (v && v->onstack ()); libcall_fn libcall = tb1->isClassHandle ()->isInterfaceDeclaration () ? LIBCALL_CALLINTERFACEFINALIZER : LIBCALL_CALLFINALIZER; this->result_ = build_libcall (libcall, Type::tvoid, 1, t1); return; } else { error ("don%'t know how to delete %qs", e->e1->toChars ()); this->result_ = error_mark_node; } } /* Build a remove expression, which removes a particular key from an associative array. */ void visit (RemoveExp *e) final override { /* Check that the array is actually an associative array. */ if (e->e1->type->toBasetype ()->ty == TY::Taarray) { Type *tb = e->e1->type->toBasetype (); Type *tkey = tb->isTypeAArray ()->index->toBasetype (); tree index = convert_expr (build_expr (e->e2), e->e2->type, tkey); this->result_ = build_libcall (LIBCALL_AADELX, Type::tbool, 3, build_expr (e->e1), build_typeinfo (e, tkey), build_address (index)); } else { error ("%qs is not an associative array", e->e1->toChars ()); this->result_ = error_mark_node; } } /* Build an unary not expression. */ void visit (NotExp *e) final override { tree result = convert_for_condition (build_expr (e->e1), e->e1->type); /* Need to convert to boolean type or this will fail. */ result = fold_build1 (TRUTH_NOT_EXPR, d_bool_type, result); this->result_ = d_convert (build_ctype (e->type), result); } /* Build a compliment expression, where all the bits in the value are complemented. Note: unlike in C, the usual integral promotions are not performed prior to the complement operation. */ void visit (ComExp *e) final override { TY ty1 = e->e1->type->toBasetype ()->ty; gcc_assert (ty1 != TY::Tarray && ty1 != TY::Tsarray); this->result_ = fold_build1 (BIT_NOT_EXPR, build_ctype (e->type), build_expr (e->e1)); } /* Build an unary negation expression. */ void visit (NegExp *e) final override { TY ty1 = e->e1->type->toBasetype ()->ty; gcc_assert (ty1 != TY::Tarray && ty1 != TY::Tsarray); tree type = build_ctype (e->type); tree expr = build_expr (e->e1); /* If the operation needs excess precision. */ tree eptype = excess_precision_type (type); if (eptype != NULL_TREE) expr = d_convert (eptype, expr); else eptype = type; tree ret = fold_build1 (NEGATE_EXPR, eptype, expr); this->result_ = d_convert (type, ret); } /* Build a pointer index expression. */ void visit (PtrExp *e) final override { Type *tnext = NULL; size_t offset; tree result; if (e->e1->op == EXP::add) { AddExp *ae = e->e1->isAddExp (); if (ae->e1->op == EXP::address && ae->e2->isConst () && ae->e2->type->isintegral ()) { Expression *ex = ae->e1->isAddrExp ()->e1; tnext = ex->type->toBasetype (); result = build_expr (ex); offset = ae->e2->toUInteger (); } } else if (e->e1->op == EXP::symbolOffset) { SymOffExp *se = e->e1->isSymOffExp (); if (!declaration_reference_p (se->var)) { tnext = se->var->type->toBasetype (); result = get_decl_tree (se->var); offset = se->offset; } } /* Produce better code by converting *(#record + n) to COMPONENT_REFERENCE. Otherwise, the variable will always be allocated in memory because its address is taken. */ if (tnext && tnext->ty == TY::Tstruct) { StructDeclaration *sd = tnext->isTypeStruct ()->sym; for (size_t i = 0; i < sd->fields.length; i++) { VarDeclaration *field = sd->fields[i]; if (field->offset == offset && same_type_p (field->type, e->type)) { /* Catch errors, backend will ICE otherwise. */ if (error_operand_p (result)) this->result_ = result; else { result = component_ref (result, get_symbol_decl (field)); this->result_ = result; } return; } else if (field->offset > offset) break; } } this->result_ = indirect_ref (build_ctype (e->type), build_expr (e->e1)); } /* Build an unary address expression. */ void visit (AddrExp *e) final override { tree type = build_ctype (e->type); tree exp; /* The frontend optimizer can convert const symbol into a struct literal. Taking the address of a struct literal is otherwise illegal. */ if (e->e1->op == EXP::structLiteral) { StructLiteralExp *sle = e->e1->isStructLiteralExp ()->origin; gcc_assert (sle != NULL); /* Build the reference symbol, the decl is built first as the initializer may have recursive references. */ if (!sle->sym) { sle->sym = build_artificial_decl (build_ctype (sle->type), NULL_TREE, "S"); DECL_INITIAL (sle->sym) = build_expr (sle, true); d_pushdecl (sle->sym); rest_of_decl_compilation (sle->sym, 1, 0); } exp = sle->sym; } else exp = build_expr (e->e1, this->constp_, this->literalp_); TREE_CONSTANT (exp) = 0; this->result_ = d_convert (type, build_address (exp)); } /* Build a function call expression. */ void visit (CallExp *e) final override { Type *tb = e->e1->type->toBasetype (); Expression *e1b = e->e1; tree callee = NULL_TREE; tree object = NULL_TREE; tree cleanup = NULL_TREE; tree returnvalue = NULL_TREE; TypeFunction *tf = NULL; /* Calls to delegates can sometimes look like this. */ if (e1b->op == EXP::comma) { e1b = e1b->isCommaExp ()->e2; gcc_assert (e1b->op == EXP::variable); Declaration *var = e1b->isVarExp ()->var; gcc_assert (var->isFuncDeclaration () && !var->needThis ()); } if (e1b->op == EXP::dotVariable && tb->ty != TY::Tdelegate) { DotVarExp *dve = e1b->isDotVarExp (); /* Don't modify the static initializer for struct literals. */ if (dve->e1->op == EXP::structLiteral) { StructLiteralExp *sle = dve->e1->isStructLiteralExp (); sle->useStaticInit = false; } FuncDeclaration *fd = dve->var->isFuncDeclaration (); if (fd != NULL) { /* Get the correct callee from the DotVarExp object. */ tree fndecl = get_symbol_decl (fd); AggregateDeclaration *ad = fd->isThis (); /* Static method; ignore the object instance. */ if (!ad) callee = build_address (fndecl); else { 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); } if (TREE_CODE (thisexp) == CONSTRUCTOR) thisexp = force_target_expr (thisexp); /* Want reference to `this' object. */ if (!POINTER_TYPE_P (TREE_TYPE (thisexp))) thisexp = build_address (thisexp); /* Make the callee a virtual call. */ if (fd->isVirtual () && !fd->isFinalFunc () && !e->directcall) { tree fntype = build_pointer_type (TREE_TYPE (fndecl)); tree thistype = build_ctype (ad->handleType ()); thisexp = build_nop (thistype, d_save_expr (thisexp)); fndecl = build_vindex_ref (thisexp, fntype, fd->vtblIndex); } else fndecl = build_address (fndecl); /* C++ constructors return void, even though front-end semantic treats them as implicitly returning `this'. Set returnvalue to override the result of this expression. */ if (fd->isCtorDeclaration ()) { thisexp = d_save_expr (thisexp); returnvalue = thisexp; } callee = build_method_call (fndecl, thisexp, fd->type); } } } if (callee == NULL_TREE) callee = build_expr (e1b); if (METHOD_CALL_EXPR (callee)) { /* This could be a delegate expression (TY == Tdelegate), but not actually a delegate variable. */ if (e1b->op == EXP::dotVariable) { /* This gets the true function type, getting the function type from e1->type can sometimes be incorrect, such as when calling a `ref' return function. */ tf = get_function_type (e1b->isDotVarExp ()->var->type); } else tf = get_function_type (tb); extract_from_method_call (callee, callee, object); } else if (tb->ty == TY::Tdelegate) { /* Delegate call, extract .object and .funcptr from var. */ callee = d_save_expr (callee); tf = get_function_type (tb); object = delegate_object (callee); callee = delegate_method (callee); } else if (e1b->op == EXP::variable) { FuncDeclaration *fd = e1b->isVarExp ()->var->isFuncDeclaration (); gcc_assert (fd != NULL); tf = get_function_type (fd->type); if (fd->isNested ()) { /* Maybe re-evaluate symbol storage treating `fd' as public. */ if (call_by_alias_p (d_function_chain->function, fd)) TREE_PUBLIC (callee) = 1; object = get_frame_for_symbol (fd); } else if (fd->needThis ()) { error_at (make_location_t (e1b->loc), "need % to access member %qs", fd->toChars ()); /* Continue compiling... */ object = null_pointer_node; } } else { /* Normal direct function call. */ tf = get_function_type (tb); } gcc_assert (tf != NULL); /* Now we have the type, callee and maybe object reference, build the call expression. */ tree exp = d_build_call (tf, callee, object, e->arguments); if (returnvalue != NULL_TREE) exp = compound_expr (exp, returnvalue); if (tf->isref ()) exp = build_deref (exp); /* Some library calls are defined to return a generic type. this->type is the real type we want to return. */ 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. */ if (cleanup != NULL_TREE) { tree init = TARGET_EXPR_INITIAL (cleanup); TARGET_EXPR_INITIAL (cleanup) = compound_expr (init, exp); /* Keep the return value outside the TARGET_EXPR. */ if (returnvalue != NULL_TREE) cleanup = compound_expr (cleanup, TREE_OPERAND (exp, 1)); exp = cleanup; } this->result_ = exp; } /* Build a delegate expression. */ void visit (DelegateExp *e) final override { if (e->func->semanticRun == PASS::semantic3done) { /* Add the function as nested function if it belongs to this module. ie: it is a member of this module, or it is a template instance. */ Dsymbol *owner = e->func->toParent (); while (!owner->isTemplateInstance () && owner->toParent ()) owner = owner->toParent (); if (owner->isTemplateInstance () || owner == d_function_chain->module) build_decl_tree (e->func); } tree fndecl; tree object; if (e->func->isNested () && !e->func->isThis ()) { if (e->e1->op == EXP::null_) object = build_expr (e->e1); else object = get_frame_for_symbol (e->func); fndecl = build_address (get_symbol_decl (e->func)); } else { if (!e->func->isThis ()) { error ("delegates are only for non-static functions"); this->result_ = error_mark_node; return; } object = build_expr (e->e1); /* Want reference to `this' object. */ if (e->e1->type->ty != TY::Tclass && e->e1->type->ty != TY::Tpointer) object = build_address (object); /* Object reference could be the outer `this' field of a class or closure of type `void*'. Cast it to the right type. */ if (e->e1->type->ty == TY::Tclass) object = d_convert (build_ctype (e->e1->type), object); fndecl = get_symbol_decl (e->func); /* Get pointer to function out of the virtual table. */ if (e->func->isVirtual () && !e->func->isFinalFunc () && e->e1->op != EXP::super_ && e->e1->op != EXP::dotType) { tree fntype = build_pointer_type (TREE_TYPE (fndecl)); object = d_save_expr (object); fndecl = build_vindex_ref (object, fntype, e->func->vtblIndex); } else fndecl = build_address (fndecl); } this->result_ = build_method_call (fndecl, object, e->type); } /* Build a type component expression. */ void visit (DotTypeExp *e) final override { /* Just a pass through to underlying expression. */ this->result_ = build_expr (e->e1); } /* Build a component reference expression. */ void visit (DotVarExp *e) final override { VarDeclaration *vd = e->var->isVarDeclaration (); /* This could also be a function, but relying on that being taken care of by the visitor interface for CallExp. */ if (vd != NULL) { if (!vd->isField ()) this->result_ = get_decl_tree (vd); else { tree object = build_expr (e->e1); Type *tb = e->e1->type->toBasetype (); if (tb->ty != TY::Tstruct) object = build_deref (object); /* __complex is represented as a struct in the front-end, but underlying is really a complex type. */ if (e->e1->type->ty == TY::Tenum && e->e1->type->isTypeEnum ()->sym->isSpecial ()) object = underlying_complex_expr (build_ctype (tb), object); this->result_ = component_ref (object, get_symbol_decl (vd)); } } else { error ("%qs is not a field, but a %qs", e->var->toChars (), e->var->kind ()); this->result_ = error_mark_node; } } /* Build an assert expression, used to declare conditions that must hold at that a given point in the program. */ void visit (AssertExp *e) final override { Type *tb1 = e->e1->type->toBasetype (); tree arg = build_expr (e->e1); tree tmsg = NULL_TREE; tree assert_pass = void_node; tree assert_fail; if (global.params.useAssert == CHECKENABLEon && !checkaction_trap_p ()) { /* Generate: ((bool) e1 ? (void)0 : _d_assert (...)) or: (e1 != null ? e1._invariant() : _d_assert (...)) */ bool unittest_p = d_function_chain->function->isUnitTestDeclaration (); libcall_fn libcall; if (e->msg) { tmsg = build_expr_dtor (e->msg); libcall = unittest_p ? LIBCALL_UNITTEST_MSG : LIBCALL_ASSERT_MSG; } else libcall = unittest_p ? LIBCALL_UNITTESTP : LIBCALL_ASSERTP; /* Build a call to _d_assert(). */ assert_fail = build_assert_call (e->loc, libcall, tmsg); if (global.params.useInvariants == CHECKENABLEon) { /* If the condition is a D class or struct object with an invariant, call it if the condition result is true. */ if (tb1->ty == TY::Tclass) { ClassDeclaration *cd = tb1->isClassHandle (); if (!cd->isInterfaceDeclaration () && !cd->isCPPclass ()) { arg = d_save_expr (arg); assert_pass = build_libcall (LIBCALL_INVARIANT, Type::tvoid, 1, arg); } } else if (tb1->ty == TY::Tpointer && tb1->nextOf ()->ty == TY::Tstruct) { StructDeclaration *sd = tb1->nextOf ()->isTypeStruct ()->sym; if (sd->inv != NULL) { Expressions args; arg = d_save_expr (arg); assert_pass = d_build_call_expr (sd->inv, arg, &args); } } } } else if (global.params.useAssert == CHECKENABLEon && checkaction_trap_p ()) { /* Generate: __builtin_trap() */ tree fn = builtin_decl_explicit (BUILT_IN_TRAP); assert_fail = build_call_expr (fn, 0); } else { /* Assert contracts are turned off. */ this->result_ = void_node; return; } /* Build condition that we are asserting in this contract. */ tree condition = convert_for_condition (arg, e->e1->type); /* We expect the condition to always be true, as what happens if an assert contract is false is undefined behavior. */ tree fn = builtin_decl_explicit (BUILT_IN_EXPECT); tree arg_types = TYPE_ARG_TYPES (TREE_TYPE (fn)); tree pred_type = TREE_VALUE (arg_types); tree expected_type = TREE_VALUE (TREE_CHAIN (arg_types)); condition = build_call_expr (fn, 2, d_convert (pred_type, condition), build_int_cst (expected_type, 1)); condition = d_truthvalue_conversion (condition); this->result_ = build_vcondition (condition, assert_pass, assert_fail); } /* Build a declaration expression. */ void visit (DeclarationExp *e) final override { /* Compile the declaration. */ push_stmt_list (); build_decl_tree (e->declaration); tree result = pop_stmt_list (); /* Construction of an array for typesafe-variadic function arguments can cause an empty STMT_LIST here. This can causes problems during gimplification. */ if (TREE_CODE (result) == STATEMENT_LIST && !STATEMENT_LIST_HEAD (result)) result = build_empty_stmt (input_location); this->result_ = result; } /* Build a typeid expression. Returns an instance of class TypeInfo corresponding to. */ void visit (TypeidExp *e) final override { if (Type *tid = isType (e->obj)) { tree ti = build_typeinfo (e, tid); /* If the typeinfo is at an offset. */ if (tid->vtinfo->offset) ti = build_offset (ti, size_int (tid->vtinfo->offset)); this->result_ = build_nop (build_ctype (e->type), ti); } else if (Expression *tid = isExpression (e->obj)) { Type *type = tid->type->toBasetype (); assert (type->ty == TY::Tclass); /* Generate **classptr to get the classinfo. */ tree ci = build_expr (tid); ci = indirect_ref (ptr_type_node, ci); ci = indirect_ref (ptr_type_node, ci); /* Add extra indirection for interfaces. */ if (type->isTypeClass ()->sym->isInterfaceDeclaration ()) ci = indirect_ref (ptr_type_node, ci); this->result_ = build_nop (build_ctype (e->type), ci); } else gcc_unreachable (); } /* Build a function/lambda expression. */ void visit (FuncExp *e) final override { Type *ftype = e->type->toBasetype (); /* This check is for lambda's, remove `vthis' as function isn't nested. */ if (e->fd->tok == TOK::reserved && ftype->ty == TY::Tpointer) { e->fd->tok = TOK::function_; e->fd->vthis = NULL; } /* Compile the function literal body. */ build_decl_tree (e->fd); /* If nested, this will be a trampoline. */ if (e->fd->isNested ()) { tree func = build_address (get_symbol_decl (e->fd)); tree object; if (this->constp_) { /* Static delegate variables have no context pointer. */ object = null_pointer_node; this->result_ = build_method_call (func, object, e->fd->type); TREE_CONSTANT (this->result_) = 1; } else { object = get_frame_for_symbol (e->fd); this->result_ = build_method_call (func, object, e->fd->type); } } else { this->result_ = build_nop (build_ctype (e->type), build_address (get_symbol_decl (e->fd))); } } /* Build a halt expression. */ void visit (HaltExp *) final override { /* Should we use trap() or abort()? */ tree ttrap = builtin_decl_explicit (BUILT_IN_TRAP); this->result_ = build_call_expr (ttrap, 0); } /* Build a symbol pointer offset expression. */ void visit (SymOffExp *e) final override { /* Build the address and offset of the symbol. */ size_t soffset = e->isSymOffExp ()->offset; tree result = get_decl_tree (e->var); TREE_USED (result) = 1; if (declaration_reference_p (e->var)) gcc_assert (POINTER_TYPE_P (TREE_TYPE (result))); else result = build_address (result); if (!soffset) result = d_convert (build_ctype (e->type), result); else { tree offset = size_int (soffset); result = build_nop (build_ctype (e->type), build_offset (result, offset)); } this->result_ = result; } /* Build a variable expression. */ void visit (VarExp *e) final override { if (e->var->needThis ()) { error ("need % to access member %qs", e->var->ident->toChars ()); this->result_ = error_mark_node; return; } else if (e->var->ident == Identifier::idPool ("__ctfe")) { /* __ctfe is always false at run-time. */ this->result_ = integer_zero_node; return; } /* This check is same as is done in FuncExp for lambdas. */ FuncLiteralDeclaration *fld = e->var->isFuncLiteralDeclaration (); if (fld != NULL) { if (fld->tok == TOK::reserved) { fld->tok = TOK::function_; fld->vthis = NULL; } /* Compiler the function literal body. */ build_decl_tree (fld); } if (this->constp_) { /* Want the initializer, not the expression. */ VarDeclaration *var = e->var->isVarDeclaration (); SymbolDeclaration *sdecl = e->var->isSymbolDeclaration (); tree init = NULL_TREE; if (var && (var->isConst () || var->isImmutable ()) && e->type->toBasetype ()->ty != TY::Tsarray && var->_init) { if (var->inuse) error_at (make_location_t (e->loc), "recursive reference %qs", e->toChars ()); else { var->inuse++; init = build_expr (initializerToExpression (var->_init), true); var->inuse--; } } else if (sdecl && sdecl->dsym) { if (StructDeclaration *sd = sdecl->dsym->isStructDeclaration ()) init = layout_struct_initializer (sd); else if (ClassDeclaration *cd = sdecl->dsym->isClassDeclaration ()) init = layout_class_initializer (cd); else gcc_unreachable (); } else error_at (make_location_t (e->loc), "non-constant expression %qs", e->toChars ()); if (init != NULL_TREE) this->result_ = init; else this->result_ = error_mark_node; } else { tree result = get_decl_tree (e->var); TREE_USED (result) = 1; /* The variable expression generated for `__traits(initSymbol)'. */ if (SymbolDeclaration *sd = e->var->isSymbolDeclaration ()) { if (e->type->isTypeDArray ()) { /* Generate a slice for non-zero initialized aggregates, otherwise create an empty array. */ gcc_assert (e->type == Type::tvoid->arrayOf ()->constOf ()); tree type = build_ctype (e->type); tree length = size_int (sd->dsym->structsize); tree ptr = (sd->dsym->isStructDeclaration () && sd->dsym->type->isZeroInit (e->loc)) ? null_pointer_node : build_address (result); this->result_ = d_array_value (type, length, ptr); return; } } /* For variables that are references - currently only out/inout arguments; objects don't count - evaluating the variable means we want what it refers to. */ if (declaration_reference_p (e->var)) result = indirect_ref (build_ctype (e->var->type), result); this->result_ = result; } } /* Build a this variable expression. */ void visit (ThisExp *e) final override { FuncDeclaration *fd = d_function_chain ? d_function_chain->function : NULL; tree result = NULL_TREE; if (e->var) result = get_decl_tree (e->var); else { gcc_assert (fd && fd->vthis); result = get_decl_tree (fd->vthis); } if (e->type->ty == TY::Tstruct) result = build_deref (result); this->result_ = result; } /* Build a new expression, which allocates memory either on the garbage collected heap or by using a class or struct specific allocator. */ void visit (NewExp *e) final override { Type *tb = e->type->toBasetype (); tree result; if (tb->ty == TY::Tclass) { /* Allocating a new class. */ tb = e->newtype->toBasetype (); ClassDeclaration *cd = tb->isTypeClass ()->sym; tree type = build_ctype (tb); tree setup_exp = NULL_TREE; tree new_call; if (e->onstack) { /* If being used as an initializer for a local variable with scope storage class, then the instance is allocated on the stack rather than the heap or using the class specific allocator. */ tree var = build_local_temp (TREE_TYPE (type)); new_call = build_nop (type, build_address (var)); setup_exp = modify_expr (var, aggregate_initializer_decl (cd)); } else if (global.params.ehnogc && e->thrownew) { /* Allocating a `@nogc' Exception with `_d_newThrowable' has already been handled by the front-end. */ gcc_unreachable (); } else { /* Generate: _d_newclass() */ tree arg = build_address (get_classinfo_decl (cd)); new_call = build_libcall (LIBCALL_NEWCLASS, tb, 1, arg); } /* Set the context pointer for nested classes. */ if (cd->isNested ()) { tree field = get_symbol_decl (cd->vthis); tree value = NULL_TREE; if (e->thisexp) { ClassDeclaration *tcd = e->thisexp->type->isClassHandle (); /* The class or function we're nested in. */ Dsymbol *outer = cd->toParentLocal (); value = build_expr (e->thisexp); if (outer != tcd) { ClassDeclaration *ocd = outer->isClassDeclaration (); int offset = 0; gcc_assert (ocd->isBaseOf (tcd, &offset)); /* Could just add offset... */ value = convert_expr (value, e->thisexp->type, ocd->type); } } else value = build_vthis (cd); if (value != NULL_TREE) { /* Generate: (new())->vthis = this; */ new_call = d_save_expr (new_call); field = component_ref (build_deref (new_call), field); setup_exp = compound_expr (setup_exp, modify_expr (field, value)); } } new_call = compound_expr (setup_exp, new_call); /* Call the class constructor. */ if (e->member) result = d_build_call_expr (e->member, new_call, e->arguments); else result = new_call; if (e->argprefix) result = compound_expr (build_expr (e->argprefix), result); } else if (tb->ty == TY::Tpointer && tb->nextOf ()->toBasetype ()->ty == TY::Tstruct) { /* Allocating memory for a new struct. */ Type *htype = e->newtype->toBasetype (); gcc_assert (!e->onstack); TypeStruct *stype = htype->isTypeStruct (); StructDeclaration *sd = stype->sym; tree new_call; /* Cannot new an opaque struct. */ if (sd->size (e->loc) == 0) { this->result_ = d_convert (build_ctype (e->type), integer_zero_node); return; } /* Generate: _d_newitemT() */ libcall_fn libcall = htype->isZeroInit () ? LIBCALL_NEWITEMT : LIBCALL_NEWITEMIT; tree arg = build_typeinfo (e, e->newtype); new_call = build_libcall (libcall, tb, 1, arg); if (e->member || !e->arguments) { /* Set the context pointer for nested structs. */ if (sd->isNested ()) { tree value = build_vthis (sd); tree field = get_symbol_decl (sd->vthis); tree type = build_ctype (stype); new_call = d_save_expr (new_call); field = component_ref (indirect_ref (type, new_call), field); new_call = compound_expr (modify_expr (field, value), new_call); } /* Call the struct constructor. */ if (e->member) result = d_build_call_expr (e->member, new_call, e->arguments); else result = new_call; } else { /* If we have a user supplied initializer, then set-up with a struct literal. */ if (e->arguments != NULL && sd->fields.length != 0) { StructLiteralExp *se = StructLiteralExp::create (e->loc, sd, e->arguments, htype); new_call = d_save_expr (new_call); se->type = sd->type; se->sym = new_call; result = compound_expr (build_expr (se), new_call); } else result = new_call; } if (e->argprefix) result = compound_expr (build_expr (e->argprefix), result); } else if (tb->ty == TY::Tarray) { /* Allocating memory for a new D array. */ tb = e->newtype->toBasetype (); TypeDArray *tarray = tb->isTypeDArray (); gcc_assert (e->arguments && e->arguments->length >= 1); if (e->arguments->length == 1) { /* Single dimension array allocations. */ Expression *arg = (*e->arguments)[0]; if (tarray->next->size () == 0) { /* Array element size is unknown. */ this->result_ = d_array_value (build_ctype (e->type), size_int (0), null_pointer_node); return; } libcall_fn libcall = tarray->next->isZeroInit () ? LIBCALL_NEWARRAYT : LIBCALL_NEWARRAYIT; result = build_libcall (libcall, tb, 2, build_typeinfo (e, e->type), build_expr (arg)); } else { /* Multidimensional array allocations. */ tree tarray = make_array_type (Type::tsize_t, e->arguments->length); tree var = build_local_temp (tarray); vec *elms = NULL; /* Get the base element type for the array, generating the initializer for the dims parameter along the way. */ Type *telem = e->newtype->toBasetype (); for (size_t i = 0; i < e->arguments->length; i++) { Expression *arg = (*e->arguments)[i]; CONSTRUCTOR_APPEND_ELT (elms, size_int (i), build_expr (arg)); gcc_assert (telem->ty == TY::Tarray); telem = telem->toBasetype ()->nextOf (); gcc_assert (telem); } /* Initialize the temporary. */ tree init = modify_expr (var, build_constructor (tarray, elms)); var = compound_expr (init, var); /* Generate: _d_newarraymTX(ti, dims) or: _d_newarraymiTX(ti, dims) */ libcall_fn libcall = telem->isZeroInit () ? LIBCALL_NEWARRAYMTX : LIBCALL_NEWARRAYMITX; tree tinfo = build_typeinfo (e, e->type); tree dims = d_array_value (build_ctype (Type::tsize_t->arrayOf ()), size_int (e->arguments->length), build_address (var)); result = build_libcall (libcall, tb, 2, tinfo, dims); } if (e->argprefix) result = compound_expr (build_expr (e->argprefix), result); } else if (tb->ty == TY::Tpointer) { /* Allocating memory for a new pointer. */ TypePointer *tpointer = tb->isTypePointer (); if (tpointer->next->size () == 0) { /* Pointer element size is unknown. */ this->result_ = d_convert (build_ctype (e->type), integer_zero_node); return; } libcall_fn libcall = tpointer->next->isZeroInit (e->loc) ? LIBCALL_NEWITEMT : LIBCALL_NEWITEMIT; tree arg = build_typeinfo (e, e->newtype); result = build_libcall (libcall, tb, 1, arg); if (e->arguments && e->arguments->length == 1) { result = d_save_expr (result); tree init = modify_expr (build_deref (result), build_expr ((*e->arguments)[0])); result = compound_expr (init, result); } if (e->argprefix) result = compound_expr (build_expr (e->argprefix), result); } else gcc_unreachable (); this->result_ = convert_expr (result, tb, e->type); } /* Build an integer literal. */ void visit (IntegerExp *e) final override { tree ctype = build_ctype (e->type->toBasetype ()); this->result_ = build_integer_cst (e->value, ctype); } /* Build a floating-point literal. */ void visit (RealExp *e) final override { this->result_ = build_float_cst (e->value, e->type->toBasetype ()); } /* Build a complex literal. */ void visit (ComplexExp *e) final override { Type *tnext; switch (e->type->toBasetype ()->ty) { case TY::Tcomplex32: tnext = (TypeBasic *) Type::tfloat32; break; case TY::Tcomplex64: tnext = (TypeBasic *) Type::tfloat64; break; case TY::Tcomplex80: tnext = (TypeBasic *) Type::tfloat80; break; default: gcc_unreachable (); } this->result_ = build_complex (build_ctype (e->type), build_float_cst (creall (e->value), tnext), build_float_cst (cimagl (e->value), tnext)); } /* Build a string literal, all strings are null terminated except for static arrays. */ void visit (StringExp *e) final override { Type *tb = e->type->toBasetype (); tree type = build_ctype (e->type); if (tb->ty == TY::Tsarray) { /* Turn the string into a constructor for the static array. */ vec *elms = NULL; vec_safe_reserve (elms, e->len); tree etype = TREE_TYPE (type); for (size_t i = 0; i < e->len; i++) { tree value = build_integer_cst (e->getCodeUnit (i), etype); CONSTRUCTOR_APPEND_ELT (elms, size_int (i), value); } tree ctor = build_constructor (type, elms); TREE_CONSTANT (ctor) = 1; this->result_ = ctor; } else { /* Copy the string contents to a null terminated string. */ dinteger_t length = (e->len * e->sz); char *string = XALLOCAVEC (char, length + 1); if (length > 0) memcpy (string, e->string, length); string[length] = '\0'; /* String value and type includes the null terminator. */ tree value = build_string (length, string); TREE_TYPE (value) = make_array_type (tb->nextOf (), length + 1); value = build_address (value); if (tb->ty == TY::Tarray) value = d_array_value (type, size_int (e->len), value); TREE_CONSTANT (value) = 1; this->result_ = d_convert (type, value); } } /* Build a tuple literal. Just an argument list that may have side effects that need evaluation. */ void visit (TupleExp *e) final override { tree result = NULL_TREE; if (e->e0) result = build_expr (e->e0, this->constp_, true); for (size_t i = 0; i < e->exps->length; ++i) { Expression *exp = (*e->exps)[i]; result = compound_expr (result, build_expr (exp, this->constp_, true)); } if (result == NULL_TREE) result = void_node; this->result_ = result; } /* Build an array literal. The common type of the all elements is taken to be the type of the array element, and all elements are implicitly converted to that type. */ void visit (ArrayLiteralExp *e) final override { Type *tb = e->type->toBasetype (); /* Implicitly convert void[n] to ubyte[n]. */ if (tb->ty == TY::Tsarray && tb->nextOf ()->toBasetype ()->ty == TY::Tvoid) tb = Type::tuns8->sarrayOf (tb->isTypeSArray ()->dim->toUInteger ()); gcc_assert (tb->ty == TY::Tarray || tb->ty == TY::Tsarray || tb->ty == TY::Tpointer); /* Handle empty array literals. */ if (e->elements->length == 0) { if (tb->ty == TY::Tarray) this->result_ = d_array_value (build_ctype (e->type), size_int (0), null_pointer_node); else this->result_ = build_constructor (make_array_type (tb->nextOf (), 0), NULL); return; } /* Build an expression that assigns the expressions in ELEMENTS to a constructor. */ vec *elms = NULL; vec_safe_reserve (elms, e->elements->length); bool constant_p = true; tree saved_elems = NULL_TREE; Type *etype = tb->nextOf (); tree satype = make_array_type (etype, e->elements->length); for (size_t i = 0; i < e->elements->length; i++) { Expression *expr = e->getElement (i); tree value = build_expr (expr, this->constp_, true); /* Only append nonzero values, the backend will zero out the rest of the constructor as we don't set CONSTRUCTOR_NO_CLEARING. */ if (!initializer_zerop (value)) { if (!TREE_CONSTANT (value)) constant_p = false; /* Split construction of values out of the constructor if there may be side effects. */ tree init = stabilize_expr (&value); if (init != NULL_TREE) saved_elems = compound_expr (saved_elems, init); CONSTRUCTOR_APPEND_ELT (elms, size_int (i), convert_expr (value, expr->type, etype)); } } /* Now return the constructor as the correct type. For static arrays there is nothing else to do. For dynamic arrays, return a two field struct. For pointers, return the address. */ tree ctor = build_constructor (satype, elms); tree type = build_ctype (e->type); /* Nothing else to do for static arrays. */ if (tb->ty == TY::Tsarray || this->constp_) { /* Can't take the address of the constructor, so create an anonymous static symbol, and then refer to it. */ if (tb->ty != TY::Tsarray) { tree decl = build_artificial_decl (TREE_TYPE (ctor), ctor, "A"); ctor = build_address (decl); if (tb->ty == TY::Tarray) ctor = d_array_value (type, size_int (e->elements->length), ctor); d_pushdecl (decl); rest_of_decl_compilation (decl, 1, 0); } /* If the array literal is readonly or static. */ if (constant_p) TREE_CONSTANT (ctor) = 1; if (constant_p && initializer_constant_valid_p (ctor, TREE_TYPE (ctor))) TREE_STATIC (ctor) = 1; /* Use memset to fill any alignment holes in the array. */ if (!this->constp_ && !this->literalp_) { TypeStruct *ts = etype->baseElemOf ()->isTypeStruct (); if (ts != NULL && (!identity_compare_p (ts->sym) || ts->sym->isUnionDeclaration ())) { tree var = build_local_temp (TREE_TYPE (ctor)); tree init = build_memset_call (var); /* Evaluate memset() first, then any saved elements. */ saved_elems = compound_expr (init, saved_elems); ctor = compound_expr (modify_expr (var, ctor), var); } } this->result_ = compound_expr (saved_elems, d_convert (type, ctor)); } else { /* Allocate space on the memory managed heap. */ tree mem = build_libcall (LIBCALL_ARRAYLITERALTX, etype->pointerTo (), 2, build_typeinfo (e, etype->arrayOf ()), size_int (e->elements->length)); mem = d_save_expr (mem); /* Now copy the constructor into memory. */ tree size = size_mult_expr (size_int (e->elements->length), size_int (tb->nextOf ()->size ())); tree result = build_memcpy_call (mem, build_address (ctor), size); /* Return the array pointed to by MEM. */ result = compound_expr (result, mem); if (tb->ty == TY::Tarray) result = d_array_value (type, size_int (e->elements->length), result); this->result_ = compound_expr (saved_elems, result); } } /* Build an associative array literal. The common type of the all keys is taken to be the key type, and common type of all values the value type. All keys and values are then implicitly converted as needed. */ void visit (AssocArrayLiteralExp *e) final override { /* Want the mutable type for typeinfo reference. */ Type *tb = e->type->toBasetype ()->mutableOf (); /* Handle empty assoc array literals. */ TypeAArray *ta = tb->isTypeAArray (); if (e->keys->length == 0) { this->result_ = build_constructor (build_ctype (ta), NULL); return; } /* Build an expression that assigns all expressions in KEYS to a constructor. */ tree akeys = build_array_from_exprs (ta->index->sarrayOf (e->keys->length), e->keys, this->constp_); tree init = stabilize_expr (&akeys); /* Do the same with all expressions in VALUES. */ tree avals = build_array_from_exprs (ta->next->sarrayOf (e->values->length), e->values, this->constp_); init = compound_expr (init, stabilize_expr (&avals)); /* Generate: _d_assocarrayliteralTX (ti, keys, vals); */ tree keys = d_array_value (build_ctype (ta->index->arrayOf ()), size_int (e->keys->length), build_address (akeys)); tree vals = d_array_value (build_ctype (ta->next->arrayOf ()), size_int (e->values->length), build_address (avals)); tree mem = build_libcall (LIBCALL_ASSOCARRAYLITERALTX, Type::tvoidptr, 3, build_typeinfo (e, ta), keys, vals); /* Return an associative array pointed to by MEM. */ tree aatype = build_ctype (ta); vec *ce = NULL; CONSTRUCTOR_APPEND_ELT (ce, TYPE_FIELDS (aatype), mem); tree result = build_nop (build_ctype (e->type), build_constructor (aatype, ce)); this->result_ = compound_expr (init, result); } /* Build a struct literal. */ void visit (StructLiteralExp *e) final override { /* Handle empty struct literals. */ if (e->elements == NULL || e->sd->fields.length == 0) { this->result_ = build_constructor (build_ctype (e->type), NULL); return; } /* Building sinit trees are delayed until after frontend semantic processing has complete. Build the static initializer now. */ if (e->useStaticInit && !this->constp_) { tree init = aggregate_initializer_decl (e->sd); /* If initializing a symbol, don't forget to set it. */ if (e->sym != NULL) { tree var = build_deref (e->sym); init = compound_expr (modify_expr (var, init), var); } this->result_ = init; return; } /* Build a constructor that assigns the expressions in ELEMENTS at each field index that has been filled in. */ vec *ve = NULL; tree saved_elems = NULL_TREE; /* CTFE may fill the hidden pointer by NullExp. */ gcc_assert (e->elements->length <= e->sd->fields.length); Type *tb = e->type->toBasetype (); gcc_assert (tb->ty == TY::Tstruct); for (size_t i = 0; i < e->elements->length; i++) { Expression *exp = (*e->elements)[i]; if (!exp) continue; VarDeclaration *field = e->sd->fields[i]; Type *type = exp->type->toBasetype (); Type *ftype = field->type->toBasetype (); tree value = NULL_TREE; if (ftype->ty == TY::Tsarray && !same_type_p (type, ftype)) { /* Initialize a static array with a single element. */ tree elem = build_expr (exp, this->constp_, true); saved_elems = compound_expr (saved_elems, stabilize_expr (&elem)); elem = d_save_expr (elem); if (initializer_zerop (elem)) value = build_constructor (build_ctype (ftype), NULL); else value = build_array_from_val (ftype, elem); } else { value = convert_expr (build_expr (exp, this->constp_, true), exp->type, field->type); } /* Split construction of values out of the constructor. */ saved_elems = compound_expr (saved_elems, stabilize_expr (&value)); CONSTRUCTOR_APPEND_ELT (ve, get_symbol_decl (field), value); } /* Maybe setup hidden pointer to outer scope context. */ if (e->sd->isNested () && e->elements->length != e->sd->fields.length && this->constp_ == false) { tree field = get_symbol_decl (e->sd->vthis); tree value = build_vthis (e->sd); CONSTRUCTOR_APPEND_ELT (ve, field, value); gcc_assert (e->useStaticInit == false); } /* Build a constructor in the correct shape of the aggregate type. */ tree ctor = build_struct_literal (build_ctype (e->type), ve); /* Nothing more to do for constant literals. */ if (this->constp_) { /* If the struct literal is a valid for static data. */ if (TREE_CONSTANT (ctor) && initializer_constant_valid_p (ctor, TREE_TYPE (ctor))) TREE_STATIC (ctor) = 1; this->result_ = compound_expr (saved_elems, ctor); return; } /* Construct the struct literal for run-time. */ if (e->sym != NULL) { /* Store the result in a symbol to initialize the literal. */ tree var = build_deref (e->sym); ctor = compound_expr (modify_expr (var, ctor), var); } else if (!this->literalp_) { /* Use memset to fill any alignment holes in the object. */ if (!identity_compare_p (e->sd) || e->sd->isUnionDeclaration ()) { tree var = build_local_temp (TREE_TYPE (ctor)); tree init = build_memset_call (var); /* Evaluate memset() first, then any saved element constructors. */ saved_elems = compound_expr (init, saved_elems); ctor = compound_expr (modify_expr (var, ctor), var); } } this->result_ = compound_expr (saved_elems, ctor); } /* Build a null literal. */ void visit (NullExp *e) final override { this->result_ = build_typeof_null_value (e->type); } /* Build a vector literal. */ void visit (VectorExp *e) final override { /* First handle array literal expressions. */ if (e->e1->op == EXP::arrayLiteral) { ArrayLiteralExp *ale = e->e1->isArrayLiteralExp (); vec *elms = NULL; bool constant_p = true; tree type = build_ctype (e->type); vec_safe_reserve (elms, ale->elements->length); for (size_t i = 0; i < ale->elements->length; i++) { Expression *expr = ale->getElement (i); tree value = d_convert (TREE_TYPE (type), build_expr (expr, this->constp_, true)); if (!CONSTANT_CLASS_P (value)) constant_p = false; CONSTRUCTOR_APPEND_ELT (elms, size_int (i), value); } /* Build a VECTOR_CST from a constant vector constructor. */ if (constant_p) this->result_ = build_vector_from_ctor (type, elms); else this->result_ = build_constructor (type, elms); } else if (e->e1->type->toBasetype ()->ty == TY::Tsarray) { /* Build a vector representation from a static array. */ this->result_ = convert_expr (build_expr (e->e1, this->constp_), e->e1->type, e->type); } else { /* Build constructor from single value. */ tree type = build_ctype (e->type); tree value = d_convert (TREE_TYPE (type), build_expr (e->e1, this->constp_, true)); this->result_ = build_vector_from_val (type, value); } } /* Build a static array representation of a vector expression. */ void visit (VectorArrayExp *e) final override { this->result_ = convert_expr (build_expr (e->e1, this->constp_, true), e->e1->type, e->type); } /* Build a static class literal, return its reference. */ void visit (ClassReferenceExp *e) final override { /* The result of build_new_class_expr is a RECORD_TYPE, we want the reference. */ tree var = build_address (build_new_class_expr (e)); /* If the type of this literal is an interface, the we must add the interface offset to symbol. */ if (this->constp_) { TypeClass *tc = e->type->toBasetype ()->isTypeClass (); InterfaceDeclaration *to = tc->sym->isInterfaceDeclaration (); if (to != NULL) { ClassDeclaration *from = e->originalClass (); int offset = 0; gcc_assert (to->isBaseOf (from, &offset) != 0); if (offset != 0) var = build_offset (var, size_int (offset)); } } this->result_ = var; } /* Build an uninitialized value, generated from void initializers. */ void visit (VoidInitExp *e) final override { /* The front-end only generates these for the initializer of globals. Represent `void' as zeroes, regardless of the type's default value. */ gcc_assert (this->constp_); this->result_ = build_zero_cst (build_ctype (e->type)); } /* These expressions are mainly just a placeholders in the frontend. We shouldn't see them here. */ void visit (ScopeExp *e) final override { error_at (make_location_t (e->loc), "%qs is not an expression", e->toChars ()); this->result_ = error_mark_node; } void visit (TypeExp *e) final override { error_at (make_location_t (e->loc), "type %qs is not an expression", e->toChars ()); this->result_ = error_mark_node; } }; /* Main entry point for ExprVisitor interface to generate code for the Expression AST class E. If CONST_P is true, then E is a constant expression. If LITERAL_P is true, then E is a value used in the initialization of another literal. */ tree build_expr (Expression *e, bool const_p, bool literal_p) { ExprVisitor v = ExprVisitor (const_p, literal_p); location_t saved_location = input_location; input_location = make_location_t (e->loc); e->accept (&v); tree expr = v.result (); input_location = saved_location; /* Check if initializer expression is valid constant. */ if (const_p && !initializer_constant_valid_p (expr, TREE_TYPE (expr))) { error_at (make_location_t (e->loc), "non-constant expression %qs", e->toChars ()); return error_mark_node; } return expr; } /* 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 saved_vars = vec_safe_length (d_function_chain->vars_in_scope); tree result = build_expr (e); 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; } /* Same as build_expr_dtor, but handles the result of E as a return value. */ tree build_return_dtor (Expression *e, Type *type, TypeFunction *tf) { size_t saved_vars = vec_safe_length (d_function_chain->vars_in_scope); tree result = build_expr (e); /* Convert for initializing the DECL_RESULT. */ if (tf->isref ()) { /* If we are returning a reference, take the address. */ result = convert_expr (result, e->type, type); result = build_address (result); } else result = convert_for_rvalue (result, e->type, type); /* The decl to store the return expression. */ tree decl = DECL_RESULT (cfun->decl); /* Split comma expressions, so that the result is returned directly. */ tree expr = stabilize_expr (&result); result = build_assign (INIT_EXPR, decl, result); result = compound_expr (expr, return_expr (result)); /* 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; }