/* decl.cc -- Lower D frontend declarations to GCC trees. Copyright (C) 2006-2019 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #include "config.h" #include "system.h" #include "coretypes.h" #include "dmd/aggregate.h" #include "dmd/attrib.h" #include "dmd/cond.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; /* If we're lowering the body of a version(unittest) condition. */ bool in_version_unittest_; public: DeclVisitor (void) { this->in_version_unittest_ = false; } /* 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) { if (d->semanticRun >= PASSobj) return; /* 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); } d->semanticRun = PASSobj; } /* 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); } /* Conditional compilation is the process of selecting which code to compile and which code to not compile. Look for version conditions that may */ void visit (ConditionalDeclaration *d) { bool old_condition = this->in_version_unittest_; if (global.params.useUnitTests) { VersionCondition *vc = d->condition->isVersionCondition (); if (vc && vc->ident == Identifier::idPool ("unittest")) this->in_version_unittest_ = true; } visit ((AttribDeclaration *) d); this->in_version_unittest_ = old_condition; } /* 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); } } /* Templates are D's approach to generic programming. They have no members that can be emitted, however if the template is nested and used as a voldemort type, then it's members must be compiled before the parent function finishes. */ void visit (TemplateDeclaration *d) { /* Type cannot be directly named outside of the scope it's declared in, so the only way it can be escaped is if the function has auto return. */ FuncDeclaration *fd = d_function_chain ? d_function_chain->function : NULL; if (!fd || !fd->isAuto ()) return; /* Check if the function returns an instantiated type that may contain nested members. Only applies to classes or structs. */ Type *tb = fd->type->nextOf ()->baseElemOf (); while (tb->ty == Tarray || tb->ty == Tpointer) tb = tb->nextOf ()->baseElemOf (); TemplateInstance *ti = NULL; if (tb->ty == Tstruct) ti = ((TypeStruct *) tb)->sym->isInstantiated (); else if (tb->ty == Tclass) ti = ((TypeClass *) tb)->sym->isInstantiated (); /* Return type is instantiated from this template declaration, walk over all members of the instance. */ if (ti && ti->tempdecl == d) ti->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->semanticRun >= PASSobj) return; 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. */ if (have_typeinfo_p (Type::dtypeinfo)) 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); d->semanticRun = PASSobj; } /* 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 % 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->semanticRun >= PASSobj) return; 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. */ if (have_typeinfo_p (Type::dtypeinfo)) 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 *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)); d->semanticRun = PASSobj; } /* Write out compiler generated TypeInfo and vtables for the given interface declaration, walking over all static members. */ void visit (InterfaceDeclaration *d) { if (d->semanticRun >= PASSobj) return; 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. */ if (have_typeinfo_p (Type::dtypeinfo)) { 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)); d->semanticRun = PASSobj; } /* 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. */ if (have_typeinfo_p (Type::dtypeinfo)) 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->semanticRun >= PASSobj) return; 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; } if (!d->canTakeAddressOf ()) { /* Do not store variables we cannot take the address of, but keep the values for purposes of debugging. */ if (!d->type->isscalar ()) { tree decl = get_symbol_decl (d); 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)"); } } } d->semanticRun = PASSobj; } /* Generate and compile a static TypeInfo declaration, but only if it is needed in the current compilation. */ void visit (TypeInfoDeclaration *d) { if (d->semanticRun >= PASSobj) return; if (speculative_type_p (d->tinfo)) return; tree t = get_typeinfo_decl (d); DECL_INITIAL (t) = layout_typeinfo (d); d_finish_decl (t); d->semanticRun = PASSobj; } /* 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; DECL_IN_UNITTEST_CONDITION_P (fndecl) = this->in_version_unittest_; 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 ("", 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; /* 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 (vd->storage_class & STCmanifest) { /* Cannot make an expression out of a void initializer. */ if (vd->_init && !vd->_init->isVoidInitializer ()) { Expression *ie = initializerToExpression (vd->_init); if (!vd->type->isscalar ()) DECL_INITIAL (decl->csym) = build_expr (ie, false); else DECL_INITIAL (decl->csym) = build_expr (ie, true); } } } /* 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. When the function is outside the current compilation unit however, then the thunk must be kept private to not conflict. */ TREE_PUBLIC (thunk) = TREE_PUBLIC (function) && !DECL_EXTERNAL (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 (); 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 *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)); /* If a templated type, use the template instance name, as that includes all template parameters. */ const char *name = dsym->parent->isTemplateInstance () ? ((TemplateInstance *) dsym->parent)->toChars () : dsym->ident->toChars (); tree decl = build_decl (make_location_t (dsym->loc), TYPE_DECL, get_identifier (name), 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)); }