diff options
Diffstat (limited to 'gcc/d/expr.cc')
-rw-r--r-- | gcc/d/expr.cc | 3138 |
1 files changed, 3138 insertions, 0 deletions
diff --git a/gcc/d/expr.cc b/gcc/d/expr.cc new file mode 100644 index 0000000..9a1aad4 --- /dev/null +++ b/gcc/d/expr.cc @@ -0,0 +1,3138 @@ +/* expr.cc -- Lower D frontend expressions to GCC trees. + Copyright (C) 2015-2018 Free Software Foundation, Inc. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" + +#include "dmd/aggregate.h" +#include "dmd/ctfe.h" +#include "dmd/declaration.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" + + +/* 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_; + + /* Determine if type is a struct that has a postblit. */ + + bool needs_postblit (Type *t) + { + t = t->baseElemOf (); + + if (t->ty == Tstruct) + { + StructDeclaration *sd = ((TypeStruct *) t)->sym; + if (sd->postblit) + return true; + } + + return false; + } + + /* Determine if type is a struct that has a destructor. */ + + bool needs_dtor (Type *t) + { + t = t->baseElemOf (); + + if (t->ty == Tstruct) + { + StructDeclaration *sd = ((TypeStruct *) t)->sym; + if (sd->dtor) + return true; + } + + return false; + } + + /* Determine if expression is suitable lvalue. */ + + bool lvalue_p (Expression *e) + { + return ((e->op != TOKslice && e->isLvalue ()) + || (e->op == TOKslice && ((UnaExp *) e)->e1->isLvalue ()) + || (e->op == TOKcast && ((UnaExp *) e)->e1->isLvalue ())); + } + + /* Build an expression of code CODE, data type TYPE, and operands ARG0 and + ARG1. Perform relevant conversions needed for correct code operations. */ + + 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; + + bool unsignedp = TYPE_UNSIGNED (t0) || TYPE_UNSIGNED (t1); + + /* 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 (INTEGRAL_TYPE_P (type) && (TYPE_UNSIGNED (type) != unsignedp)) + { + tree inttype = (unsignedp) + ? d_unsigned_type (type) : d_signed_type (type); + ret = fold_build2 (code, inttype, 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 = fold_build2 (code, eptype, arg0, arg1); + } + + return d_convert (type, ret); + } + + /* Build a binary expression of code CODE, assigning the result into E1. */ + + tree binop_assignment (tree_code code, Expression *e1, Expression *e2) + { + /* Skip casts for lhs assignment. */ + Expression *e1b = e1; + while (e1b->op == TOKcast) + { + CastExp *ce = (CastExp *) e1b; + 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 = this->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); + } + +public: + ExprVisitor (bool constp) + { + this->result_ = NULL_TREE; + this->constp_ = constp; + } + + tree result (void) + { + return this->result_; + } + + /* Visitor interfaces, each Expression class should have + overridden the default. */ + + void visit (Expression *) + { + 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) + { + 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 != 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) + { + tree_code code = (e->op == TOKidentity) ? EQ_EXPR : NE_EXPR; + Type *tb1 = e->e1->type->toBasetype (); + Type *tb2 = e->e2->type->toBasetype (); + + if ((tb1->ty == Tsarray || tb1->ty == Tarray) + && (tb2->ty == Tsarray || tb2->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 != 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)); + + tree tmemcmp = builtin_decl_explicit (BUILT_IN_MEMCMP); + tree size = size_int (TYPE_PRECISION (TREE_TYPE (t1)) / BITS_PER_UNIT); + + tree result = build_call_expr (tmemcmp, 3, build_address (t1), + build_address (t2), size); + this->result_ = build_boolop (code, result, integer_zero_node); + } + else if (tb1->ty == Tstruct) + { + /* For struct objects, identity is defined as bits in operands being + identical also. Alignment holes in structs are ignored. */ + StructDeclaration *sd = ((TypeStruct *) tb1)->sym; + 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, sd, 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) + { + Type *tb1 = e->e1->type->toBasetype (); + Type *tb2 = e->e2->type->toBasetype (); + tree_code code = (e->op == TOKequal) ? EQ_EXPR : NE_EXPR; + + if ((tb1->ty == Tsarray || tb1->ty == Tarray) + && (tb2->ty == Tsarray || tb2->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 == Tvoid + || (t1elem->ty == Tstruct && !((TypeStruct *)t1elem)->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 != Tstruct + || identity_compare_p (((TypeStruct *) t1elem)->sym)) + { + tree size = size_mult_expr (t1len, size_int (t1elem->size ())); + tree tmemcmp = builtin_decl_explicit (BUILT_IN_MEMCMP); + + result = build_call_expr (tmemcmp, 3, t1ptr, t2ptr, size); + result = build_boolop (code, result, integer_zero_node); + } + else + { + StructDeclaration *sd = ((TypeStruct *) t1elem)->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 == TOKequal) + 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 == Tsarray && tb2->ty == Tsarray) + gcc_assert (tb1->size () == tb2->size ()); + else + { + tree tlencmp = build_boolop (code, t1len, t2len); + if (e->op == TOKequal) + 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 (t1array)); + + if (e->op == TOKnotequal) + result = build1 (TRUTH_NOT_EXPR, build_ctype (e->type), result); + + this->result_ = result; + } + } + else if (tb1->ty == Tstruct) + { + /* Equality for struct objects means the logical product of all + equality results of the corresponding object fields. */ + StructDeclaration *sd = ((TypeStruct *) tb1)->sym; + 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, sd, t1, t2); + } + else if (tb1->ty == Taarray && tb2->ty == Taarray) + { + /* Use _aaEqual() for associative arrays. */ + TypeAArray *taa1 = (TypeAArray *) tb1; + tree result = build_libcall (LIBCALL_AAEQUAL, e->type, 3, + build_typeinfo (taa1), + build_expr (e->e1), + build_expr (e->e2)); + + if (e->op == TOKnotequal) + 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) + { + Type *tb2 = e->e2->type->toBasetype (); + gcc_assert (tb2->ty == Taarray); + + Type *tkey = ((TypeAArray *) tb2)->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 (tkey), + build_address (key)); + } + + /* Build a relational expression. The result type is bool. */ + + void visit (CmpExp *e) + { + Type *tb1 = e->e1->type->toBasetype (); + Type *tb2 = e->e2->type->toBasetype (); + + tree result; + tree_code code; + + switch (e->op) + { + case TOKle: + code = LE_EXPR; + break; + + case TOKlt: + code = LT_EXPR; + break; + + case TOKge: + code = GE_EXPR; + break; + + case TOKgt: + code = GT_EXPR; + break; + + default: + gcc_unreachable (); + } + + if ((tb1->ty == Tsarray || tb1->ty == Tarray) + && (tb2->ty == Tsarray || tb2->ty == Tarray)) + { + /* For static and dynamic arrays, the result of the relational op is + the result of the operator applied to the first non-equal element + of the array. If two arrays compare equal, but are of different + lengths, the shorter array compares as less than the longer. */ + Type *telem = tb1->nextOf ()->toBasetype (); + + tree call = build_libcall (LIBCALL_ADCMP2, Type::tint32, 3, + d_array_convert (e->e1), + d_array_convert (e->e2), + build_typeinfo (telem->arrayOf ())); + result = build_boolop (code, call, integer_zero_node); + + this->result_ = d_convert (build_ctype (e->type), result); + return; + } + + /* Simple comparison. */ + result = build_boolop (code, build_expr (e->e1), build_expr (e->e2)); + this->result_ = d_convert (build_ctype (e->type), result); + } + + /* Build an `and if' expression. If the right operand expression is void, + then the resulting type is void. Otherwise the result is bool. */ + + void visit (AndAndExp *e) + { + if (e->e2->type->toBasetype ()->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 (TRUTH_ANDIF_EXPR, t1, t2)); + } + else + { + tree t1 = convert_for_condition (build_expr (e->e1), e->e1->type); + tree t2 = build_expr_dtor (e->e2); + + this->result_ = build_condition (build_ctype (e->type), + t1, t2, void_node); + } + } + + /* Build an `or if' expression. If the right operand expression is void, + then the resulting type is void. Otherwise the result is bool. */ + + void visit (OrOrExp *e) + { + if (e->e2->type->toBasetype ()->ty != Tvoid) + { + tree t1 = convert_for_condition (build_expr (e->e1), e->e1->type); + tree t2 = convert_for_condition (build_expr (e->e2), e->e2->type); + + this->result_ = d_convert (build_ctype (e->type), + build_boolop (TRUTH_ORIF_EXPR, t1, t2)); + } + else + { + tree t1 = convert_for_condition (build_expr (e->e1), e->e1->type); + tree t2 = build_expr_dtor (e->e2); + tree cond = build1 (TRUTH_NOT_EXPR, d_bool_type, t1); + + this->result_ = build_condition (build_ctype (e->type), + cond, 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) + { + tree_code code; + + switch (e->op) + { + case TOKadd: + case TOKmin: + 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 == TOKmin) + 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 == TOKadd) + ? PLUS_EXPR : MINUS_EXPR; + break; + + case TOKmul: + code = MULT_EXPR; + break; + + case TOKdiv: + code = e->e1->type->isintegral () + ? TRUNC_DIV_EXPR : RDIV_EXPR; + break; + + case TOKmod: + code = e->e1->type->isfloating () + ? FLOAT_MOD_EXPR : TRUNC_MOD_EXPR; + break; + + case TOKand: + code = BIT_AND_EXPR; + break; + + case TOKor: + code = BIT_IOR_EXPR; + break; + + case TOKxor: + code = BIT_XOR_EXPR; + break; + + case TOKshl: + code = LSHIFT_EXPR; + break; + + case TOKshr: + code = RSHIFT_EXPR; + break; + + case TOKushr: + code = UNSIGNED_RSHIFT_EXPR; + break; + + default: + gcc_unreachable (); + } + + this->result_ = this->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) + { + Type *tb1 = e->e1->type->toBasetype (); + Type *tb2 = e->e2->type->toBasetype (); + Type *etype; + + if (tb1->ty == Tarray || tb1->ty == Tsarray) + etype = tb1->nextOf (); + else + etype = tb2->nextOf (); + + vec<tree, va_gc> *elemvars = NULL; + tree result; + + if (e->e1->op == TOKcat) + { + /* 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 == TOKcat;) + { + if (ex->op == TOKcat) + { + ex = ((CatExp *) ex)->e1; + ndims++; + } + } + + /* Store all concatenation args to a temporary byte[][ndims] array. */ + Type *targselem = Type::tint8->arrayOf (); + tree var = create_temporary_var (make_array_type (targselem, ndims)); + tree init = build_constructor (TREE_TYPE (var), NULL); + vec_safe_push (elemvars, var); + + /* Loop through each concatenation from right to left. */ + vec<constructor_elt, va_gc> *elms = NULL; + CatExp *ce = e; + int dim = ndims - 1; + + for (Expression *oe = ce->e2; oe != NULL; + (ce->e1->op != TOKcat + ? (oe = ce->e1) + : (ce = (CatExp *)ce->e1, oe = ce->e2))) + { + tree arg = d_array_convert (etype, oe, &elemvars); + 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); + CONSTRUCTOR_ELTS (init) = elms; + DECL_INITIAL (var) = init; + + 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->type), arrs); + } + else + { + /* Handle single concatenation (a ~ b). */ + result = build_libcall (LIBCALL_ARRAYCATT, e->type, 3, + build_typeinfo (e->type), + d_array_convert (etype, e->e1, &elemvars), + d_array_convert (etype, e->e2, &elemvars)); + } + + for (size_t i = 0; i < vec_safe_length (elemvars); ++i) + result = bind_expr ((*elemvars)[i], result); + + 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) + { + tree_code code; + Expression *e1b = e->e1; + + switch (e->op) + { + case TOKaddass: + code = PLUS_EXPR; + break; + + case TOKminass: + code = MINUS_EXPR; + break; + + case TOKmulass: + code = MULT_EXPR; + break; + + case TOKdivass: + code = e->e1->type->isintegral () + ? TRUNC_DIV_EXPR : RDIV_EXPR; + break; + + case TOKmodass: + code = e->e1->type->isfloating () + ? FLOAT_MOD_EXPR : TRUNC_MOD_EXPR; + break; + + case TOKandass: + code = BIT_AND_EXPR; + break; + + case TOKorass: + code = BIT_IOR_EXPR; + break; + + case TOKxorass: + code = BIT_XOR_EXPR; + break; + + case TOKpowass: + gcc_unreachable (); + + case TOKshlass: + code = LSHIFT_EXPR; + break; + + case TOKshrass: + case TOKushrass: + /* 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 == TOKcast) + { + CastExp *ce = (CastExp *) e1b; + gcc_assert (same_type_p (ce->type, ce->to)); + e1b = ce->e1; + } + code = (e->op == TOKshrass) ? RSHIFT_EXPR : UNSIGNED_RSHIFT_EXPR; + break; + + default: + gcc_unreachable (); + } + + tree exp = this->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 the left operand. */ + + void visit (CatAssignExp *e) + { + Type *tb1 = e->e1->type->toBasetype (); + Type *tb2 = e->e2->type->toBasetype (); + Type *etype = tb1->nextOf ()->toBasetype (); + + if (tb1->ty == Tarray && tb2->ty == Tdchar + && (etype->ty == Tchar || etype->ty == Twchar)) + { + /* Append a dchar to a char[] or wchar[] */ + libcall_fn libcall = (etype->ty == Tchar) + ? LIBCALL_ARRAYAPPENDCD : LIBCALL_ARRAYAPPENDWD; + + this->result_ = build_libcall (libcall, e->type, 2, + build_address (build_expr (e->e1)), + build_expr (e->e2)); + } + else + { + gcc_assert (tb1->ty == Tarray || tb2->ty == Tsarray); + + tree tinfo = build_typeinfo (e->type); + tree ptr = build_address (build_expr (e->e1)); + + if ((tb2->ty == Tarray || tb2->ty == Tsarray) + && same_type_p (etype, tb2->nextOf ()->toBasetype ())) + { + /* Append an array. */ + this->result_ = build_libcall (LIBCALL_ARRAYAPPENDT, e->type, 3, + tinfo, ptr, d_array_convert (e->e2)); + + } + else if (same_type_p (etype, tb2)) + { + /* Append an element. */ + tree result = build_libcall (LIBCALL_ARRAYAPPENDCTX, e->type, 3, + tinfo, ptr, size_one_node); + result = d_save_expr (result); + + /* Assign e2 to last element. */ + tree offexp = d_array_length (result); + offexp = build2 (MINUS_EXPR, TREE_TYPE (offexp), + offexp, size_one_node); + offexp = d_save_expr (offexp); + + tree ptrexp = d_array_ptr (result); + ptrexp = void_okay_p (ptrexp); + ptrexp = build_array_index (ptrexp, offexp); + + /* Evaluate expression before appending. */ + tree t2 = build_expr (e->e2); + tree expr = stabilize_expr (&t2); + + t2 = d_save_expr (t2); + result = modify_expr (build_deref (ptrexp), t2); + result = compound_expr (t2, result); + + this->result_ = compound_expr (expr, result); + } + else + gcc_unreachable (); + } + } + + /* 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) + { + /* First, handle special assignment semantics. */ + + /* Look for array.length = n; */ + if (e->e1->op == TOKarraylength) + { + /* Assignment to an array's length property; resize the array. */ + ArrayLengthExp *ale = (ArrayLengthExp *) e->e1; + tree newlength = convert_expr (build_expr (e->e2), e->e2->type, + Type::tsize_t); + tree ptr = build_address (build_expr (ale->e1)); + + /* Don't want the basetype for the element type. */ + Type *etype = ale->e1->type->toBasetype ()->nextOf (); + libcall_fn libcall = etype->isZeroInit () + ? LIBCALL_ARRAYSETLENGTHT : LIBCALL_ARRAYSETLENGTHIT; + + tree result = build_libcall (libcall, ale->e1->type, 3, + build_typeinfo (ale->e1->type), + newlength, ptr); + + this->result_ = d_array_length (result); + return; + } + + /* Look for array[] = n; */ + if (e->e1->op == TOKslice) + { + SliceExp *se = (SliceExp *) e->e1; + Type *stype = se->e1->type->toBasetype (); + Type *etype = stype->nextOf ()->toBasetype (); + + /* Determine if we need to run postblit or dtor. */ + bool postblit = this->needs_postblit (etype) && this->lvalue_p (e->e2); + bool destructor = this->needs_dtor (etype); + + if (e->memset & blockAssign) + { + /* Set a range of elements to one value. */ + tree t1 = d_save_expr (build_expr (e->e1)); + tree t2 = build_expr (e->e2); + tree result; + + if ((postblit || destructor) && e->op != TOKblit) + { + libcall_fn libcall = (e->op == TOKconstruct) + ? LIBCALL_ARRAYSETCTOR : LIBCALL_ARRAYSETASSIGN; + /* So we can call postblits on const/immutable objects. */ + tree ti = build_typeinfo (etype->unSharedOf ()->mutableOf ()); + + tree result = build_libcall (libcall, Type::tvoid, 4, + d_array_ptr (t1), + build_address (t2), + d_array_length (t1), ti); + this->result_ = compound_expr (result, t1); + return; + } + + if (integer_zerop (t2)) + { + tree tmemset = builtin_decl_explicit (BUILT_IN_MEMSET); + tree size = size_mult_expr (d_array_length (t1), + size_int (etype->size ())); + + result = build_call_expr (tmemset, 3, d_array_ptr (t1), + integer_zero_node, size); + } + else + result = build_array_set (d_array_ptr (t1), + d_array_length (t1), t2); + + this->result_ = compound_expr (result, t1); + } + else + { + /* Perform a memcpy operation. */ + gcc_assert (e->e2->type->ty != Tpointer); + + if (!postblit && !destructor && !array_bounds_check ()) + { + tree t1 = d_save_expr (d_array_convert (e->e1)); + tree t2 = d_array_convert (e->e2); + tree tmemcpy = builtin_decl_explicit (BUILT_IN_MEMCPY); + tree size = size_mult_expr (d_array_length (t1), + size_int (etype->size ())); + + tree result = build_call_expr (tmemcpy, 3, d_array_ptr (t1), + d_array_ptr (t2), size); + this->result_ = compound_expr (result, t1); + } + else if ((postblit || destructor) && e->op != TOKblit) + { + /* Generate: _d_arrayassign(ti, from, to) + or: _d_arrayctor(ti, from, to) */ + libcall_fn libcall = (e->op == TOKconstruct) + ? LIBCALL_ARRAYCTOR : LIBCALL_ARRAYASSIGN; + + this->result_ = build_libcall (libcall, e->type, 3, + build_typeinfo (etype), + d_array_convert (e->e2), + d_array_convert (e->e1)); + } + 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 & referenceInit) + { + gcc_assert (e->op == TOKconstruct || e->op == TOKblit); + gcc_assert (e->e1->op == TOKvar); + + Declaration *decl = ((VarExp *) e->e1)->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 == TOKconstruct) ? INIT_EXPR : MODIFY_EXPR; + + /* Look for struct assignment. */ + if (tb1->ty == Tstruct) + { + tree t1 = build_expr (e->e1); + tree t2 = convert_for_assignment (build_expr (e->e2), + e->e2->type, e->e1->type); + + /* Look for struct = 0. */ + if (e->e2->op == TOKint64) + { + /* Use memset to fill struct. */ + gcc_assert (e->op == TOKblit); + StructDeclaration *sd = ((TypeStruct *) tb1)->sym; + + tree tmemset = builtin_decl_explicit (BUILT_IN_MEMSET); + tree result = build_call_expr (tmemset, 3, build_address (t1), + t2, size_int (sd->structsize)); + + /* 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 + this->result_ = build_assign (modifycode, t1, t2); + + return; + } + + /* Look for static array assignment. */ + if (tb1->ty == Tsarray) + { + /* Look for array = 0. */ + if (e->e2->op == TOKint64) + { + /* Use memset to fill the array. */ + gcc_assert (e->op == TOKblit); + + tree t1 = build_expr (e->e1); + tree t2 = convert_for_assignment (build_expr (e->e2), + e->e2->type, e->e1->type); + tree size = size_int (e->e1->type->size ()); + + tree tmemset = builtin_decl_explicit (BUILT_IN_MEMSET); + this->result_ = build_call_expr (tmemset, 3, build_address (t1), + t2, size); + return; + } + + Type *etype = tb1->nextOf (); + gcc_assert (e->e2->type->toBasetype ()->ty == Tsarray); + + /* Determine if we need to run postblit. */ + bool postblit = this->needs_postblit (etype); + bool destructor = this->needs_dtor (etype); + bool lvalue_p = this->lvalue_p (e->e2); + + /* 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 == TOKconstruct && !lvalue_p && postblit) + || (e->op == TOKblit || 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; + } + + Type *arrtype = (e->type->ty == Tsarray) ? etype->arrayOf () : e->type; + tree result; + + if (e->op == TOKconstruct) + { + /* Generate: _d_arrayctor(ti, from, to) */ + result = build_libcall (LIBCALL_ARRAYCTOR, arrtype, 3, + build_typeinfo (etype), + d_array_convert (e->e2), + d_array_convert (e->e1)); + } + else + { + /* Generate: _d_arrayassign_l() + or: _d_arrayassign_r() */ + libcall_fn libcall = (lvalue_p) + ? LIBCALL_ARRAYASSIGN_L : LIBCALL_ARRAYASSIGN_R; + tree elembuf = build_local_temp (build_ctype (etype)); + + result = build_libcall (libcall, arrtype, 4, + build_typeinfo (etype), + d_array_convert (e->e2), + d_array_convert (e->e1), + build_address (elembuf)); + } + + /* Cast the libcall result back to a static array. */ + if (e->type->ty == Tsarray) + result = indirect_ref (build_ctype (e->type), + d_array_ptr (result)); + + this->result_ = result; + return; + } + + /* 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 postfix expression. */ + + void visit (PostExp *e) + { + tree result; + + if (e->op == TOKplusplus) + { + result = build2 (POSTINCREMENT_EXPR, build_ctype (e->type), + build_expr (e->e1), build_expr (e->e2)); + } + else if (e->op == TOKminusminus) + { + 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) + { + Type *tb1 = e->e1->type->toBasetype (); + + if (tb1->ty == Taarray) + { + /* Get the key for the associative array. */ + Type *tkey = ((TypeAArray *) tb1)->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 (tb1->unSharedOf ()->mutableOf ()); + } + else + { + libcall = LIBCALL_AAGETRVALUEX; + ptr = build_expr (e->e1); + tinfo = build_typeinfo (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 = d_assert_call (e->loc, LIBCALL_ARRAY_BOUNDS); + 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 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; + if (tb1->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 != Tpointer && !e->indexIsInBounds) + index = build_bounds_condition (e->e2->loc, index, length, false); + + /* Index the .ptr. */ + ptr = void_okay_p (ptr); + this->result_ = indirect_ref (TREE_TYPE (TREE_TYPE (ptr)), + build_array_index (ptr, index)); + } + } + + /* Build a comma expression. The type is the type of the right operand. */ + + void visit (CommaExp *e) + { + 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) + { + if (e->e1->type->toBasetype ()->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) + { + 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) + { + tree t1 = build_expr (e->e1); + this->result_ = delegate_method (t1); + } + + /* Build a slice expression. */ + + void visit (SliceExp *e) + { + Type *tb = e->type->toBasetype (); + Type *tb1 = e->e1->type->toBasetype (); + gcc_assert (tb->ty == Tarray || tb->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 == 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 != 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_array_index (void_okay_p (ptr), lwr_tree); + ptr = build_nop (ptrtype, ptr); + } + else + lwr_tree = NULL_TREE; + + /* Nothing more to do for static arrays, their bounds checking has been + done at compile-time. */ + if (tb->ty == Tsarray) + { + this->result_ = indirect_ref (build_ctype (e->type), ptr); + return; + } + else + gcc_assert (tb->ty == Tarray); + + /* Generate bounds checking code. */ + tree newlength; + + if (!e->upperIsInBounds) + { + if (length) + { + newlength = build_bounds_condition (e->upr->loc, upr_tree, + length, true); + } + else + { + /* Still need to check bounds lwr <= upr for pointers. */ + gcc_assert (tb1->ty == Tpointer); + newlength = upr_tree; + } + } + else + newlength = upr_tree; + + if (lwr_tree) + { + /* Enforces lwr <= upr. No need to check lwr <= length as + we've already ensured that upr <= length. */ + if (!e->lowerIsLessThanUpper) + { + tree cond = build_bounds_condition (e->lwr->loc, lwr_tree, + upr_tree, true); + + /* When bounds checking is off, the index value is + returned directly. */ + if (cond != lwr_tree) + newlength = compound_expr (cond, newlength); + } + + /* Need to ensure lwr always gets evaluated first, as it may be a + function call. Generates (lwr, upr) - lwr. */ + newlength = fold_build2 (MINUS_EXPR, TREE_TYPE (newlength), + compound_expr (lwr_tree, newlength), lwr_tree); + } + + 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) + { + Type *ebtype = e->e1->type->toBasetype (); + Type *tbtype = e->to->toBasetype (); + tree result = build_expr (e->e1, this->constp_); + + /* Just evaluate e1 if it has any side effects. */ + if (tbtype->ty == Tvoid) + this->result_ = build_nop (build_ctype (tbtype), result); + else + this->result_ = convert_expr (result, ebtype, tbtype); + } + + /* Build a delete expression. */ + + void visit (DeleteExp *e) + { + tree t1 = build_expr (e->e1); + Type *tb1 = e->e1->type->toBasetype (); + + if (tb1->ty == Tclass) + { + /* For class object references, if there is a destructor for that class, + the destructor is called for the object instance. */ + libcall_fn libcall; + + if (e->e1->op == TOKvar) + { + VarDeclaration *v = ((VarExp *) e->e1)->var->isVarDeclaration (); + if (v && v->onstack) + { + libcall = tb1->isClassHandle ()->isInterfaceDeclaration () + ? LIBCALL_CALLINTERFACEFINALIZER : LIBCALL_CALLFINALIZER; + + this->result_ = build_libcall (libcall, Type::tvoid, 1, t1); + return; + } + } + + /* Otherwise, the garbage collector is called to immediately free the + memory allocated for the class instance. */ + libcall = tb1->isClassHandle ()->isInterfaceDeclaration () + ? LIBCALL_DELINTERFACE : LIBCALL_DELCLASS; + + t1 = build_address (t1); + this->result_ = build_libcall (libcall, Type::tvoid, 1, t1); + } + else if (tb1->ty == Tarray) + { + /* For dynamic arrays, the garbage collector is called to immediately + release the memory. */ + Type *telem = tb1->nextOf ()->baseElemOf (); + tree ti = null_pointer_node; + + if (telem->ty == Tstruct) + { + /* Might need to run destructor on array contents. */ + TypeStruct *ts = (TypeStruct *) telem; + if (ts->sym->dtor) + ti = build_typeinfo (tb1->nextOf ()); + } + + /* Generate: _delarray_t (&t1, ti); */ + this->result_ = build_libcall (LIBCALL_DELARRAYT, Type::tvoid, 2, + build_address (t1), ti); + } + else if (tb1->ty == Tpointer) + { + /* For pointers to a struct instance, if the struct has overloaded + operator delete, then that operator is called. */ + t1 = build_address (t1); + Type *tnext = ((TypePointer *)tb1)->next->toBasetype (); + + if (tnext->ty == Tstruct) + { + TypeStruct *ts = (TypeStruct *)tnext; + if (ts->sym->dtor) + { + this->result_ = build_libcall (LIBCALL_DELSTRUCT, Type::tvoid, + 2, t1, build_typeinfo (tnext)); + return; + } + } + + /* Otherwise, the garbage collector is called to immediately free the + memory allocated for the pointer. */ + this->result_ = build_libcall (LIBCALL_DELMEMORY, Type::tvoid, 1, t1); + } + 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) + { + /* Check that the array is actually an associative array. */ + if (e->e1->type->toBasetype ()->ty == Taarray) + { + Type *tb = e->e1->type->toBasetype (); + Type *tkey = ((TypeAArray *) tb)->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 (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) + { + 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) + { + TY ty1 = e->e1->type->toBasetype ()->ty; + gcc_assert (ty1 != Tarray && ty1 != 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) + { + TY ty1 = e->e1->type->toBasetype ()->ty; + gcc_assert (ty1 != Tarray && ty1 != 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) + { + Type *tnext = NULL; + size_t offset; + tree result; + + if (e->e1->op == TOKadd) + { + BinExp *be = (BinExp *) e->e1; + if (be->e1->op == TOKaddress + && be->e2->isConst () && be->e2->type->isintegral ()) + { + Expression *ae = ((AddrExp *) be->e1)->e1; + tnext = ae->type->toBasetype (); + result = build_expr (ae); + offset = be->e2->toUInteger (); + } + } + else if (e->e1->op == TOKsymoff) + { + SymOffExp *se = (SymOffExp *) e->e1; + 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 == Tstruct) + { + StructDeclaration *sd = ((TypeStruct *) tnext)->sym; + + for (size_t i = 0; i < sd->fields.dim; 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) + { + 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 == TOKstructliteral) + { + StructLiteralExp *sle = ((StructLiteralExp *) e->e1)->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_); + + TREE_CONSTANT (exp) = 0; + this->result_ = d_convert (type, build_address (exp)); + } + + /* Build a function call expression. */ + + void visit (CallExp *e) + { + Type *tb = e->e1->type->toBasetype (); + Expression *e1b = e->e1; + + tree callee = NULL_TREE; + tree object = NULL_TREE; + tree cleanup = NULL_TREE; + TypeFunction *tf = NULL; + + /* Calls to delegates can sometimes look like this. */ + if (e1b->op == TOKcomma) + { + e1b = ((CommaExp *) e1b)->e2; + gcc_assert (e1b->op == TOKvar); + + Declaration *var = ((VarExp *) e1b)->var; + gcc_assert (var->isFuncDeclaration () && !var->needThis ()); + } + + if (e1b->op == TOKdotvar && tb->ty != Tdelegate) + { + DotVarExp *dve = (DotVarExp *) e1b; + + /* Don't modify the static initializer for struct literals. */ + if (dve->e1->op == TOKstructliteral) + { + StructLiteralExp *sle = (StructLiteralExp *) dve->e1; + 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); + } + + /* 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); + + 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 == TOKdotvar) + { + /* 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 (((DotVarExp *) e1b)->var->type); + } + else + tf = get_function_type (tb); + + extract_from_method_call (callee, callee, object); + } + else if (tb->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 == TOKvar) + { + FuncDeclaration *fd = ((VarExp *) e1b)->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 %<this%> 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 (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. 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; + } + + /* Build a delegate expression. */ + + void visit (DelegateExp *e) + { + if (e->func->semanticRun == PASSsemantic3done) + { + /* 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 ()) + { + if (e->e1->op == TOKnull) + 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 != Tclass && e->e1->type->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 == 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 != TOKsuper && e->e1->op != TOKdottype) + { + 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) + { + /* Just a pass through to underlying expression. */ + this->result_ = build_expr (e->e1); + } + + /* Build a component reference expression. */ + + void visit (DotVarExp *e) + { + 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); + + if (e->e1->type->toBasetype ()->ty != Tstruct) + object = build_deref (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) + { + 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) + { + /* 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_UNITTEST : LIBCALL_ASSERT; + + /* Build a call to _d_assert(). */ + assert_fail = d_assert_call (e->loc, libcall, tmsg); + + if (global.params.useInvariants) + { + /* If the condition is a D class or struct object with an invariant, + call it if the condition result is true. */ + if (tb1->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 == Tpointer && tb1->nextOf ()->ty == Tstruct) + { + StructDeclaration *sd = ((TypeStruct *) tb1->nextOf ())->sym; + if (sd->inv != NULL) + { + Expressions args; + arg = d_save_expr (arg); + assert_pass = d_build_call_expr (sd->inv, arg, &args); + } + } + } + } + else + { + /* Assert contracts are turned off, if the contract condition has no + side effects can still use it as a predicate for the optimizer. */ + if (TREE_SIDE_EFFECTS (arg)) + { + this->result_ = void_node; + return; + } + + assert_fail = build_predict_expr (PRED_NORETURN, NOT_TAKEN); + } + + /* 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) + { + /* 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) + { + if (Type *tid = isType (e->obj)) + { + tree ti = build_typeinfo (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 == 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 (((TypeClass *) type)->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) + { + Type *ftype = e->type->toBasetype (); + + /* This check is for lambda's, remove 'vthis' as function isn't nested. */ + if (e->fd->tok == TOKreserved && ftype->ty == Tpointer) + { + e->fd->tok = TOKfunction; + 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 *) + { + /* 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) + { + /* Build the address and offset of the symbol. */ + size_t soffset = ((SymOffExp *) e)->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) + { + if (e->var->needThis ()) + { + error ("need %<this%> 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 == TOKreserved) + { + fld->tok = TOKfunction; + 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 *sd = e->var->isSymbolDeclaration (); + tree init = NULL_TREE; + + if (var && (var->isConst () || var->isImmutable ()) + && e->type->toBasetype ()->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 (sd && sd->dsym) + init = layout_struct_initializer (sd->dsym); + 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; + + /* 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) + { + 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 == 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) + { + Type *tb = e->type->toBasetype (); + tree result; + + if (e->allocator) + gcc_assert (e->newargs); + + if (tb->ty == Tclass) + { + /* Allocating a new class. */ + tb = e->newtype->toBasetype (); + gcc_assert (tb->ty == Tclass); + + ClassDeclaration *cd = ((TypeClass *) tb)->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 (e->allocator) + { + /* Call class allocator, and copy the initializer into memory. */ + new_call = d_build_call_expr (e->allocator, NULL_TREE, e->newargs); + new_call = d_save_expr (new_call); + new_call = build_nop (type, new_call); + setup_exp = modify_expr (build_deref (new_call), + aggregate_initializer_decl (cd)); + } + 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 (); + Dsymbol *outer = cd->toParent2 (); + int offset = 0; + + value = build_expr (e->thisexp); + if (outer != tcd) + { + ClassDeclaration *ocd = outer->isClassDeclaration (); + 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 == Tpointer && tb->nextOf ()->toBasetype ()->ty == Tstruct) + { + /* Allocating memory for a new struct. */ + Type *htype = e->newtype->toBasetype (); + gcc_assert (htype->ty == Tstruct); + gcc_assert (!e->onstack); + + TypeStruct *stype = (TypeStruct *) htype; + 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; + } + + if (e->allocator) + { + /* Call struct allocator. */ + new_call = d_build_call_expr (e->allocator, NULL_TREE, e->newargs); + new_call = build_nop (build_ctype (tb), new_call); + } + else + { + /* Generate: _d_newitemT() */ + libcall_fn libcall = htype->isZeroInit () + ? LIBCALL_NEWITEMT : LIBCALL_NEWITEMIT; + tree arg = build_typeinfo (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.dim != 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 == Tarray) + { + /* Allocating memory for a new D array. */ + tb = e->newtype->toBasetype (); + gcc_assert (tb->ty == Tarray); + TypeDArray *tarray = (TypeDArray *) tb; + + gcc_assert (!e->allocator); + gcc_assert (e->arguments && e->arguments->dim >= 1); + + if (e->arguments->dim == 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->type), + build_expr (arg)); + } + else + { + /* Multidimensional array allocations. */ + vec<constructor_elt, va_gc> *elms = NULL; + Type *telem = e->newtype->toBasetype (); + tree tarray = make_array_type (Type::tsize_t, e->arguments->dim); + tree var = create_temporary_var (tarray); + tree init = build_constructor (TREE_TYPE (var), NULL); + + for (size_t i = 0; i < e->arguments->dim; i++) + { + Expression *arg = (*e->arguments)[i]; + CONSTRUCTOR_APPEND_ELT (elms, size_int (i), build_expr (arg)); + + gcc_assert (telem->ty == Tarray); + telem = telem->toBasetype ()->nextOf (); + gcc_assert (telem); + } + + CONSTRUCTOR_ELTS (init) = elms; + DECL_INITIAL (var) = init; + + /* Generate: _d_newarraymTX(ti, dims) + or: _d_newarraymiTX(ti, dims) */ + libcall_fn libcall = telem->isZeroInit () + ? LIBCALL_NEWARRAYMTX : LIBCALL_NEWARRAYMITX; + + tree tinfo = build_typeinfo (e->type); + tree dims = d_array_value (build_ctype (Type::tsize_t->arrayOf ()), + size_int (e->arguments->dim), + build_address (var)); + + result = build_libcall (libcall, tb, 2, tinfo, dims); + result = bind_expr (var, result); + } + + if (e->argprefix) + result = compound_expr (build_expr (e->argprefix), result); + } + else if (tb->ty == Tpointer) + { + /* Allocating memory for a new pointer. */ + TypePointer *tpointer = (TypePointer *) tb; + + 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->newtype); + result = build_libcall (libcall, tb, 1, arg); + + if (e->arguments && e->arguments->dim == 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) + { + tree ctype = build_ctype (e->type->toBasetype ()); + this->result_ = build_integer_cst (e->value, ctype); + } + + /* Build a floating-point literal. */ + + void visit (RealExp *e) + { + this->result_ = build_float_cst (e->value, e->type->toBasetype ()); + } + + /* Build a complex literal. */ + + void visit (ComplexExp *e) + { + Type *tnext; + + switch (e->type->toBasetype ()->ty) + { + case Tcomplex32: + tnext = (TypeBasic *) Type::tfloat32; + break; + + case Tcomplex64: + tnext = (TypeBasic *) Type::tfloat64; + break; + + case 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) + { + Type *tb = e->type->toBasetype (); + tree type = build_ctype (e->type); + + if (tb->ty == Tsarray) + { + /* Turn the string into a constructor for the static array. */ + vec<constructor_elt, va_gc> *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->charAt (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); + 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 == 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) + { + tree result = NULL_TREE; + + if (e->e0) + result = build_expr (e->e0); + + for (size_t i = 0; i < e->exps->dim; ++i) + { + Expression *exp = (*e->exps)[i]; + result = compound_expr (result, build_expr (exp)); + } + + 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) + { + Type *tb = e->type->toBasetype (); + + /* Implicitly convert void[n] to ubyte[n]. */ + if (tb->ty == Tsarray && tb->nextOf ()->toBasetype ()->ty == Tvoid) + tb = Type::tuns8->sarrayOf (((TypeSArray *) tb)->dim->toUInteger ()); + + gcc_assert (tb->ty == Tarray || tb->ty == Tsarray || tb->ty == Tpointer); + + /* Handle empty array literals. */ + if (e->elements->dim == 0) + { + if (tb->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<constructor_elt, va_gc> *elms = NULL; + vec_safe_reserve (elms, e->elements->dim); + bool constant_p = true; + tree saved_elems = NULL_TREE; + + Type *etype = tb->nextOf (); + tree satype = make_array_type (etype, e->elements->dim); + + for (size_t i = 0; i < e->elements->dim; i++) + { + Expression *expr = e->getElement (i); + tree value = build_expr (expr, this->constp_); + + /* 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 == 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 != Tsarray) + { + tree decl = build_artificial_decl (TREE_TYPE (ctor), ctor, "A"); + ctor = build_address (decl); + if (tb->ty == Tarray) + ctor = d_array_value (type, size_int (e->elements->dim), 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; + + 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 (etype->arrayOf ()), + size_int (e->elements->dim)); + mem = d_save_expr (mem); + + /* Now copy the constructor into memory. */ + tree tmemcpy = builtin_decl_explicit (BUILT_IN_MEMCPY); + tree size = size_mult_expr (size_int (e->elements->dim), + size_int (tb->nextOf ()->size ())); + + tree result = build_call_expr (tmemcpy, 3, mem, + build_address (ctor), size); + + /* Return the array pointed to by MEM. */ + result = compound_expr (result, mem); + + if (tb->ty == Tarray) + result = d_array_value (type, size_int (e->elements->dim), 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) + { + /* Want the mutable type for typeinfo reference. */ + Type *tb = e->type->toBasetype ()->mutableOf (); + gcc_assert (tb->ty == Taarray); + + /* Handle empty assoc array literals. */ + TypeAArray *ta = (TypeAArray *) tb; + if (e->keys->dim == 0) + { + this->result_ = build_constructor (build_ctype (ta), NULL); + return; + } + + /* Build an expression that assigns all expressions in KEYS + to a constructor. */ + vec<constructor_elt, va_gc> *kelts = NULL; + vec_safe_reserve (kelts, e->keys->dim); + for (size_t i = 0; i < e->keys->dim; i++) + { + Expression *key = (*e->keys)[i]; + tree t = build_expr (key); + CONSTRUCTOR_APPEND_ELT (kelts, size_int (i), + convert_expr (t, key->type, ta->index)); + } + tree tkeys = make_array_type (ta->index, e->keys->dim); + tree akeys = build_constructor (tkeys, kelts); + + /* Do the same with all expressions in VALUES. */ + vec<constructor_elt, va_gc> *velts = NULL; + vec_safe_reserve (velts, e->values->dim); + for (size_t i = 0; i < e->values->dim; i++) + { + Expression *value = (*e->values)[i]; + tree t = build_expr (value); + CONSTRUCTOR_APPEND_ELT (velts, size_int (i), + convert_expr (t, value->type, ta->next)); + } + tree tvals = make_array_type (ta->next, e->values->dim); + tree avals = build_constructor (tvals, velts); + + /* Generate: _d_assocarrayliteralTX (ti, keys, vals); */ + tree keys = d_array_value (build_ctype (ta->index->arrayOf ()), + size_int (e->keys->dim), build_address (akeys)); + tree vals = d_array_value (build_ctype (ta->next->arrayOf ()), + size_int (e->values->dim), + build_address (avals)); + + tree mem = build_libcall (LIBCALL_ASSOCARRAYLITERALTX, Type::tvoidptr, 3, + build_typeinfo (ta), keys, vals); + + /* Return an associative array pointed to by MEM. */ + tree aatype = build_ctype (ta); + vec<constructor_elt, va_gc> *ce = NULL; + CONSTRUCTOR_APPEND_ELT (ce, TYPE_FIELDS (aatype), mem); + + this->result_ = build_nop (build_ctype (e->type), + build_constructor (aatype, ce)); + } + + /* Build a struct literal. */ + + void visit (StructLiteralExp *e) + { + /* Handle empty struct literals. */ + if (e->elements == NULL || e->sd->fields.dim == 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_) + { + this->result_ = aggregate_initializer_decl (e->sd); + return; + } + + /* Build a constructor that assigns the expressions in ELEMENTS + at each field index that has been filled in. */ + vec<constructor_elt, va_gc> *ve = NULL; + tree saved_elems = NULL_TREE; + + /* CTFE may fill the hidden pointer by NullExp. */ + gcc_assert (e->elements->dim <= e->sd->fields.dim); + + Type *tb = e->type->toBasetype (); + gcc_assert (tb->ty == Tstruct); + + for (size_t i = 0; i < e->elements->dim; 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 == Tsarray && !same_type_p (type, ftype)) + { + /* Initialize a static array with a single element. */ + tree elem = build_expr (exp, this->constp_); + 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_), + exp->type, field->type); + } + + /* Split construction of values out of the constructor. */ + tree init = stabilize_expr (&value); + if (init != NULL_TREE) + saved_elems = compound_expr (saved_elems, init); + + CONSTRUCTOR_APPEND_ELT (ve, get_symbol_decl (field), value); + } + + /* Maybe setup hidden pointer to outer scope context. */ + if (e->sd->isNested () && e->elements->dim != e->sd->fields.dim + && 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; + } + + if (e->sym != NULL) + { + tree var = build_deref (e->sym); + ctor = compound_expr (modify_expr (var, ctor), var); + this->result_ = compound_expr (saved_elems, ctor); + } + else if (e->sd->isUnionDeclaration ()) + { + /* For unions, use memset to fill holes in the object. */ + tree var = build_local_temp (TREE_TYPE (ctor)); + tree tmemset = builtin_decl_explicit (BUILT_IN_MEMSET); + tree init = build_call_expr (tmemset, 3, build_address (var), + size_zero_node, + size_int (e->sd->structsize)); + + init = compound_expr (init, saved_elems); + init = compound_expr (init, modify_expr (var, ctor)); + this->result_ = compound_expr (init, var); + } + else + this->result_ = compound_expr (saved_elems, ctor); + } + + /* Build a null literal. */ + + void visit (NullExp *e) + { + Type *tb = e->type->toBasetype (); + tree value; + + /* Handle certain special case conversions, where the underlying type is an + aggregate with a nullable interior pointer. */ + if (tb->ty == Tarray) + { + /* For dynamic arrays, set length and pointer fields to zero. */ + value = d_array_value (build_ctype (e->type), size_int (0), + null_pointer_node); + } + else if (tb->ty == Taarray) + { + /* For associative arrays, set the pointer field to null. */ + value = build_constructor (build_ctype (e->type), NULL); + } + else if (tb->ty == Tdelegate) + { + /* For delegates, set the frame and function pointer to null. */ + value = build_delegate_cst (null_pointer_node, + null_pointer_node, e->type); + } + else + value = d_convert (build_ctype (e->type), integer_zero_node); + + TREE_CONSTANT (value) = 1; + this->result_ = value; + } + + /* Build a vector literal. */ + + void visit (VectorExp *e) + { + tree type = build_ctype (e->type); + tree etype = TREE_TYPE (type); + + /* First handle array literal expressions. */ + if (e->e1->op == TOKarrayliteral) + { + ArrayLiteralExp *ale = ((ArrayLiteralExp *) e->e1); + vec<constructor_elt, va_gc> *elms = NULL; + bool constant_p = true; + + vec_safe_reserve (elms, ale->elements->dim); + for (size_t i = 0; i < ale->elements->dim; i++) + { + Expression *expr = ale->getElement (i); + tree value = d_convert (etype, build_expr (expr, this->constp_)); + 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 + { + /* Build constructor from single value. */ + tree val = d_convert (etype, build_expr (e->e1, this->constp_)); + this->result_ = build_vector_from_val (type, val); + } + } + + /* Build a static class literal, return its reference. */ + + void visit (ClassReferenceExp *e) + { + /* 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 = (TypeClass *) e->type; + 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; + } + + /* These expressions are mainly just a placeholders in the frontend. + We shouldn't see them here. */ + + void visit (ScopeExp *e) + { + error_at (make_location_t (e->loc), "%qs is not an expression", + e->toChars ()); + this->result_ = error_mark_node; + } + + void visit (TypeExp *e) + { + 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. */ + +tree +build_expr (Expression *e, bool const_p) +{ + ExprVisitor v = ExprVisitor (const_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. */ + result = convert_expr (result, e->type, type); + + /* If we are returning a reference, take the address. */ + if (tf->isref) + result = build_address (result); + + /* 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; +} + |