diff options
Diffstat (limited to 'gcc/d/decl.cc')
-rw-r--r-- | gcc/d/decl.cc | 2312 |
1 files changed, 2312 insertions, 0 deletions
diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc new file mode 100644 index 0000000..6fd0808 --- /dev/null +++ b/gcc/d/decl.cc @@ -0,0 +1,2312 @@ +/* decl.cc -- Lower D frontend declarations to GCC trees. + Copyright (C) 2006-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/attrib.h" +#include "dmd/ctfe.h" +#include "dmd/declaration.h" +#include "dmd/enum.h" +#include "dmd/errors.h" +#include "dmd/globals.h" +#include "dmd/hdrgen.h" +#include "dmd/identifier.h" +#include "dmd/import.h" +#include "dmd/init.h" +#include "dmd/mangle.h" +#include "dmd/module.h" +#include "dmd/nspace.h" +#include "dmd/target.h" +#include "dmd/template.h" + +#include "tree.h" +#include "tree-iterator.h" +#include "fold-const.h" +#include "diagnostic.h" +#include "langhooks.h" +#include "target.h" +#include "common/common-target.h" +#include "cgraph.h" +#include "toplev.h" +#include "stringpool.h" +#include "varasm.h" +#include "stor-layout.h" +#include "attribs.h" +#include "function.h" +#include "debug.h" +#include "tree-pretty-print.h" + +#include "d-tree.h" + + +/* Return identifier for the external mangled name of DECL. */ + +static const char * +mangle_decl (Dsymbol *decl) +{ + if (decl->isFuncDeclaration ()) + return mangleExact ((FuncDeclaration *)decl); + else + { + OutBuffer buf; + mangleToBuffer (decl, &buf); + return buf.extractString (); + } +} + +/* Generate a mangled identifier using NAME and SUFFIX, prefixed by the + assembler name for DECL. */ + +tree +mangle_internal_decl (Dsymbol *decl, const char *name, const char *suffix) +{ + const char *prefix = mangle_decl (decl); + unsigned namelen = strlen (name); + unsigned buflen = (2 + strlen (prefix) + namelen + strlen (suffix)) * 2; + char *buf = (char *) alloca (buflen); + + snprintf (buf, buflen, "_D%s%u%s%s", prefix, namelen, name, suffix); + tree ident = get_identifier (buf); + + /* Symbol is not found in user code, but generate a readable name for it + anyway for debug and diagnostic reporting. */ + snprintf (buf, buflen, "%s.%s", decl->toPrettyChars (), name); + IDENTIFIER_PRETTY_NAME (ident) = get_identifier (buf); + + return ident; +} + +/* Returns true if DECL is from the gcc.attribute module. */ + +static bool +gcc_attribute_p (Dsymbol *decl) +{ + ModuleDeclaration *md = decl->getModule ()->md; + + if (md && md->packages && md->packages->dim == 1) + { + if (!strcmp ((*md->packages)[0]->toChars (), "gcc") + && !strcmp (md->id->toChars (), "attribute")) + return true; + } + + return false; +} + +/* Implements the visitor interface to lower all Declaration AST classes + emitted from the D Front-end to GCC trees. + All visit methods accept one parameter D, which holds the frontend AST + of the declaration to compile. These also don't return any value, instead + generated code are appened to global_declarations or added to the + current_binding_level by d_pushdecl(). */ + +class DeclVisitor : public Visitor +{ + using Visitor::visit; + +public: + DeclVisitor (void) + { + } + + /* This should be overridden by each declaration class. */ + + void visit (Dsymbol *) + { + } + + /* Compile a D module, and all members of it. */ + + void visit (Module *d) + { + if (d->semanticRun >= PASSobj) + return; + + build_module_tree (d); + d->semanticRun = PASSobj; + } + + /* Write the imported symbol to debug. */ + + void visit (Import *d) + { + /* Implements import declarations by telling the debug back-end we are + importing the NAMESPACE_DECL of the module or IMPORTED_DECL of the + declaration into the current lexical scope CONTEXT. NAME is set if + this is a renamed import. */ + if (d->isstatic) + return; + + /* Get the context of this import, this should never be null. */ + tree context = d_module_context (); + + if (d->ident == NULL) + { + /* Importing declaration list. */ + for (size_t i = 0; i < d->names.dim; i++) + { + AliasDeclaration *aliasdecl = d->aliasdecls[i]; + tree decl = build_import_decl (aliasdecl); + + /* Skip over unhandled imports. */ + if (decl == NULL_TREE) + continue; + + Identifier *alias = d->aliases[i]; + tree name = (alias != NULL) + ? get_identifier (alias->toChars ()) : NULL_TREE; + + debug_hooks->imported_module_or_decl (decl, name, context, + false, false); + } + } + else + { + /* Importing the entire module. */ + tree decl = build_import_decl (d->mod); + + tree name = (d->aliasId != NULL) + ? get_identifier (d->aliasId->toChars ()) : NULL_TREE; + + debug_hooks->imported_module_or_decl (decl, name, context, + false, false); + } + } + + /* Expand any local variables found in tuples. */ + + void visit (TupleDeclaration *d) + { + for (size_t i = 0; i < d->objects->dim; i++) + { + RootObject *o = (*d->objects)[i]; + if ((o->dyncast () == DYNCAST_EXPRESSION) + && ((Expression *) o)->op == TOKdsymbol) + { + Declaration *d = ((DsymbolExp *) o)->s->isDeclaration (); + if (d) + d->accept (this); + } + } + } + + /* Walk over all declarations in the attribute scope. */ + + void visit (AttribDeclaration *d) + { + Dsymbols *ds = d->include (NULL, NULL); + + if (!ds) + return; + + for (size_t i = 0; i < ds->dim; i++) + { + Dsymbol *s = (*ds)[i]; + s->accept (this); + } + } + + /* Pragmas are a way to pass special information to the compiler and to add + vendor specific extensions to D. We don't do anything here, yet. */ + + void visit (PragmaDeclaration *d) + { + if (!global.params.ignoreUnsupportedPragmas) + { + if (d->ident == Identifier::idPool ("lib") + || d->ident == Identifier::idPool ("startaddress")) + { + warning_at (make_location_t (d->loc), OPT_Wunknown_pragmas, + "pragma(%s) not implemented", d->ident->toChars ()); + } + } + + visit ((AttribDeclaration *) d); + } + + /* Walk over all members in the namespace scope. */ + + void visit (Nspace *d) + { + if (isError (d) || !d->members) + return; + + for (size_t i = 0; i < d->members->dim; i++) + { + Dsymbol *s = (*d->members)[i]; + s->accept (this); + } + } + + /* Walk over all members in the instantiated template. */ + + void visit (TemplateInstance *d) + { + if (isError (d)|| !d->members) + return; + + if (!d->needsCodegen ()) + return; + + for (size_t i = 0; i < d->members->dim; i++) + { + Dsymbol *s = (*d->members)[i]; + s->accept (this); + } + } + + /* Walk over all members in the mixin template scope. */ + + void visit (TemplateMixin *d) + { + if (isError (d)|| !d->members) + return; + + for (size_t i = 0; i < d->members->dim; i++) + { + Dsymbol *s = (*d->members)[i]; + s->accept (this); + } + } + + /* Write out compiler generated TypeInfo, initializer and functions for the + given struct declaration, walking over all static members. */ + + void visit (StructDeclaration *d) + { + if (d->type->ty == Terror) + { + error_at (make_location_t (d->loc), + "had semantic errors when compiling"); + return; + } + + /* Add this decl to the current binding level. */ + tree ctype = build_ctype (d->type); + if (TYPE_NAME (ctype)) + d_pushdecl (TYPE_NAME (ctype)); + + /* Anonymous structs/unions only exist as part of others, + do not output forward referenced structs. */ + if (d->isAnonymous () || !d->members) + return; + + /* Don't emit any symbols from gcc.attribute module. */ + if (gcc_attribute_p (d)) + return; + + /* Generate TypeInfo. */ + create_typeinfo (d->type, NULL); + + /* Generate static initializer. */ + d->sinit = aggregate_initializer_decl (d); + DECL_INITIAL (d->sinit) = layout_struct_initializer (d); + + if (d->isInstantiated ()) + d_linkonce_linkage (d->sinit); + + d_finish_decl (d->sinit); + + /* Put out the members. */ + for (size_t i = 0; i < d->members->dim; i++) + { + Dsymbol *member = (*d->members)[i]; + /* There might be static ctors in the members, and they cannot + be put in separate object files. */ + member->accept (this); + } + + /* Put out xopEquals, xopCmp and xopHash. */ + if (d->xeq && d->xeq != d->xerreq) + d->xeq->accept (this); + + if (d->xcmp && d->xcmp != d->xerrcmp) + d->xcmp->accept (this); + + if (d->xhash) + d->xhash->accept (this); + } + + /* Finish semantic analysis of functions in vtbl for class CD. */ + + bool finish_vtable (ClassDeclaration *d) + { + bool has_errors = false; + + /* Finish semantic analysis of functions in vtbl[]. */ + for (size_t i = d->vtblOffset (); i < d->vtbl.dim; i++) + { + FuncDeclaration *fd = d->vtbl[i]->isFuncDeclaration (); + + if (!fd || (!fd->fbody && d->isAbstract ())) + continue; + + /* Ensure function has a return value. */ + if (!fd->functionSemantic ()) + has_errors = true; + + /* No name hiding to check for. */ + if (!d->isFuncHidden (fd) || fd->isFuture ()) + continue; + + /* The function fd is hidden from the view of the class. + If it overlaps with any function in the vtbl[], then + issue an error. */ + for (size_t j = 1; j < d->vtbl.dim; j++) + { + if (j == i) + continue; + + FuncDeclaration *fd2 = d->vtbl[j]->isFuncDeclaration (); + if (!fd2->ident->equals (fd->ident)) + continue; + + /* The function is marked as @__future, a deprecation has + already been given by the frontend. */ + if (fd2->isFuture ()) + continue; + + if (fd->leastAsSpecialized (fd2) || fd2->leastAsSpecialized (fd)) + { + TypeFunction *tf = (TypeFunction *) fd->type; + if (tf->ty == Tfunction) + { + error_at (make_location_t (fd->loc), "use of %qs", + fd->toPrettyChars ()); + inform (make_location_t (fd2->loc), "is hidden by %qs", + fd2->toPrettyChars ()); + inform (make_location_t (d->loc), + "use %<alias %s = %s.%s;%> to introduce base class " + "overload set.", fd->toChars (), + fd->parent->toChars (), fd->toChars ()); + } + else + { + error_at (make_location_t (fd->loc), "use of %qs", + fd->toPrettyChars ()); + inform (make_location_t (fd2->loc), "is hidden by %qs", + fd2->toPrettyChars ()); + } + + has_errors = true; + break; + } + } + } + + return !has_errors; + } + + /* Write out compiler generated TypeInfo, initializer and vtables for the + given class declaration, walking over all static members. */ + + void visit (ClassDeclaration *d) + { + if (d->type->ty == Terror) + { + error_at (make_location_t (d->loc), + "had semantic errors when compiling"); + return; + } + + if (!d->members) + return; + + /* Put out the members. */ + for (size_t i = 0; i < d->members->dim; i++) + { + Dsymbol *member = (*d->members)[i]; + member->accept (this); + } + + /* If something goes wrong during final semantic pass, don't bother with + the rest as we may have incomplete info. */ + if (!this->finish_vtable (d)) + return; + + /* Generate C symbols. */ + d->csym = get_classinfo_decl (d); + d->vtblsym = get_vtable_decl (d); + d->sinit = aggregate_initializer_decl (d); + + /* Generate static initializer. */ + DECL_INITIAL (d->sinit) = layout_class_initializer (d); + d_linkonce_linkage (d->sinit); + d_finish_decl (d->sinit); + + /* Put out the TypeInfo. */ + create_typeinfo (d->type, NULL); + DECL_INITIAL (d->csym) = layout_classinfo (d); + d_linkonce_linkage (d->csym); + d_finish_decl (d->csym); + + /* Put out the vtbl[]. */ + vec<constructor_elt, va_gc> *elms = NULL; + + /* First entry is ClassInfo reference. */ + if (d->vtblOffset ()) + CONSTRUCTOR_APPEND_ELT (elms, size_zero_node, build_address (d->csym)); + + for (size_t i = d->vtblOffset (); i < d->vtbl.dim; i++) + { + FuncDeclaration *fd = d->vtbl[i]->isFuncDeclaration (); + + if (fd && (fd->fbody || !d->isAbstract())) + { + CONSTRUCTOR_APPEND_ELT (elms, size_int (i), + build_address (get_symbol_decl (fd))); + } + } + + DECL_INITIAL (d->vtblsym) + = build_constructor (TREE_TYPE (d->vtblsym), elms); + d_comdat_linkage (d->vtblsym); + d_finish_decl (d->vtblsym); + + /* Add this decl to the current binding level. */ + tree ctype = TREE_TYPE (build_ctype (d->type)); + if (TYPE_NAME (ctype)) + d_pushdecl (TYPE_NAME (ctype)); + } + + /* Write out compiler generated TypeInfo and vtables for the given interface + declaration, walking over all static members. */ + + void visit (InterfaceDeclaration *d) + { + if (d->type->ty == Terror) + { + error_at (make_location_t (d->loc), + "had semantic errors when compiling"); + return; + } + + if (!d->members) + return; + + /* Put out the members. */ + for (size_t i = 0; i < d->members->dim; i++) + { + Dsymbol *member = (*d->members)[i]; + member->accept (this); + } + + /* Generate C symbols. */ + d->csym = get_classinfo_decl (d); + + /* Put out the TypeInfo. */ + create_typeinfo (d->type, NULL); + d->type->vtinfo->accept (this); + + DECL_INITIAL (d->csym) = layout_classinfo (d); + d_linkonce_linkage (d->csym); + d_finish_decl (d->csym); + + /* Add this decl to the current binding level. */ + tree ctype = TREE_TYPE (build_ctype (d->type)); + if (TYPE_NAME (ctype)) + d_pushdecl (TYPE_NAME (ctype)); + } + + /* Write out compiler generated TypeInfo and initializer for the given + enum declaration. */ + + void visit (EnumDeclaration *d) + { + if (d->semanticRun >= PASSobj) + return; + + if (d->errors || d->type->ty == Terror) + { + error_at (make_location_t (d->loc), + "had semantic errors when compiling"); + return; + } + + if (d->isAnonymous ()) + return; + + /* Generate TypeInfo. */ + create_typeinfo (d->type, NULL); + + TypeEnum *tc = (TypeEnum *) d->type; + if (tc->sym->members && !d->type->isZeroInit ()) + { + /* Generate static initializer. */ + d->sinit = enum_initializer_decl (d); + DECL_INITIAL (d->sinit) = build_expr (tc->sym->defaultval, true); + + if (d->isInstantiated ()) + d_linkonce_linkage (d->sinit); + + d_finish_decl (d->sinit); + + /* Add this decl to the current binding level. */ + tree ctype = build_ctype (d->type); + if (TREE_CODE (ctype) == ENUMERAL_TYPE && TYPE_NAME (ctype)) + d_pushdecl (TYPE_NAME (ctype)); + } + + d->semanticRun = PASSobj; + } + + /* Finish up a variable declaration and push it into the current scope. + This can either be a static, local or manifest constant. */ + + void visit (VarDeclaration *d) + { + if (d->type->ty == Terror) + { + error_at (make_location_t (d->loc), + "had semantic errors when compiling"); + return; + } + + if (d->aliassym) + { + d->toAlias ()->accept (this); + return; + } + + /* Do not store variables we cannot take the address of, + but keep the values for purposes of debugging. */ + if (!d->canTakeAddressOf ()) + { + /* Don't know if there is a good way to handle instantiations. */ + if (d->isInstantiated ()) + return; + + tree decl = get_symbol_decl (d); + gcc_assert (d->_init && !d->_init->isVoidInitializer ()); + Expression *ie = initializerToExpression (d->_init); + + /* CONST_DECL was initially intended for enumerals and may be used for + scalars in general, but not for aggregates. Here a non-constant + value is generated anyway so as the CONST_DECL only serves as a + placeholder for the value, however the DECL itself should never be + referenced in any generated code, or passed to the back-end. */ + if (!d->type->isscalar ()) + DECL_INITIAL (decl) = build_expr (ie, false); + else + { + DECL_INITIAL (decl) = build_expr (ie, true); + d_pushdecl (decl); + rest_of_decl_compilation (decl, 1, 0); + } + } + else if (d->isDataseg () && !(d->storage_class & STCextern)) + { + tree decl = get_symbol_decl (d); + + /* Duplicated VarDeclarations map to the same symbol. Check if this + is the one declaration which will be emitted. */ + tree ident = DECL_ASSEMBLER_NAME (decl); + if (IDENTIFIER_DSYMBOL (ident) && IDENTIFIER_DSYMBOL (ident) != d) + return; + + /* How big a symbol can be should depend on back-end. */ + tree size = build_integer_cst (d->type->size (d->loc), + build_ctype (Type::tsize_t)); + if (!valid_constant_size_p (size)) + { + error_at (make_location_t (d->loc), "size is too large"); + return; + } + + if (d->_init && !d->_init->isVoidInitializer ()) + { + Expression *e = initializerToExpression (d->_init, d->type); + DECL_INITIAL (decl) = build_expr (e, true); + } + else + { + if (d->type->ty == Tstruct) + { + StructDeclaration *sd = ((TypeStruct *) d->type)->sym; + DECL_INITIAL (decl) = layout_struct_initializer (sd); + } + else + { + Expression *e = d->type->defaultInitLiteral (d->loc); + DECL_INITIAL (decl) = build_expr (e, true); + } + } + + /* Frontend should have already caught this. */ + gcc_assert (!integer_zerop (size) + || d->type->toBasetype ()->ty == Tsarray); + + d_finish_decl (decl); + + /* Maybe record the var against the current module. */ + register_module_decl (d); + } + else if (!d->isDataseg () && !d->isMember ()) + { + /* This is needed for VarDeclarations in mixins that are to be local + variables of a function. Otherwise, it would be enough to make + a check for isVarDeclaration() in DeclarationExp codegen. */ + declare_local_var (d); + + if (d->_init) + { + tree decl = get_symbol_decl (d); + + if (!d->_init->isVoidInitializer ()) + { + ExpInitializer *vinit = d->_init->isExpInitializer (); + Expression *ie = initializerToExpression (vinit); + tree exp = build_expr (ie); + + /* Maybe put variable on list of things needing destruction. */ + if (d->needsScopeDtor ()) + { + vec_safe_push (d_function_chain->vars_in_scope, decl); + /* Force a TARGET_EXPR to add the corresponding cleanup. */ + exp = force_target_expr (compound_expr (exp, decl)); + TARGET_EXPR_CLEANUP (exp) = build_expr (d->edtor); + } + + add_stmt (exp); + } + else if (d->size (d->loc) != 0) + { + /* Zero-length arrays do not have an initializer. */ + warning (OPT_Wuninitialized, "uninitialized variable '%s'", + d->ident ? d->ident->toChars () : "(no name)"); + } + } + } + } + + /* Generate and compile a static TypeInfo declaration, but only if it is + needed in the current compilation. */ + + void visit (TypeInfoDeclaration *d) + { + if (speculative_type_p (d->tinfo)) + return; + + tree t = get_typeinfo_decl (d); + DECL_INITIAL (t) = layout_typeinfo (d); + d_finish_decl (t); + } + + /* Finish up a function declaration and compile it all the way + down to assembler language output. */ + + void visit (FuncDeclaration *d) + { + /* Already generated the function. */ + if (d->semanticRun >= PASSobj) + return; + + /* Don't emit any symbols from gcc.attribute module. */ + if (gcc_attribute_p (d)) + return; + + /* Not emitting unittest functions. */ + if (!global.params.useUnitTests && d->isUnitTestDeclaration ()) + return; + + /* Check if any errors occurred when running semantic. */ + if (d->type->ty == Tfunction) + { + TypeFunction *tf = (TypeFunction *) d->type; + if (tf->next == NULL || tf->next->ty == Terror) + return; + } + + if (d->semantic3Errors) + return; + + if (d->isNested ()) + { + FuncDeclaration *fdp = d; + while (fdp && fdp->isNested ()) + { + fdp = fdp->toParent2 ()->isFuncDeclaration (); + + if (fdp == NULL) + break; + + /* Parent failed to compile, but errors were gagged. */ + if (fdp->semantic3Errors) + return; + } + } + + /* Ensure all semantic passes have run. */ + if (d->semanticRun < PASSsemantic3) + { + d->functionSemantic3 (); + Module::runDeferredSemantic3 (); + } + + if (global.errors) + return; + + /* Duplicated FuncDeclarations map to the same symbol. Check if this + is the one declaration which will be emitted. */ + tree fndecl = get_symbol_decl (d); + tree ident = DECL_ASSEMBLER_NAME (fndecl); + if (IDENTIFIER_DSYMBOL (ident) && IDENTIFIER_DSYMBOL (ident) != d) + return; + + if (!d->fbody) + { + rest_of_decl_compilation (fndecl, 1, 0); + return; + } + + if (global.params.verbose) + message ("function %s", d->toPrettyChars ()); + + /* Start generating code for this function. */ + gcc_assert (d->semanticRun == PASSsemantic3done); + d->semanticRun = PASSobj; + + tree old_context = start_function (d); + + tree parm_decl = NULL_TREE; + tree param_list = NULL_TREE; + + /* Special arguments... */ + + /* 'this' parameter: + For nested functions, D still generates a vthis, but it + should not be referenced in any expression. */ + if (d->vthis) + { + parm_decl = get_symbol_decl (d->vthis); + DECL_ARTIFICIAL (parm_decl) = 1; + TREE_READONLY (parm_decl) = 1; + + if (d->vthis->type == Type::tvoidptr) + { + /* Replace generic pointer with back-end closure type + (this wins for gdb). */ + tree frame_type = FRAMEINFO_TYPE (get_frameinfo (d)); + gcc_assert (frame_type != NULL_TREE); + TREE_TYPE (parm_decl) = build_pointer_type (frame_type); + } + + param_list = chainon (param_list, parm_decl); + d_function_chain->static_chain = parm_decl; + } + + /* _arguments parameter. */ + if (d->v_arguments) + { + parm_decl = get_symbol_decl (d->v_arguments); + param_list = chainon (param_list, parm_decl); + } + + /* formal function parameters. */ + size_t n_parameters = d->parameters ? d->parameters->dim : 0; + + for (size_t i = 0; i < n_parameters; i++) + { + VarDeclaration *param = (*d->parameters)[i]; + parm_decl = get_symbol_decl (param); + /* Chain them in the correct order. */ + param_list = chainon (param_list, parm_decl); + } + + DECL_ARGUMENTS (fndecl) = param_list; + rest_of_decl_compilation (fndecl, 1, 0); + + /* If this is a member function that nested (possibly indirectly) in another + function, construct an expession for this member function's static chain + by going through parent link of nested classes. */ + if (d->isThis ()) + { + AggregateDeclaration *ad = d->isThis (); + tree this_tree = get_symbol_decl (d->vthis); + + while (ad->isNested ()) + { + Dsymbol *pd = ad->toParent2 (); + tree vthis_field = get_symbol_decl (ad->vthis); + this_tree = component_ref (build_deref (this_tree), vthis_field); + + ad = pd->isAggregateDeclaration (); + if (ad == NULL) + { + cfun->language->static_chain = this_tree; + break; + } + } + } + + /* May change cfun->static_chain. */ + build_closure (d); + + if (d->vresult) + declare_local_var (d->vresult); + + if (d->v_argptr) + push_stmt_list (); + + /* Named return value optimisation support for D. + Implemented by overriding all the RETURN_EXPRs and replacing all + occurrences of VAR with the RESULT_DECL for the function. + This is only worth doing for functions that can return in memory. */ + if (d->nrvo_can) + { + tree restype = TREE_TYPE (DECL_RESULT (fndecl)); + + if (!AGGREGATE_TYPE_P (restype)) + d->nrvo_can = 0; + else + d->nrvo_can = aggregate_value_p (restype, fndecl); + } + + if (d->nrvo_can) + { + tree resdecl = DECL_RESULT (fndecl); + + TREE_TYPE (resdecl) + = build_reference_type (TREE_TYPE (resdecl)); + DECL_BY_REFERENCE (resdecl) = 1; + TREE_ADDRESSABLE (resdecl) = 0; + relayout_decl (resdecl); + + if (d->nrvo_var) + { + tree var = get_symbol_decl (d->nrvo_var); + + /* Copy name from VAR to RESULT. */ + DECL_NAME (resdecl) = DECL_NAME (var); + /* Don't forget that we take its address. */ + TREE_ADDRESSABLE (var) = 1; + resdecl = build_deref (resdecl); + + SET_DECL_VALUE_EXPR (var, resdecl); + DECL_HAS_VALUE_EXPR_P (var) = 1; + SET_DECL_LANG_NRVO (var, resdecl); + } + } + + build_function_body (d); + + /* Initialize the _argptr variable. */ + if (d->v_argptr) + { + tree body = pop_stmt_list (); + tree var = get_decl_tree (d->v_argptr); + var = build_address (var); + + tree init = build_call_expr (builtin_decl_explicit (BUILT_IN_VA_START), + 2, var, parm_decl); + declare_local_var (d->v_argptr); + add_stmt (init); + + tree cleanup = build_call_expr (builtin_decl_explicit (BUILT_IN_VA_END), + 1, var); + add_stmt (build2 (TRY_FINALLY_EXPR, void_type_node, body, cleanup)); + } + + finish_function (old_context); + + /* Maybe record the function against the current module. */ + register_module_decl (d); + } +}; + +/* Main entry point for the DeclVisitor interface to send + the Declaration AST class D to GCC back-end. */ + +void +build_decl_tree (Dsymbol *d) +{ + location_t saved_location = input_location; + + /* Set input location, empty DECL_SOURCE_FILE can crash debug generator. */ + if (d->loc.filename) + input_location = make_location_t (d->loc); + else + input_location = make_location_t (Loc ("<no_file>", 1, 0)); + + DeclVisitor v = DeclVisitor (); + d->accept (&v); + + input_location = saved_location; +} + +/* Return the decl for the symbol, create it if it doesn't already exist. */ + +tree +get_symbol_decl (Declaration *decl) +{ + if (decl->csym) + return decl->csym; + + /* Deal with placeholder symbols immediately: + SymbolDeclaration is used as a shell around an initializer symbol. */ + SymbolDeclaration *sd = decl->isSymbolDeclaration (); + if (sd) + { + decl->csym = aggregate_initializer_decl (sd->dsym); + return decl->csym; + } + + /* Global static TypeInfo declaration. */ + if (decl->isTypeInfoDeclaration ()) + return get_typeinfo_decl ((TypeInfoDeclaration *) decl); + + /* FuncAliasDeclaration is used to import functions from another scope. */ + FuncAliasDeclaration *fad = decl->isFuncAliasDeclaration (); + if (fad) + { + decl->csym = get_symbol_decl (fad->funcalias); + return decl->csym; + } + + /* It is possible for a field declaration symbol to be requested + before the parent type has been built. */ + if (decl->isField ()) + { + AggregateDeclaration *ad = decl->toParent ()->isAggregateDeclaration (); + gcc_assert (ad != NULL); + + /* Finishing off the type should create the associated FIELD_DECL. */ + build_ctype (ad->type); + gcc_assert (decl->csym != NULL); + + return decl->csym; + } + + /* Build the tree for the symbol. */ + FuncDeclaration *fd = decl->isFuncDeclaration (); + if (fd) + { + /* Run full semantic on functions we need to know about. */ + if (!fd->functionSemantic ()) + { + decl->csym = error_mark_node; + return decl->csym; + } + + decl->csym = build_decl (make_location_t (decl->loc), FUNCTION_DECL, + get_identifier (decl->ident->toChars ()), + NULL_TREE); + + /* Set function type afterwards as there could be self references. */ + TREE_TYPE (decl->csym) = build_ctype (fd->type); + + if (!fd->fbody) + DECL_EXTERNAL (decl->csym) = 1; + } + else + { + /* Build the variable declaration. */ + VarDeclaration *vd = decl->isVarDeclaration (); + gcc_assert (vd != NULL); + + tree_code code = vd->isParameter () ? PARM_DECL + : !vd->canTakeAddressOf () ? CONST_DECL + : VAR_DECL; + decl->csym = build_decl (make_location_t (decl->loc), code, + get_identifier (decl->ident->toChars ()), + declaration_type (vd)); + + /* If any alignment was set on the declaration. */ + if (vd->alignment != STRUCTALIGN_DEFAULT) + { + SET_DECL_ALIGN (decl->csym, vd->alignment * BITS_PER_UNIT); + DECL_USER_ALIGN (decl->csym) = 1; + } + + if (vd->storage_class & STCextern) + DECL_EXTERNAL (decl->csym) = 1; + } + + /* Set the declaration mangled identifier if static. */ + if (decl->isCodeseg () || decl->isDataseg ()) + { + tree mangled_name; + + if (decl->mangleOverride) + mangled_name = get_identifier (decl->mangleOverride); + else + mangled_name = get_identifier (mangle_decl (decl)); + + mangled_name = targetm.mangle_decl_assembler_name (decl->csym, + mangled_name); + /* The frontend doesn't handle duplicate definitions of unused symbols + with the same mangle. So a check is done here instead. */ + if (!DECL_EXTERNAL (decl->csym)) + { + if (IDENTIFIER_DSYMBOL (mangled_name)) + { + Declaration *other = IDENTIFIER_DSYMBOL (mangled_name); + + /* Non-templated variables shouldn't be defined twice. */ + if (!decl->isInstantiated ()) + ScopeDsymbol::multiplyDefined (decl->loc, decl, other); + + decl->csym = get_symbol_decl (other); + return decl->csym; + } + + IDENTIFIER_PRETTY_NAME (mangled_name) + = get_identifier (decl->toPrettyChars (true)); + IDENTIFIER_DSYMBOL (mangled_name) = decl; + } + + SET_DECL_ASSEMBLER_NAME (decl->csym, mangled_name); + } + + DECL_LANG_SPECIFIC (decl->csym) = build_lang_decl (decl); + DECL_CONTEXT (decl->csym) = d_decl_context (decl); + + if (TREE_CODE (decl->csym) == PARM_DECL) + { + /* Pass non-trivial structs by invisible reference. */ + if (TREE_ADDRESSABLE (TREE_TYPE (decl->csym))) + { + tree argtype = build_reference_type (TREE_TYPE (decl->csym)); + argtype = build_qualified_type (argtype, TYPE_QUAL_RESTRICT); + gcc_assert (!DECL_BY_REFERENCE (decl->csym)); + TREE_TYPE (decl->csym) = argtype; + DECL_BY_REFERENCE (decl->csym) = 1; + TREE_ADDRESSABLE (decl->csym) = 0; + relayout_decl (decl->csym); + decl->storage_class |= STCref; + } + + DECL_ARG_TYPE (decl->csym) = TREE_TYPE (decl->csym); + gcc_assert (TREE_CODE (DECL_CONTEXT (decl->csym)) == FUNCTION_DECL); + } + else if (TREE_CODE (decl->csym) == CONST_DECL) + { + /* Manifest constants have no address in memory. */ + TREE_CONSTANT (decl->csym) = 1; + TREE_READONLY (decl->csym) = 1; + } + else if (TREE_CODE (decl->csym) == FUNCTION_DECL) + { + /* The real function type may differ from its declaration. */ + tree fntype = TREE_TYPE (decl->csym); + tree newfntype = NULL_TREE; + + if (fd->isNested ()) + { + /* Add an extra argument for the frame/closure pointer, this is also + required to be compatible with D delegates. */ + newfntype = build_vthis_function (void_type_node, fntype); + } + else if (fd->isThis ()) + { + /* Add an extra argument for the 'this' parameter. The handle type is + used even if there is no debug info. It is needed to make sure + virtual member functions are not called statically. */ + AggregateDeclaration *ad = fd->isMember2 (); + tree handle = build_ctype (ad->handleType ()); + + /* If handle is a pointer type, get record type. */ + if (!ad->isStructDeclaration ()) + handle = TREE_TYPE (handle); + + newfntype = build_vthis_function (handle, fntype); + + /* Set the vindex on virtual functions. */ + if (fd->isVirtual () && fd->vtblIndex != -1) + { + DECL_VINDEX (decl->csym) = size_int (fd->vtblIndex); + DECL_VIRTUAL_P (decl->csym) = 1; + } + } + else if (fd->isMain () || fd->isCMain ()) + { + /* The main function is named 'D main' to distinguish from C main. */ + if (fd->isMain ()) + DECL_NAME (decl->csym) = get_identifier (fd->toPrettyChars (true)); + + /* 'void main' is implicitly converted to returning an int. */ + newfntype = build_function_type (d_int_type, TYPE_ARG_TYPES (fntype)); + } + + if (newfntype != NULL_TREE) + { + /* Copy the old attributes from the original type. */ + TYPE_ATTRIBUTES (newfntype) = TYPE_ATTRIBUTES (fntype); + TYPE_LANG_SPECIFIC (newfntype) = TYPE_LANG_SPECIFIC (fntype); + TREE_ADDRESSABLE (newfntype) = TREE_ADDRESSABLE (fntype); + TREE_TYPE (decl->csym) = newfntype; + d_keep (newfntype); + } + + /* Miscellaneous function flags. */ + if (fd->isMember2 () || fd->isFuncLiteralDeclaration ()) + { + /* See grokmethod in cp/decl.c. Maybe we shouldn't be setting inline + flags without reason or proper handling. */ + DECL_DECLARED_INLINE_P (decl->csym) = 1; + DECL_NO_INLINE_WARNING_P (decl->csym) = 1; + } + + /* Function was declared 'naked'. */ + if (fd->naked) + { + insert_decl_attribute (decl->csym, "naked"); + DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl->csym) = 1; + } + + /* Vector array operations are always compiler generated. */ + if (fd->isArrayOp) + { + TREE_PUBLIC (decl->csym) = 1; + DECL_ARTIFICIAL (decl->csym) = 1; + DECL_DECLARED_INLINE_P (decl->csym) = 1; + d_comdat_linkage (decl->csym); + } + + /* And so are ensure and require contracts. */ + if (fd->ident == Identifier::idPool ("ensure") + || fd->ident == Identifier::idPool ("require")) + { + DECL_ARTIFICIAL (decl->csym) = 1; + TREE_PUBLIC (decl->csym) = 1; + } + + if (decl->storage_class & STCfinal) + DECL_FINAL_P (decl->csym) = 1; + + /* Check whether this function is expanded by the frontend. */ + DECL_INTRINSIC_CODE (decl->csym) = INTRINSIC_NONE; + maybe_set_intrinsic (fd); + + /* For nested functions in particular, unnest fndecl in the cgraph, as + all static chain passing is handled by the front-end. Do this even + if we are not emitting the body. */ + struct cgraph_node *node = cgraph_node::get_create (decl->csym); + if (node->origin) + node->unnest (); + } + + /* Mark compiler generated temporaries as artificial. */ + if (decl->storage_class & STCtemp) + DECL_ARTIFICIAL (decl->csym) = 1; + + /* Propagate shared on the decl. */ + if (TYPE_SHARED (TREE_TYPE (decl->csym))) + TREE_ADDRESSABLE (decl->csym) = 1; + + /* Symbol was marked volatile. */ + if (decl->storage_class & STCvolatile) + TREE_THIS_VOLATILE (decl->csym) = 1; + + /* Protection attributes are used by the debugger. */ + if (decl->protection.kind == PROTprivate) + TREE_PRIVATE (decl->csym) = 1; + else if (decl->protection.kind == PROTprotected) + TREE_PROTECTED (decl->csym) = 1; + + /* Likewise, so could the deprecated attribute. */ + if (decl->storage_class & STCdeprecated) + TREE_DEPRECATED (decl->csym) = 1; + +#if TARGET_DLLIMPORT_DECL_ATTRIBUTES + /* Have to test for import first. */ + if (decl->isImportedSymbol ()) + { + insert_decl_attribute (decl->csym, "dllimport"); + DECL_DLLIMPORT_P (decl->csym) = 1; + } + else if (decl->isExport ()) + insert_decl_attribute (decl->csym, "dllexport"); +#endif + + if (decl->isDataseg () || decl->isCodeseg () || decl->isThreadlocal ()) + { + /* Set TREE_PUBLIC by default, but allow private template to override. */ + if (!fd || !fd->isNested ()) + TREE_PUBLIC (decl->csym) = 1; + + TREE_STATIC (decl->csym) = 1; + /* The decl has not been defined -- yet. */ + DECL_EXTERNAL (decl->csym) = 1; + + if (decl->isInstantiated ()) + d_linkonce_linkage (decl->csym); + } + + /* Symbol is going in thread local storage. */ + if (decl->isThreadlocal () && !DECL_ARTIFICIAL (decl->csym)) + { + if (global.params.vtls) + message (decl->loc, "`%s` is thread local", decl->toChars ()); + + set_decl_tls_model (decl->csym, decl_default_tls_model (decl->csym)); + } + + /* Apply any user attributes that may affect semantic meaning. */ + if (decl->userAttribDecl) + { + Expressions *attrs = decl->userAttribDecl->getAttributes (); + decl_attributes (&decl->csym, build_attributes (attrs), 0); + } + else if (DECL_ATTRIBUTES (decl->csym) != NULL) + decl_attributes (&decl->csym, DECL_ATTRIBUTES (decl->csym), 0); + + /* %% Probably should be a little more intelligent about setting this. */ + TREE_USED (decl->csym) = 1; + d_keep (decl->csym); + + return decl->csym; +} + +/* Returns a declaration for a VAR_DECL. Used to create compiler-generated + global variables. */ + +tree +declare_extern_var (tree ident, tree type) +{ + /* If the VAR_DECL has already been declared, return it. */ + if (IDENTIFIER_DECL_TREE (ident)) + return IDENTIFIER_DECL_TREE (ident); + + tree name = IDENTIFIER_PRETTY_NAME (ident) + ? IDENTIFIER_PRETTY_NAME (ident) : ident; + tree decl = build_decl (input_location, VAR_DECL, name, type); + + IDENTIFIER_DECL_TREE (ident) = decl; + d_keep (decl); + + SET_DECL_ASSEMBLER_NAME (decl, ident); + DECL_ARTIFICIAL (decl) = 1; + TREE_STATIC (decl) = 1; + TREE_PUBLIC (decl) = 1; + + /* The decl has not been defined -- yet. */ + DECL_EXTERNAL (decl) = 1; + + return decl; +} + +/* Add local variable VAR into the current function body. */ + +void +declare_local_var (VarDeclaration *var) +{ + gcc_assert (!var->isDataseg () && !var->isMember ()); + gcc_assert (current_function_decl != NULL_TREE); + + FuncDeclaration *fd = cfun->language->function; + tree decl = get_symbol_decl (var); + + gcc_assert (!TREE_STATIC (decl)); + d_pushdecl (decl); + DECL_CONTEXT (decl) = current_function_decl; + + /* Compiler generated symbols. */ + if (var == fd->vresult || var == fd->v_argptr) + DECL_ARTIFICIAL (decl) = 1; + + if (DECL_LANG_FRAME_FIELD (decl)) + { + /* Fixes debugging local variables. */ + SET_DECL_VALUE_EXPR (decl, get_decl_tree (var)); + DECL_HAS_VALUE_EXPR_P (decl) = 1; + } +} + +/* Return an unnamed local temporary of type TYPE. */ + +tree +build_local_temp (tree type) +{ + tree decl = build_decl (input_location, VAR_DECL, NULL_TREE, type); + + DECL_CONTEXT (decl) = current_function_decl; + DECL_ARTIFICIAL (decl) = 1; + DECL_IGNORED_P (decl) = 1; + d_pushdecl (decl); + + return decl; +} + +/* Return the correct decl to be used for DECL. For VAR_DECLs, this could + instead be a FIELD_DECL from a closure, or a RESULT_DECL from a named return + value. For PARM_DECLs, this could be a FIELD_DECL for a non-local `this'. + For all other kinds of decls, this just returns the result of + get_symbol_decl(). */ + +tree +get_decl_tree (Declaration *decl) +{ + tree t = get_symbol_decl (decl); + FuncDeclaration *fd = cfun ? cfun->language->function : NULL; + VarDeclaration *vd = decl->isVarDeclaration (); + + /* If cfun is NULL, then this is a global static. */ + if (vd == NULL || fd == NULL) + return t; + + /* Get the named return value. */ + if (DECL_LANG_NRVO (t)) + return DECL_LANG_NRVO (t); + + /* Get the closure holding the var decl. */ + if (DECL_LANG_FRAME_FIELD (t)) + { + FuncDeclaration *parent = vd->toParent2 ()->isFuncDeclaration (); + tree frame_ref = get_framedecl (fd, parent); + + return component_ref (build_deref (frame_ref), + DECL_LANG_FRAME_FIELD (t)); + } + + /* Get the non-local 'this' value by going through parent link + of nested classes, this routine pretty much undoes what + getRightThis in the frontend removes from codegen. */ + if (vd->parent != fd && vd->isThisDeclaration ()) + { + /* Find the first parent that is a member function. */ + while (!fd->isMember2 ()) + { + gcc_assert (fd->vthis); + fd = fd->toParent2 ()->isFuncDeclaration (); + gcc_assert (fd != NULL); + } + + AggregateDeclaration *ad = fd->isThis (); + gcc_assert (ad != NULL); + + t = get_decl_tree (fd->vthis); + Dsymbol *outer = fd; + + while (outer != vd->parent) + { + gcc_assert (ad != NULL); + outer = ad->toParent2 (); + + /* Get the this->this parent link. */ + tree vfield = get_symbol_decl (ad->vthis); + t = component_ref (build_deref (t), vfield); + + ad = outer->isAggregateDeclaration (); + if (ad != NULL) + continue; + + fd = outer->isFuncDeclaration (); + while (fd != NULL) + { + /* If outer function creates a closure, then the 'this' + value would be the closure pointer, and the real + 'this' the first field of that closure. */ + tree ff = get_frameinfo (fd); + if (FRAMEINFO_CREATES_FRAME (ff)) + { + t = build_nop (build_pointer_type (FRAMEINFO_TYPE (ff)), t); + t = indirect_ref (build_ctype (fd->vthis->type), t); + } + + if (fd == vd->parent) + break; + + /* Continue looking for the right `this'. */ + outer = outer->toParent2 (); + fd = outer->isFuncDeclaration (); + } + + ad = outer->isAggregateDeclaration (); + } + + return t; + } + + /* Auto variable that the back end will handle for us. */ + return t; +} + +/* Finish up a variable declaration and compile it all the way to + the assembler language output. */ + +void +d_finish_decl (tree decl) +{ + gcc_assert (!error_operand_p (decl)); + + /* We are sending this symbol to object file, can't be extern. */ + TREE_STATIC (decl) = 1; + DECL_EXTERNAL (decl) = 0; + + /* Update the TLS model as the linkage has been modified. */ + if (DECL_THREAD_LOCAL_P (decl)) + set_decl_tls_model (decl, decl_default_tls_model (decl)); + + relayout_decl (decl); + + if (flag_checking && DECL_INITIAL (decl)) + { + /* Initializer must never be bigger than symbol size. */ + dinteger_t tsize = int_size_in_bytes (TREE_TYPE (decl)); + dinteger_t dtsize = int_size_in_bytes (TREE_TYPE (DECL_INITIAL (decl))); + + if (tsize < dtsize) + { + tree name = DECL_ASSEMBLER_NAME (decl); + + internal_error ("Mismatch between declaration %qE size (%wd) and " + "its initializer size (%wd).", + IDENTIFIER_PRETTY_NAME (name) + ? IDENTIFIER_PRETTY_NAME (name) : name, + tsize, dtsize); + } + } + + /* Without weak symbols, symbol should be put in .common, but that can't + be done if there is a nonzero initializer. */ + if (DECL_COMDAT (decl) && DECL_COMMON (decl) + && initializer_zerop (DECL_INITIAL (decl))) + DECL_INITIAL (decl) = error_mark_node; + + /* Add this decl to the current binding level. */ + d_pushdecl (decl); + + rest_of_decl_compilation (decl, 1, 0); +} + +/* Thunk code is based on g++. */ + +static int thunk_labelno; + +/* Create a static alias to function. */ + +static tree +make_alias_for_thunk (tree function) +{ + tree alias; + char buf[256]; + + /* Thunks may reference extern functions which cannot be aliased. */ + if (DECL_EXTERNAL (function)) + return function; + + targetm.asm_out.generate_internal_label (buf, "LTHUNK", thunk_labelno); + thunk_labelno++; + + alias = build_decl (DECL_SOURCE_LOCATION (function), FUNCTION_DECL, + get_identifier (buf), TREE_TYPE (function)); + DECL_LANG_SPECIFIC (alias) = DECL_LANG_SPECIFIC (function); + lang_hooks.dup_lang_specific_decl (alias); + DECL_CONTEXT (alias) = NULL_TREE; + TREE_READONLY (alias) = TREE_READONLY (function); + TREE_THIS_VOLATILE (alias) = TREE_THIS_VOLATILE (function); + TREE_PUBLIC (alias) = 0; + + DECL_EXTERNAL (alias) = 0; + DECL_ARTIFICIAL (alias) = 1; + + DECL_DECLARED_INLINE_P (alias) = 0; + DECL_INITIAL (alias) = error_mark_node; + DECL_ARGUMENTS (alias) = copy_list (DECL_ARGUMENTS (function)); + + TREE_ADDRESSABLE (alias) = 1; + TREE_USED (alias) = 1; + SET_DECL_ASSEMBLER_NAME (alias, DECL_NAME (alias)); + + if (!flag_syntax_only) + { + cgraph_node *aliasn; + aliasn = cgraph_node::create_same_body_alias (alias, function); + gcc_assert (aliasn != NULL); + } + return alias; +} + +/* Emit the definition of a D vtable thunk. */ + +static void +finish_thunk (tree thunk, tree function) +{ + /* Setup how D thunks are outputted. */ + int fixed_offset = -THUNK_LANG_OFFSET (thunk); + bool this_adjusting = true; + tree alias; + + if (TARGET_USE_LOCAL_THUNK_ALIAS_P (function)) + alias = make_alias_for_thunk (function); + else + alias = function; + + TREE_ADDRESSABLE (function) = 1; + TREE_USED (function) = 1; + + if (flag_syntax_only) + { + TREE_ASM_WRITTEN (thunk) = 1; + return; + } + + if (TARGET_USE_LOCAL_THUNK_ALIAS_P (function) + && targetm_common.have_named_sections) + { + tree fn = function; + symtab_node *symbol = symtab_node::get (function); + + if (symbol != NULL && symbol->alias) + { + if (symbol->analyzed) + fn = symtab_node::get (function)->ultimate_alias_target ()->decl; + else + fn = symtab_node::get (function)->alias_target; + } + resolve_unique_section (fn, 0, flag_function_sections); + + if (DECL_SECTION_NAME (fn) != NULL && DECL_ONE_ONLY (fn)) + { + resolve_unique_section (thunk, 0, flag_function_sections); + + /* Output the thunk into the same section as function. */ + set_decl_section_name (thunk, DECL_SECTION_NAME (fn)); + symtab_node::get (thunk)->implicit_section + = symtab_node::get (fn)->implicit_section; + } + } + + /* Set up cloned argument trees for the thunk. */ + tree t = NULL_TREE; + for (tree a = DECL_ARGUMENTS (function); a; a = DECL_CHAIN (a)) + { + tree x = copy_node (a); + DECL_CHAIN (x) = t; + DECL_CONTEXT (x) = thunk; + SET_DECL_RTL (x, NULL); + DECL_HAS_VALUE_EXPR_P (x) = 0; + TREE_ADDRESSABLE (x) = 0; + t = x; + } + DECL_ARGUMENTS (thunk) = nreverse (t); + TREE_ASM_WRITTEN (thunk) = 1; + + cgraph_node *funcn, *thunk_node; + + funcn = cgraph_node::get_create (function); + gcc_assert (funcn); + thunk_node = funcn->create_thunk (thunk, thunk, this_adjusting, + fixed_offset, 0, 0, 0, alias); + + if (DECL_ONE_ONLY (function)) + thunk_node->add_to_same_comdat_group (funcn); + + /* Target assemble_mi_thunk doesn't work across section boundaries + on many targets, instead force thunk to be expanded in gimple. */ + if (DECL_EXTERNAL (function)) + { + /* cgraph::expand_thunk writes over current_function_decl, so if this + could ever be in use by the codegen pass, we want to know about it. */ + gcc_assert (current_function_decl == NULL_TREE); + + if (!stdarg_p (TREE_TYPE (thunk))) + { + thunk_node->create_edge (funcn, NULL, thunk_node->count); + thunk_node->expand_thunk (false, true); + } + + /* Tell the back-end to not bother inlining the function, this is + assumed not to work as it could be referencing symbols outside + of the current compilation unit. */ + DECL_UNINLINABLE (function) = 1; + } +} + +/* Return a thunk to DECL. Thunks adjust the incoming `this' pointer by OFFSET. + Adjustor thunks are created and pointers to them stored in the method entries + in the vtable in order to set the this pointer to the start of the object + instance corresponding to the implementing method. */ + +tree +make_thunk (FuncDeclaration *decl, int offset) +{ + tree function = get_symbol_decl (decl); + + if (!DECL_ARGUMENTS (function) || !DECL_RESULT (function)) + { + /* Compile the function body before generating the thunk, this is done + even if the decl is external to the current module. */ + if (decl->fbody) + build_decl_tree (decl); + else + { + /* Build parameters for functions that are not being compiled, + so that they can be correctly cloned in finish_thunk. */ + tree fntype = TREE_TYPE (function); + tree params = NULL_TREE; + + for (tree t = TYPE_ARG_TYPES (fntype); t; t = TREE_CHAIN (t)) + { + if (t == void_list_node) + break; + + tree param = build_decl (DECL_SOURCE_LOCATION (function), + PARM_DECL, NULL_TREE, TREE_VALUE (t)); + DECL_ARG_TYPE (param) = TREE_TYPE (param); + DECL_ARTIFICIAL (param) = 1; + DECL_IGNORED_P (param) = 1; + DECL_CONTEXT (param) = function; + params = chainon (params, param); + } + + DECL_ARGUMENTS (function) = params; + + /* Also build the result decl, which is needed when force creating + the thunk in gimple inside cgraph_node::expand_thunk. */ + tree resdecl = build_decl (DECL_SOURCE_LOCATION (function), + RESULT_DECL, NULL_TREE, + TREE_TYPE (fntype)); + DECL_ARTIFICIAL (resdecl) = 1; + DECL_IGNORED_P (resdecl) = 1; + DECL_CONTEXT (resdecl) = function; + DECL_RESULT (function) = resdecl; + } + } + + /* Don't build the thunk if the compilation step failed. */ + if (global.errors) + return error_mark_node; + + /* See if we already have the thunk in question. */ + for (tree t = DECL_LANG_THUNKS (function); t; t = DECL_CHAIN (t)) + { + if (THUNK_LANG_OFFSET (t) == offset) + return t; + } + + tree thunk = build_decl (DECL_SOURCE_LOCATION (function), + FUNCTION_DECL, NULL_TREE, TREE_TYPE (function)); + DECL_LANG_SPECIFIC (thunk) = DECL_LANG_SPECIFIC (function); + lang_hooks.dup_lang_specific_decl (thunk); + THUNK_LANG_OFFSET (thunk) = offset; + + TREE_READONLY (thunk) = TREE_READONLY (function); + TREE_THIS_VOLATILE (thunk) = TREE_THIS_VOLATILE (function); + TREE_NOTHROW (thunk) = TREE_NOTHROW (function); + + DECL_CONTEXT (thunk) = d_decl_context (decl); + + /* Thunks inherit the public access of the function they are targetting. */ + TREE_PUBLIC (thunk) = TREE_PUBLIC (function); + DECL_EXTERNAL (thunk) = 0; + + /* Thunks are always addressable. */ + TREE_ADDRESSABLE (thunk) = 1; + TREE_USED (thunk) = 1; + DECL_ARTIFICIAL (thunk) = 1; + DECL_DECLARED_INLINE_P (thunk) = 0; + + DECL_VISIBILITY (thunk) = DECL_VISIBILITY (function); + DECL_COMDAT (thunk) = DECL_COMDAT (function); + DECL_WEAK (thunk) = DECL_WEAK (function); + + tree target_name = DECL_ASSEMBLER_NAME (function); + unsigned identlen = IDENTIFIER_LENGTH (target_name) + 14; + const char *ident = XNEWVEC (const char, identlen); + snprintf (CONST_CAST (char *, ident), identlen, + "_DT%u%s", offset, IDENTIFIER_POINTER (target_name)); + + DECL_NAME (thunk) = get_identifier (ident); + SET_DECL_ASSEMBLER_NAME (thunk, DECL_NAME (thunk)); + + d_keep (thunk); + + finish_thunk (thunk, function); + + /* Add it to the list of thunks associated with the function. */ + DECL_LANG_THUNKS (thunk) = NULL_TREE; + DECL_CHAIN (thunk) = DECL_LANG_THUNKS (function); + DECL_LANG_THUNKS (function) = thunk; + + return thunk; +} + +/* Create the FUNCTION_DECL for a function definition. + This function creates a binding context for the function body + as well as setting up the FUNCTION_DECL in current_function_decl. + Returns the previous function context if it was already set. */ + +tree +start_function (FuncDeclaration *fd) +{ + tree fndecl = get_symbol_decl (fd); + + /* Function has been defined, check now whether we intend to send it to + object file, or it really is extern. Such as inlinable functions from + modules not in this compilation, or thunk aliases. */ + TemplateInstance *ti = fd->isInstantiated (); + if (ti && ti->needsCodegen ()) + { + /* Warn about templates instantiated in this compilation. */ + if (ti == fd->parent) + { + warning (OPT_Wtemplates, "%s %qs instantiated", + ti->kind (), ti->toPrettyChars (false)); + } + + DECL_EXTERNAL (fndecl) = 0; + } + else + { + Module *md = fd->getModule (); + if (md && md->isRoot ()) + DECL_EXTERNAL (fndecl) = 0; + } + + DECL_INITIAL (fndecl) = error_mark_node; + + /* Add this decl to the current binding level. */ + d_pushdecl (fndecl); + + /* Save the current function context. */ + tree old_context = current_function_decl; + + if (old_context) + push_function_context (); + + /* Let GCC know the current scope is this function. */ + current_function_decl = fndecl; + + tree restype = TREE_TYPE (TREE_TYPE (fndecl)); + tree resdecl = build_decl (make_location_t (fd->loc), RESULT_DECL, + NULL_TREE, restype); + + DECL_RESULT (fndecl) = resdecl; + DECL_CONTEXT (resdecl) = fndecl; + DECL_ARTIFICIAL (resdecl) = 1; + DECL_IGNORED_P (resdecl) = 1; + + /* Initialize the RTL code for the function. */ + allocate_struct_function (fndecl, false); + + /* Store the end of the function. */ + if (fd->endloc.filename) + cfun->function_end_locus = make_location_t (fd->endloc); + else + cfun->function_end_locus = DECL_SOURCE_LOCATION (fndecl); + + cfun->language = ggc_cleared_alloc<language_function> (); + cfun->language->function = fd; + + /* Default chain value is 'null' unless parent found. */ + cfun->language->static_chain = null_pointer_node; + + /* Find module for this function. */ + for (Dsymbol *p = fd->parent; p != NULL; p = p->parent) + { + cfun->language->module = p->isModule (); + if (cfun->language->module) + break; + } + gcc_assert (cfun->language->module != NULL); + + /* Begin the statement tree for this function. */ + push_stmt_list (); + push_binding_level (level_function); + + return old_context; +} + +/* Finish up a function declaration and compile that function all + the way to assembler language output. The free the storage for + the function definition. Restores the previous function context. */ + +void +finish_function (tree old_context) +{ + tree fndecl = current_function_decl; + + /* Tie off the statement tree for this function. */ + tree block = pop_binding_level (); + tree body = pop_stmt_list (); + tree bind = build3 (BIND_EXPR, void_type_node, + BLOCK_VARS (block), body, block); + + gcc_assert (vec_safe_is_empty (d_function_chain->stmt_list)); + + /* Back-end expects a statement list to come from somewhere, however + pop_stmt_list returns expressions when there is a single statement. + So here we create a statement list unconditionally. */ + if (TREE_CODE (body) != STATEMENT_LIST) + { + tree stmtlist = alloc_stmt_list (); + append_to_statement_list_force (body, &stmtlist); + BIND_EXPR_BODY (bind) = stmtlist; + } + else if (!STATEMENT_LIST_HEAD (body)) + { + /* For empty functions add a void return. */ + append_to_statement_list_force (return_expr (NULL_TREE), &body); + } + + DECL_SAVED_TREE (fndecl) = bind; + + if (!errorcount && !global.errors) + { + /* Dump the D-specific tree IR. */ + dump_function (TDI_original, fndecl); + + cgraph_node::finalize_function (fndecl, true); + } + + /* We're leaving the context of this function, so free it. */ + ggc_free (cfun->language); + cfun->language = NULL; + set_cfun (NULL); + + if (old_context) + pop_function_context (); + + current_function_decl = old_context; +} + +/* Mark DECL, which is a VAR_DECL or FUNCTION_DECL as a symbol that + must be emitted in this, output module. */ + +void +mark_needed (tree decl) +{ + TREE_USED (decl) = 1; + + if (TREE_CODE (decl) == FUNCTION_DECL) + { + struct cgraph_node *node = cgraph_node::get_create (decl); + node->forced_by_abi = true; + } + else if (VAR_P (decl)) + { + struct varpool_node *node = varpool_node::get_create (decl); + node->forced_by_abi = true; + } +} + +/* Get the offset to the BC's vtbl[] initializer from the start of CD. + Returns "~0u" if the base class is not found in any vtable interfaces. */ + +unsigned +base_vtable_offset (ClassDeclaration *cd, BaseClass *bc) +{ + unsigned csymoffset = Target::classinfosize; + unsigned interfacesize = int_size_in_bytes (vtbl_interface_type_node); + csymoffset += cd->vtblInterfaces->dim * interfacesize; + + for (size_t i = 0; i < cd->vtblInterfaces->dim; i++) + { + BaseClass *b = (*cd->vtblInterfaces)[i]; + if (b == bc) + return csymoffset; + csymoffset += b->sym->vtbl.dim * Target::ptrsize; + } + + /* Check all overriding interface vtbl[]s. */ + for (ClassDeclaration *cd2 = cd->baseClass; cd2; cd2 = cd2->baseClass) + { + for (size_t k = 0; k < cd2->vtblInterfaces->dim; k++) + { + BaseClass *bs = (*cd2->vtblInterfaces)[k]; + if (bs->fillVtbl (cd, NULL, 0)) + { + if (bc == bs) + return csymoffset; + csymoffset += bs->sym->vtbl.dim * Target::ptrsize; + } + } + } + + return ~0u; +} + +/* Get the VAR_DECL of the vtable symbol for DECL. If this does not yet exist, + create it. The vtable is accessible via ClassInfo, but since it is needed + frequently (like for rtti comparisons), make it directly accessible. */ + +tree +get_vtable_decl (ClassDeclaration *decl) +{ + if (decl->vtblsym) + return decl->vtblsym; + + tree ident = mangle_internal_decl (decl, "__vtbl", "Z"); + /* Note: Using a static array type for the VAR_DECL, the DECL_INITIAL value + will have a different type. However the back-end seems to accept this. */ + tree type = build_ctype (Type::tvoidptr->sarrayOf (decl->vtbl.dim)); + + decl->vtblsym = declare_extern_var (ident, type); + DECL_LANG_SPECIFIC (decl->vtblsym) = build_lang_decl (NULL); + + /* Class is a reference, want the record type. */ + DECL_CONTEXT (decl->vtblsym) = TREE_TYPE (build_ctype (decl->type)); + TREE_READONLY (decl->vtblsym) = 1; + DECL_VIRTUAL_P (decl->vtblsym) = 1; + + SET_DECL_ALIGN (decl->vtblsym, TARGET_VTABLE_ENTRY_ALIGN); + DECL_USER_ALIGN (decl->vtblsym) = true; + + return decl->vtblsym; +} + +/* Helper function of build_class_instance. Find the field inside aggregate + TYPE identified by IDENT at field OFFSET. */ + +static tree +find_aggregate_field (tree type, tree ident, tree offset) +{ + tree fields = TYPE_FIELDS (type); + + for (tree field = fields; field != NULL_TREE; field = TREE_CHAIN (field)) + { + if (DECL_NAME (field) == NULL_TREE + && RECORD_OR_UNION_TYPE_P (TREE_TYPE (field)) + && ANON_AGGR_TYPE_P (TREE_TYPE (field))) + { + /* Search nesting anonymous structs and unions. */ + tree vfield = find_aggregate_field (TREE_TYPE (field), + ident, offset); + if (vfield != NULL_TREE) + return vfield; + } + else if (DECL_NAME (field) == ident + && (offset == NULL_TREE + || DECL_FIELD_OFFSET (field) == offset)) + { + /* Found matching field at offset. */ + return field; + } + } + + return NULL_TREE; +} + +/* Helper function of build_new_class_expr. Return a constructor that matches + the layout of the class expression EXP. */ + +static tree +build_class_instance (ClassReferenceExp *exp) +{ + ClassDeclaration *cd = exp->originalClass (); + tree type = TREE_TYPE (build_ctype (exp->value->stype)); + vec<constructor_elt, va_gc> *ve = NULL; + + /* The set base vtable field. */ + tree vptr = build_address (get_vtable_decl (cd)); + CONSTRUCTOR_APPEND_ELT (ve, TYPE_FIELDS (type), vptr); + + /* Go through the inheritance graph from top to bottom. This will add all + values to the constructor out of order, however build_struct_literal + will re-order all values before returning the finished literal. */ + for (ClassDeclaration *bcd = cd; bcd != NULL; bcd = bcd->baseClass) + { + /* Anonymous vtable interface fields are laid out before the fields of + each class. The interface offset is used to determine where to put + the classinfo offset reference. */ + for (size_t i = 0; i < bcd->vtblInterfaces->dim; i++) + { + BaseClass *bc = (*bcd->vtblInterfaces)[i]; + + for (ClassDeclaration *cd2 = cd; 1; cd2 = cd2->baseClass) + { + gcc_assert (cd2 != NULL); + + unsigned csymoffset = base_vtable_offset (cd2, bc); + /* If the base class vtable was found. */ + if (csymoffset != ~0u) + { + tree csym = build_address (get_classinfo_decl (cd2)); + csym = build_offset (csym, size_int (csymoffset)); + + tree field = find_aggregate_field (type, NULL_TREE, + size_int (bc->offset)); + gcc_assert (field != NULL_TREE); + + CONSTRUCTOR_APPEND_ELT (ve, field, csym); + break; + } + } + } + + /* Generate initial values of all fields owned by current class. + Use both the name and offset to find the right field. */ + for (size_t i = 0; i < bcd->fields.dim; i++) + { + VarDeclaration *vfield = bcd->fields[i]; + int index = exp->findFieldIndexByName (vfield); + gcc_assert (index != -1); + + Expression *value = (*exp->value->elements)[index]; + if (!value) + continue; + + /* Use find_aggregate_field to get the overridden field decl, + instead of the field associated with the base class. */ + tree field = get_symbol_decl (bcd->fields[i]); + field = find_aggregate_field (type, DECL_NAME (field), + DECL_FIELD_OFFSET (field)); + gcc_assert (field != NULL_TREE); + + CONSTRUCTOR_APPEND_ELT (ve, field, build_expr (value, true)); + } + } + + return build_struct_literal (type, ve); +} + +/* Get the VAR_DECL of a class instance representing EXPR as static data. + If this does not yet exist, create it. This is used to support initializing + a static variable that is of a class type using values known during CTFE. + In user code, it is analogous to the following code snippet. + + enum E = new C(1, 2, 3); + + That we write the contents of `C(1, 2, 3)' to static data is only a compiler + implementation detail. The initialization of these symbols could be done at + run-time using during as part of the module initialization or shared static + constructors phase of run-time start-up - whichever comes after `gc_init()'. + And infact that would be the better thing to do here eventually. */ + +tree +build_new_class_expr (ClassReferenceExp *expr) +{ + if (expr->value->sym) + return expr->value->sym; + + /* Build the reference symbol. */ + tree type = build_ctype (expr->value->stype); + expr->value->sym = build_artificial_decl (TREE_TYPE (type), NULL_TREE, "C"); + + DECL_INITIAL (expr->value->sym) = build_class_instance (expr); + d_pushdecl (expr->value->sym); + rest_of_decl_compilation (expr->value->sym, 1, 0); + + return expr->value->sym; +} + +/* Get the VAR_DECL of the static initializer symbol for the struct/class DECL. + If this does not yet exist, create it. The static initializer data is + accessible via TypeInfo, and is also used in 'new class' and default + initializing struct literals. */ + +tree +aggregate_initializer_decl (AggregateDeclaration *decl) +{ + if (decl->sinit) + return decl->sinit; + + /* Class is a reference, want the record type. */ + tree type = build_ctype (decl->type); + StructDeclaration *sd = decl->isStructDeclaration (); + if (!sd) + type = TREE_TYPE (type); + + tree ident = mangle_internal_decl (decl, "__init", "Z"); + + decl->sinit = declare_extern_var (ident, type); + DECL_LANG_SPECIFIC (decl->sinit) = build_lang_decl (NULL); + + DECL_CONTEXT (decl->sinit) = type; + TREE_READONLY (decl->sinit) = 1; + + /* Honor struct alignment set by user. */ + if (sd && sd->alignment != STRUCTALIGN_DEFAULT) + { + SET_DECL_ALIGN (decl->sinit, sd->alignment * BITS_PER_UNIT); + DECL_USER_ALIGN (decl->sinit) = true; + } + + return decl->sinit; +} + +/* Generate the data for the static initializer. */ + +tree +layout_class_initializer (ClassDeclaration *cd) +{ + NewExp *ne = NewExp::create (cd->loc, NULL, NULL, cd->type, NULL); + ne->type = cd->type; + + Expression *e = ne->ctfeInterpret (); + gcc_assert (e->op == TOKclassreference); + + return build_class_instance ((ClassReferenceExp *) e); +} + +tree +layout_struct_initializer (StructDeclaration *sd) +{ + StructLiteralExp *sle = StructLiteralExp::create (sd->loc, sd, NULL); + + if (!sd->fill (sd->loc, sle->elements, true)) + gcc_unreachable (); + + sle->type = sd->type; + return build_expr (sle, true); +} + +/* Get the VAR_DECL of the static initializer symbol for the enum DECL. + If this does not yet exist, create it. The static initializer data is + accessible via TypeInfo_Enum, but the field member type is a byte[] that + requires a pointer to a symbol reference. */ + +tree +enum_initializer_decl (EnumDeclaration *decl) +{ + if (decl->sinit) + return decl->sinit; + + tree type = build_ctype (decl->type); + + Identifier *ident_save = decl->ident; + if (!decl->ident) + decl->ident = Identifier::generateId ("__enum"); + tree ident = mangle_internal_decl (decl, "__init", "Z"); + decl->ident = ident_save; + + decl->sinit = declare_extern_var (ident, type); + DECL_LANG_SPECIFIC (decl->sinit) = build_lang_decl (NULL); + + DECL_CONTEXT (decl->sinit) = d_decl_context (decl); + TREE_READONLY (decl->sinit) = 1; + + return decl->sinit; +} + +/* Return an anonymous static variable of type TYPE, initialized with INIT, + and optionally prefixing the name with PREFIX. */ + +tree +build_artificial_decl (tree type, tree init, const char *prefix) +{ + tree decl = build_decl (UNKNOWN_LOCATION, VAR_DECL, NULL_TREE, type); + const char *name = prefix ? prefix : "___s"; + char *label; + + ASM_FORMAT_PRIVATE_NAME (label, name, DECL_UID (decl)); + SET_DECL_ASSEMBLER_NAME (decl, get_identifier (label)); + DECL_NAME (decl) = DECL_ASSEMBLER_NAME (decl); + + TREE_PUBLIC (decl) = 0; + TREE_STATIC (decl) = 1; + TREE_USED (decl) = 1; + DECL_IGNORED_P (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + + /* Perhaps at some point the initializer constant should be hashed + to remove duplicates. */ + DECL_INITIAL (decl) = init; + + return decl; +} + +/* Build TYPE_DECL for the declaration DSYM. */ + +void +build_type_decl (tree type, Dsymbol *dsym) +{ + if (TYPE_STUB_DECL (type)) + return; + + gcc_assert (!POINTER_TYPE_P (type)); + + tree decl = build_decl (make_location_t (dsym->loc), TYPE_DECL, + get_identifier (dsym->ident->toChars ()), type); + SET_DECL_ASSEMBLER_NAME (decl, get_identifier (mangle_decl (dsym))); + TREE_PUBLIC (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + DECL_CONTEXT (decl) = d_decl_context (dsym); + + TYPE_CONTEXT (type) = DECL_CONTEXT (decl); + TYPE_NAME (type) = decl; + + /* Not sure if there is a need for separate TYPE_DECLs in + TYPE_NAME and TYPE_STUB_DECL. */ + if (TREE_CODE (type) == ENUMERAL_TYPE || RECORD_OR_UNION_TYPE_P (type)) + TYPE_STUB_DECL (type) = decl; + + rest_of_decl_compilation (decl, SCOPE_FILE_SCOPE_P (decl), 0); +} + +/* Create a declaration for field NAME of a given TYPE, setting the flags + for whether the field is ARTIFICIAL and/or IGNORED. */ + +tree +create_field_decl (tree type, const char *name, int artificial, int ignored) +{ + tree decl = build_decl (input_location, FIELD_DECL, + name ? get_identifier (name) : NULL_TREE, type); + DECL_ARTIFICIAL (decl) = artificial; + DECL_IGNORED_P (decl) = ignored; + + return decl; +} + +/* Return the COMDAT group into which DECL should be placed. */ + +static tree +d_comdat_group (tree decl) +{ + /* If already part of a comdat group, use that. */ + if (DECL_COMDAT_GROUP (decl)) + return DECL_COMDAT_GROUP (decl); + + return DECL_ASSEMBLER_NAME (decl); +} + +/* Set DECL up to have the closest approximation of "initialized common" + linkage available. */ + +void +d_comdat_linkage (tree decl) +{ + if (flag_weak) + make_decl_one_only (decl, d_comdat_group (decl)); + else if (TREE_CODE (decl) == FUNCTION_DECL + || (VAR_P (decl) && DECL_ARTIFICIAL (decl))) + /* We can just emit function and compiler-generated variables statically; + having multiple copies is (for the most part) only a waste of space. */ + TREE_PUBLIC (decl) = 0; + else if (DECL_INITIAL (decl) == NULL_TREE + || DECL_INITIAL (decl) == error_mark_node) + /* Fallback, cannot have multiple copies. */ + DECL_COMMON (decl) = 1; + + if (TREE_PUBLIC (decl)) + DECL_COMDAT (decl) = 1; +} + +/* Set DECL up to have the closest approximation of "linkonce" linkage. */ + +void +d_linkonce_linkage (tree decl) +{ + /* Weak definitions have to be public. */ + if (!TREE_PUBLIC (decl)) + return; + + /* Necessary to allow DECL_ONE_ONLY or DECL_WEAK functions to be inlined. */ + if (TREE_CODE (decl) == FUNCTION_DECL) + DECL_DECLARED_INLINE_P (decl) = 1; + + /* No weak support, fallback to COMDAT linkage. */ + if (!flag_weak) + return d_comdat_linkage (decl); + + make_decl_one_only (decl, d_comdat_group (decl)); +} |