| /* d-lang.cc -- Language-dependent hooks for D. |
| 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 |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| |
| #include "dmd/aggregate.h" |
| #include "dmd/cond.h" |
| #include "dmd/declaration.h" |
| #include "dmd/doc.h" |
| #include "dmd/errors.h" |
| #include "dmd/expression.h" |
| #include "dmd/hdrgen.h" |
| #include "dmd/identifier.h" |
| #include "dmd/json.h" |
| #include "dmd/mangle.h" |
| #include "dmd/mars.h" |
| #include "dmd/module.h" |
| #include "dmd/mtype.h" |
| #include "dmd/target.h" |
| |
| #include "opts.h" |
| #include "alias.h" |
| #include "tree.h" |
| #include "diagnostic.h" |
| #include "fold-const.h" |
| #include "toplev.h" |
| #include "langhooks.h" |
| #include "langhooks-def.h" |
| #include "target.h" |
| #include "stringpool.h" |
| #include "stor-layout.h" |
| #include "varasm.h" |
| #include "output.h" |
| #include "print-tree.h" |
| #include "gimple-expr.h" |
| #include "gimplify.h" |
| #include "debug.h" |
| |
| #include "d-tree.h" |
| #include "id.h" |
| |
| |
| /* Array of D frontend type/decl nodes. */ |
| tree d_global_trees[DTI_MAX]; |
| |
| /* True if compilation is currently inside the D frontend semantic passes. */ |
| bool doing_semantic_analysis_p = false; |
| |
| /* Options handled by the compiler that are separate from the frontend. */ |
| struct d_option_data |
| { |
| const char *fonly; /* -fonly=<arg> */ |
| const char *multilib; /* -imultilib <dir> */ |
| const char *prefix; /* -iprefix <dir> */ |
| |
| bool deps; /* -M */ |
| bool deps_skip_system; /* -MM */ |
| const char *deps_filename; /* -M[M]D */ |
| const char *deps_filename_user; /* -MF <arg> */ |
| OutBuffer *deps_target; /* -M[QT] <arg> */ |
| bool deps_phony; /* -MP */ |
| |
| bool stdinc; /* -nostdinc */ |
| } |
| d_option; |
| |
| /* List of modules being compiled. */ |
| static Modules builtin_modules; |
| |
| /* Module where `C main' is defined, compiled in if needed. */ |
| static Module *entrypoint_module = NULL; |
| static Module *entrypoint_root_module = NULL; |
| |
| /* The current and global binding level in effect. */ |
| struct binding_level *current_binding_level; |
| struct binding_level *global_binding_level; |
| |
| /* The context to be used for global declarations. */ |
| static GTY(()) tree global_context; |
| |
| /* Array of all global declarations to pass back to the middle-end. */ |
| static GTY(()) vec<tree, va_gc> *global_declarations; |
| |
| /* Support for GCC-style command-line make dependency generation. |
| Adds TARGET to the make dependencies target buffer. |
| QUOTED is true if the string should be quoted. */ |
| |
| static void |
| deps_add_target (const char *target, bool quoted) |
| { |
| if (!d_option.deps_target) |
| d_option.deps_target = new OutBuffer (); |
| else |
| d_option.deps_target->writeByte (' '); |
| |
| d_option.deps_target->reserve (strlen (target)); |
| |
| if (!quoted) |
| { |
| d_option.deps_target->writestring (target); |
| return; |
| } |
| |
| /* Quote characters in target which are significant to Make. */ |
| for (const char *p = target; *p != '\0'; p++) |
| { |
| switch (*p) |
| { |
| case ' ': |
| case '\t': |
| for (const char *q = p - 1; target <= q && *q == '\\'; q--) |
| d_option.deps_target->writeByte ('\\'); |
| d_option.deps_target->writeByte ('\\'); |
| break; |
| |
| case '$': |
| d_option.deps_target->writeByte ('$'); |
| break; |
| |
| case '#': |
| d_option.deps_target->writeByte ('\\'); |
| break; |
| |
| default: |
| break; |
| } |
| |
| d_option.deps_target->writeByte (*p); |
| } |
| } |
| |
| /* Write out all dependencies of a given MODULE to the specified BUFFER. |
| COLMAX is the number of columns to word-wrap at (0 means don't wrap). */ |
| |
| static void |
| deps_write (Module *module, OutBuffer *buffer, unsigned colmax = 72) |
| { |
| hash_set <const char *> dependencies; |
| |
| Modules modlist; |
| modlist.push (module); |
| |
| Modules phonylist; |
| |
| const char *str; |
| unsigned size; |
| unsigned column = 0; |
| |
| /* Write out make target module name. */ |
| if (d_option.deps_target) |
| { |
| size = d_option.deps_target->offset; |
| str = d_option.deps_target->extractString (); |
| } |
| else |
| { |
| str = module->objfile->name->str; |
| size = strlen (str); |
| } |
| |
| buffer->writestring (str); |
| column = size; |
| buffer->writestring (":"); |
| column++; |
| |
| /* Write out all make dependencies. */ |
| while (modlist.dim > 0) |
| { |
| Module *depmod = modlist.pop (); |
| |
| str = depmod->srcfile->name->str; |
| size = strlen (str); |
| |
| /* Skip dependencies that have already been written. */ |
| if (dependencies.add (str)) |
| continue; |
| |
| column += size; |
| |
| if (colmax && column > colmax) |
| { |
| buffer->writestring (" \\\n "); |
| column = size + 1; |
| } |
| else |
| { |
| buffer->writestring (" "); |
| column++; |
| } |
| |
| buffer->writestring (str); |
| |
| /* Add to list of phony targets if is not being compile. */ |
| if (d_option.deps_phony && !depmod->isRoot ()) |
| phonylist.push (depmod); |
| |
| /* Search all imports of the written dependency. */ |
| for (size_t i = 0; i < depmod->aimports.dim; i++) |
| { |
| Module *m = depmod->aimports[i]; |
| |
| /* Ignore compiler-generated modules. */ |
| if ((m->ident == Identifier::idPool ("__entrypoint") |
| || m->ident == Identifier::idPool ("__main")) |
| && m->parent == NULL) |
| continue; |
| |
| /* Don't search system installed modules, this includes |
| object, core.*, std.*, and gcc.* packages. */ |
| if (d_option.deps_skip_system) |
| { |
| if (m->ident == Identifier::idPool ("object") |
| && m->parent == NULL) |
| continue; |
| |
| if (m->md && m->md->packages) |
| { |
| Identifier *package = (*m->md->packages)[0]; |
| |
| if (package == Identifier::idPool ("core") |
| || package == Identifier::idPool ("std") |
| || package == Identifier::idPool ("gcc")) |
| continue; |
| } |
| } |
| |
| modlist.push (m); |
| } |
| } |
| |
| buffer->writenl (); |
| |
| /* Write out all phony targets. */ |
| for (size_t i = 0; i < phonylist.dim; i++) |
| { |
| Module *m = phonylist[i]; |
| |
| buffer->writenl (); |
| buffer->writestring (m->srcfile->name->str); |
| buffer->writestring (":\n"); |
| } |
| } |
| |
| /* Implements the lang_hooks.init_options routine for language D. |
| This initializes the global state for the D frontend before calling |
| the option handlers. */ |
| |
| static void |
| d_init_options (unsigned int, cl_decoded_option *decoded_options) |
| { |
| /* Set default values. */ |
| global._init (); |
| |
| global.vendor = lang_hooks.name; |
| global.params.argv0 = xstrdup (decoded_options[0].arg); |
| global.params.link = true; |
| global.params.useAssert = true; |
| global.params.useInvariants = true; |
| global.params.useIn = true; |
| global.params.useOut = true; |
| global.params.useArrayBounds = BOUNDSCHECKdefault; |
| global.params.useSwitchError = true; |
| global.params.useModuleInfo = true; |
| global.params.useTypeInfo = true; |
| global.params.useExceptions = true; |
| global.params.useInline = false; |
| global.params.obj = true; |
| global.params.hdrStripPlainFunctions = true; |
| global.params.betterC = false; |
| global.params.allInst = false; |
| |
| /* Default extern(C++) mangling to C++14. */ |
| global.params.cplusplus = CppStdRevisionCpp14; |
| |
| global.params.linkswitches = new Strings (); |
| global.params.libfiles = new Strings (); |
| global.params.objfiles = new Strings (); |
| global.params.ddocfiles = new Strings (); |
| |
| /* Warnings and deprecations are disabled by default. */ |
| global.params.useDeprecated = DIAGNOSTICoff; |
| global.params.warnings = DIAGNOSTICoff; |
| |
| global.params.imppath = new Strings (); |
| global.params.fileImppath = new Strings (); |
| global.params.modFileAliasStrings = new Strings (); |
| |
| /* Extra GDC-specific options. */ |
| d_option.fonly = NULL; |
| d_option.multilib = NULL; |
| d_option.prefix = NULL; |
| d_option.deps = false; |
| d_option.deps_skip_system = false; |
| d_option.deps_filename = NULL; |
| d_option.deps_filename_user = NULL; |
| d_option.deps_target = NULL; |
| d_option.deps_phony = false; |
| d_option.stdinc = true; |
| } |
| |
| /* Implements the lang_hooks.init_options_struct routine for language D. |
| Initializes the options structure OPTS. */ |
| |
| static void |
| d_init_options_struct (gcc_options *opts) |
| { |
| /* GCC options. */ |
| opts->x_flag_exceptions = 1; |
| |
| /* Avoid range issues for complex multiply and divide. */ |
| opts->x_flag_complex_method = 2; |
| |
| /* Unlike C, there is no global 'errno' variable. */ |
| opts->x_flag_errno_math = 0; |
| opts->frontend_set_flag_errno_math = true; |
| |
| /* Keep in sync with existing -fbounds-check flag. */ |
| opts->x_flag_bounds_check = global.params.useArrayBounds; |
| |
| /* D says that signed overflow is precisely defined. */ |
| opts->x_flag_wrapv = 1; |
| } |
| |
| /* Implements the lang_hooks.lang_mask routine for language D. |
| Returns language mask for option parsing. */ |
| |
| static unsigned int |
| d_option_lang_mask (void) |
| { |
| return CL_D; |
| } |
| |
| /* Implements the lang_hooks.init routine for language D. */ |
| |
| static bool |
| d_init (void) |
| { |
| Type::_init (); |
| Id::initialize (); |
| Module::_init (); |
| Expression::_init (); |
| Objc::_init (); |
| |
| /* Back-end init. */ |
| global_binding_level = ggc_cleared_alloc<binding_level> (); |
| current_binding_level = global_binding_level; |
| |
| /* This allows the code in d-builtins.cc to not have to worry about |
| converting (C signed char *) to (D char *) for string arguments of |
| built-in functions. The parameter (signed_char = false) specifies |
| whether char is signed. */ |
| build_common_tree_nodes (false); |
| |
| d_init_builtins (); |
| |
| if (flag_exceptions) |
| using_eh_for_cleanups (); |
| |
| if (!supports_one_only ()) |
| flag_weak = 0; |
| |
| /* This is the C main, not the D main. */ |
| main_identifier_node = get_identifier ("main"); |
| |
| Target::_init (); |
| d_init_versions (); |
| |
| /* Insert all library-configured identifiers and import paths. */ |
| add_import_paths (d_option.prefix, d_option.multilib, d_option.stdinc); |
| |
| return 1; |
| } |
| |
| /* Implements the lang_hooks.init_ts routine for language D. */ |
| |
| static void |
| d_init_ts (void) |
| { |
| MARK_TS_TYPED (FLOAT_MOD_EXPR); |
| MARK_TS_TYPED (UNSIGNED_RSHIFT_EXPR); |
| } |
| |
| /* Implements the lang_hooks.handle_option routine for language D. |
| Handles D specific options. Return false if we didn't do anything. */ |
| |
| static bool |
| d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value, |
| int kind ATTRIBUTE_UNUSED, |
| location_t loc ATTRIBUTE_UNUSED, |
| const cl_option_handlers *handlers ATTRIBUTE_UNUSED) |
| { |
| opt_code code = (opt_code) scode; |
| bool result = true; |
| |
| switch (code) |
| { |
| case OPT_fall_instantiations: |
| global.params.allInst = value; |
| break; |
| |
| case OPT_fassert: |
| global.params.useAssert = value; |
| break; |
| |
| case OPT_fbounds_check: |
| global.params.useArrayBounds = value |
| ? BOUNDSCHECKon : BOUNDSCHECKoff; |
| break; |
| |
| case OPT_fbounds_check_: |
| global.params.useArrayBounds = (value == 2) ? BOUNDSCHECKon |
| : (value == 1) ? BOUNDSCHECKsafeonly : BOUNDSCHECKoff; |
| break; |
| |
| case OPT_fdebug: |
| global.params.debuglevel = value ? 1 : 0; |
| break; |
| |
| case OPT_fdebug_: |
| if (ISDIGIT (arg[0])) |
| { |
| int level = integral_argument (arg); |
| if (level != -1) |
| { |
| DebugCondition::setGlobalLevel (level); |
| break; |
| } |
| } |
| |
| if (Identifier::isValidIdentifier (CONST_CAST (char *, arg))) |
| { |
| DebugCondition::addGlobalIdent (arg); |
| break; |
| } |
| |
| error ("bad argument for -fdebug %qs", arg); |
| break; |
| |
| case OPT_fdoc: |
| global.params.doDocComments = value; |
| break; |
| |
| case OPT_fdoc_dir_: |
| global.params.doDocComments = true; |
| global.params.docdir = arg; |
| break; |
| |
| case OPT_fdoc_file_: |
| global.params.doDocComments = true; |
| global.params.docname = arg; |
| break; |
| |
| case OPT_fdoc_inc_: |
| global.params.ddocfiles->push (arg); |
| break; |
| |
| case OPT_fdruntime: |
| global.params.betterC = !value; |
| break; |
| |
| case OPT_fdump_d_original: |
| global.params.vcg_ast = value; |
| break; |
| |
| case OPT_fexceptions: |
| global.params.useExceptions = value; |
| break; |
| |
| case OPT_fignore_unknown_pragmas: |
| global.params.ignoreUnsupportedPragmas = value; |
| break; |
| |
| case OPT_finvariants: |
| global.params.useInvariants = value; |
| break; |
| |
| case OPT_fmain: |
| global.params.addMain = value; |
| break; |
| |
| case OPT_fmodule_file_: |
| global.params.modFileAliasStrings->push (arg); |
| if (!strchr (arg, '=')) |
| error ("bad argument for -fmodule-file %qs", arg); |
| break; |
| |
| case OPT_fmoduleinfo: |
| global.params.useModuleInfo = value; |
| break; |
| |
| case OPT_fonly_: |
| d_option.fonly = arg; |
| break; |
| |
| case OPT_fpostconditions: |
| global.params.useOut = value; |
| break; |
| |
| case OPT_fpreconditions: |
| global.params.useIn = value; |
| break; |
| |
| case OPT_frelease: |
| global.params.release = value; |
| break; |
| |
| case OPT_frtti: |
| global.params.useTypeInfo = value; |
| break; |
| |
| case OPT_fswitch_errors: |
| global.params.useSwitchError = value; |
| break; |
| |
| case OPT_ftransition_all: |
| global.params.vtls = value; |
| global.params.vfield = value; |
| global.params.vcomplex = value; |
| break; |
| |
| case OPT_ftransition_checkimports: |
| global.params.check10378 = value; |
| break; |
| |
| case OPT_ftransition_complex: |
| global.params.vcomplex = value; |
| break; |
| |
| case OPT_ftransition_dip1000: |
| global.params.vsafe = value; |
| global.params.useDIP25 = value; |
| break; |
| |
| case OPT_ftransition_dip25: |
| global.params.useDIP25 = value; |
| break; |
| |
| case OPT_ftransition_field: |
| global.params.vfield = value; |
| break; |
| |
| case OPT_ftransition_import: |
| global.params.bug10378 = value; |
| break; |
| |
| case OPT_ftransition_nogc: |
| global.params.vgc = value; |
| break; |
| |
| case OPT_ftransition_tls: |
| global.params.vtls = value; |
| break; |
| |
| case OPT_funittest: |
| global.params.useUnitTests = value; |
| break; |
| |
| case OPT_fversion_: |
| if (ISDIGIT (arg[0])) |
| { |
| int level = integral_argument (arg); |
| if (level != -1) |
| { |
| VersionCondition::setGlobalLevel (level); |
| break; |
| } |
| } |
| |
| if (Identifier::isValidIdentifier (CONST_CAST (char *, arg))) |
| { |
| VersionCondition::addGlobalIdent (arg); |
| break; |
| } |
| |
| error ("bad argument for -fversion %qs", arg); |
| break; |
| |
| case OPT_H: |
| global.params.doHdrGeneration = true; |
| break; |
| |
| case OPT_Hd: |
| global.params.doHdrGeneration = true; |
| global.params.hdrdir = arg; |
| break; |
| |
| case OPT_Hf: |
| global.params.doHdrGeneration = true; |
| global.params.hdrname = arg; |
| break; |
| |
| case OPT_imultilib: |
| d_option.multilib = arg; |
| break; |
| |
| case OPT_iprefix: |
| d_option.prefix = arg; |
| break; |
| |
| case OPT_I: |
| global.params.imppath->push (arg); |
| break; |
| |
| case OPT_J: |
| global.params.fileImppath->push (arg); |
| break; |
| |
| case OPT_MM: |
| d_option.deps_skip_system = true; |
| /* Fall through. */ |
| |
| case OPT_M: |
| d_option.deps = true; |
| break; |
| |
| case OPT_MMD: |
| d_option.deps_skip_system = true; |
| /* Fall through. */ |
| |
| case OPT_MD: |
| d_option.deps = true; |
| d_option.deps_filename = arg; |
| break; |
| |
| case OPT_MF: |
| /* If specified multiple times, last one wins. */ |
| d_option.deps_filename_user = arg; |
| break; |
| |
| case OPT_MP: |
| d_option.deps_phony = true; |
| break; |
| |
| case OPT_MQ: |
| deps_add_target (arg, true); |
| break; |
| |
| case OPT_MT: |
| deps_add_target (arg, false); |
| break; |
| |
| case OPT_nostdinc: |
| d_option.stdinc = false; |
| break; |
| |
| case OPT_v: |
| global.params.verbose = value; |
| break; |
| |
| case OPT_Wall: |
| if (value) |
| global.params.warnings = DIAGNOSTICinform; |
| break; |
| |
| case OPT_Wdeprecated: |
| global.params.useDeprecated = value ? DIAGNOSTICinform : DIAGNOSTICoff; |
| break; |
| |
| case OPT_Werror: |
| if (value) |
| global.params.warnings = DIAGNOSTICerror; |
| break; |
| |
| case OPT_Wspeculative: |
| if (value) |
| global.params.showGaggedErrors = 1; |
| break; |
| |
| case OPT_Xf: |
| global.params.jsonfilename = arg; |
| /* Fall through. */ |
| |
| case OPT_X: |
| global.params.doJsonGeneration = true; |
| break; |
| |
| default: |
| break; |
| } |
| |
| D_handle_option_auto (&global_options, &global_options_set, |
| scode, arg, value, |
| d_option_lang_mask (), kind, |
| loc, handlers, global_dc); |
| |
| return result; |
| } |
| |
| /* Implements the lang_hooks.post_options routine for language D. |
| Deal with any options that imply the turning on/off of features. |
| FN is the main input filename passed on the command line. */ |
| |
| static bool |
| d_post_options (const char ** fn) |
| { |
| /* Verify the input file name. */ |
| const char *filename = *fn; |
| if (!filename || strcmp (filename, "-") == 0) |
| filename = ""; |
| |
| /* The front end considers the first input file to be the main one. */ |
| *fn = filename; |
| |
| /* Release mode doesn't turn off bounds checking for safe functions. */ |
| if (global.params.useArrayBounds == BOUNDSCHECKdefault) |
| { |
| global.params.useArrayBounds = global.params.release |
| ? BOUNDSCHECKsafeonly : BOUNDSCHECKon; |
| flag_bounds_check = !global.params.release; |
| } |
| |
| if (global.params.release) |
| { |
| if (!global_options_set.x_flag_invariants) |
| global.params.useInvariants = false; |
| |
| if (!global_options_set.x_flag_preconditions) |
| global.params.useIn = false; |
| |
| if (!global_options_set.x_flag_postconditions) |
| global.params.useOut = false; |
| |
| if (!global_options_set.x_flag_assert) |
| global.params.useAssert = false; |
| |
| if (!global_options_set.x_flag_switch_errors) |
| global.params.useSwitchError = false; |
| } |
| |
| if (global.params.betterC) |
| { |
| if (!global_options_set.x_flag_moduleinfo) |
| global.params.useModuleInfo = false; |
| |
| if (!global_options_set.x_flag_rtti) |
| global.params.useTypeInfo = false; |
| |
| if (!global_options_set.x_flag_exceptions) |
| global.params.useExceptions = false; |
| |
| global.params.checkAction = CHECKACTION_halt; |
| } |
| |
| /* Turn off partitioning unless it was explicitly requested, as it doesn't |
| work with D exception chaining, where EH handler uses LSDA to determine |
| whether two thrown exception are in the same context. */ |
| if (!global_options_set.x_flag_reorder_blocks_and_partition) |
| global_options.x_flag_reorder_blocks_and_partition = 0; |
| |
| /* Error about use of deprecated features. */ |
| if (global.params.useDeprecated == DIAGNOSTICinform |
| && global.params.warnings == DIAGNOSTICerror) |
| global.params.useDeprecated = DIAGNOSTICerror; |
| |
| /* Make -fmax-errors visible to frontend's diagnostic machinery. */ |
| if (global_options_set.x_flag_max_errors) |
| global.errorLimit = flag_max_errors; |
| |
| if (flag_excess_precision_cmdline == EXCESS_PRECISION_DEFAULT) |
| flag_excess_precision_cmdline = EXCESS_PRECISION_STANDARD; |
| |
| if (global.params.useUnitTests) |
| global.params.useAssert = true; |
| |
| global.params.symdebug = write_symbols != NO_DEBUG; |
| global.params.useInline = flag_inline_functions; |
| global.params.showColumns = flag_show_column; |
| |
| if (global.params.useInline) |
| global.params.hdrStripPlainFunctions = false; |
| |
| global.params.obj = !flag_syntax_only; |
| |
| /* Has no effect yet. */ |
| global.params.pic = flag_pic != 0; |
| |
| if (warn_return_type == -1) |
| warn_return_type = 0; |
| |
| return false; |
| } |
| |
| /* Return TRUE if an operand OP of a given TYPE being copied has no data. |
| The middle-end does a similar check with zero sized types. */ |
| |
| static bool |
| empty_modify_p (tree type, tree op) |
| { |
| tree_code code = TREE_CODE (op); |
| switch (code) |
| { |
| case COMPOUND_EXPR: |
| return empty_modify_p (type, TREE_OPERAND (op, 1)); |
| |
| case CONSTRUCTOR: |
| /* Non-empty construcors are valid. */ |
| if (CONSTRUCTOR_NELTS (op) != 0 || TREE_CLOBBER_P (op)) |
| return false; |
| break; |
| |
| case CALL_EXPR: |
| /* Leave nrvo alone because it isn't a copy. */ |
| if (CALL_EXPR_RETURN_SLOT_OPT (op)) |
| return false; |
| break; |
| |
| default: |
| /* If the operand doesn't have a simple form. */ |
| if (!is_gimple_lvalue (op) && !INDIRECT_REF_P (op)) |
| return false; |
| break; |
| } |
| |
| return empty_aggregate_p (type); |
| } |
| |
| /* Implements the lang_hooks.gimplify_expr routine for language D. |
| Do gimplification of D specific expression trees in EXPR_P. */ |
| |
| int |
| d_gimplify_expr (tree *expr_p, gimple_seq *pre_p, |
| gimple_seq *post_p ATTRIBUTE_UNUSED) |
| { |
| tree_code code = TREE_CODE (*expr_p); |
| enum gimplify_status ret = GS_UNHANDLED; |
| tree op0, op1; |
| tree type; |
| |
| switch (code) |
| { |
| case INIT_EXPR: |
| case MODIFY_EXPR: |
| op0 = TREE_OPERAND (*expr_p, 0); |
| op1 = TREE_OPERAND (*expr_p, 1); |
| |
| if (!error_operand_p (op0) && !error_operand_p (op1) |
| && (AGGREGATE_TYPE_P (TREE_TYPE (op0)) |
| || AGGREGATE_TYPE_P (TREE_TYPE (op1))) |
| && !useless_type_conversion_p (TREE_TYPE (op1), TREE_TYPE (op0))) |
| { |
| /* If the back end isn't clever enough to know that the lhs and rhs |
| types are the same, add an explicit conversion. */ |
| TREE_OPERAND (*expr_p, 1) = build1 (VIEW_CONVERT_EXPR, |
| TREE_TYPE (op0), op1); |
| ret = GS_OK; |
| } |
| else if (empty_modify_p (TREE_TYPE (op0), op1)) |
| { |
| /* Remove any copies of empty aggregates. */ |
| gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p, |
| is_gimple_lvalue, fb_lvalue); |
| |
| if (TREE_SIDE_EFFECTS (op1)) |
| gimplify_and_add (op1, pre_p); |
| |
| *expr_p = TREE_OPERAND (*expr_p, 0); |
| ret = GS_OK; |
| } |
| break; |
| |
| case ADDR_EXPR: |
| op0 = TREE_OPERAND (*expr_p, 0); |
| /* Constructors are not lvalues, so make them one. */ |
| if (TREE_CODE (op0) == CONSTRUCTOR) |
| { |
| TREE_OPERAND (*expr_p, 0) = force_target_expr (op0); |
| ret = GS_OK; |
| } |
| break; |
| |
| case CALL_EXPR: |
| if (CALL_EXPR_ARGS_ORDERED (*expr_p)) |
| { |
| /* Strictly evaluate all arguments from left to right. */ |
| int nargs = call_expr_nargs (*expr_p); |
| location_t loc = EXPR_LOC_OR_LOC (*expr_p, input_location); |
| |
| /* No need to enforce evaluation order if only one argument. */ |
| if (nargs < 2) |
| break; |
| |
| /* Or if all arguments are already free of side-effects. */ |
| bool has_side_effects = false; |
| for (int i = 0; i < nargs; i++) |
| { |
| if (TREE_SIDE_EFFECTS (CALL_EXPR_ARG (*expr_p, i))) |
| { |
| has_side_effects = true; |
| break; |
| } |
| } |
| |
| if (!has_side_effects) |
| break; |
| |
| /* Leave the last argument for gimplify_call_expr. */ |
| for (int i = 0; i < nargs - 1; i++) |
| { |
| tree new_arg = CALL_EXPR_ARG (*expr_p, i); |
| |
| /* If argument has a side-effect, gimplify_arg will handle it. */ |
| if (gimplify_arg (&new_arg, pre_p, loc) == GS_ERROR) |
| ret = GS_ERROR; |
| |
| /* Even if an argument itself doesn't have any side-effects, it |
| might be altered by another argument in the list. */ |
| if (new_arg == CALL_EXPR_ARG (*expr_p, i) |
| && !really_constant_p (new_arg)) |
| new_arg = get_formal_tmp_var (new_arg, pre_p); |
| |
| CALL_EXPR_ARG (*expr_p, i) = new_arg; |
| } |
| |
| if (ret != GS_ERROR) |
| ret = GS_OK; |
| } |
| break; |
| |
| case UNSIGNED_RSHIFT_EXPR: |
| /* Convert op0 to an unsigned type. */ |
| op0 = TREE_OPERAND (*expr_p, 0); |
| op1 = TREE_OPERAND (*expr_p, 1); |
| |
| type = d_unsigned_type (TREE_TYPE (op0)); |
| |
| *expr_p = convert (TREE_TYPE (*expr_p), |
| build2 (RSHIFT_EXPR, type, convert (type, op0), op1)); |
| ret = GS_OK; |
| break; |
| |
| case FLOAT_MOD_EXPR: |
| gcc_unreachable (); |
| |
| default: |
| break; |
| } |
| |
| return ret; |
| } |
| |
| /* Add the module M to the list of modules that may declare GCC builtins. |
| These are scanned after first semantic and before codegen passes. |
| See d_maybe_set_builtin() for the implementation. */ |
| |
| void |
| d_add_builtin_module (Module *m) |
| { |
| builtin_modules.push (m); |
| } |
| |
| /* Record the entrypoint module ENTRY which will be compiled in the current |
| compilation. ROOT is the module scope where this was requested from. */ |
| |
| void |
| d_add_entrypoint_module (Module *entry, Module *root) |
| { |
| /* We are emitting this straight to object file. */ |
| entrypoint_module = entry; |
| entrypoint_root_module = root; |
| } |
| |
| /* Implements the lang_hooks.parse_file routine for language D. */ |
| |
| void |
| d_parse_file (void) |
| { |
| if (global.params.verbose) |
| { |
| message ("binary %s", global.params.argv0); |
| message ("version %s", global.version); |
| |
| if (global.params.versionids) |
| { |
| OutBuffer buf; |
| buf.writestring ("predefs "); |
| for (size_t i = 0; i < global.params.versionids->dim; i++) |
| { |
| const char *s = (*global.params.versionids)[i]; |
| buf.writestring (" "); |
| buf.writestring (s); |
| } |
| |
| message ("%.*s", (int) buf.offset, (char *) buf.data); |
| } |
| } |
| |
| /* Start the main input file, if the debug writer wants it. */ |
| if (debug_hooks->start_end_main_source_file) |
| debug_hooks->start_source_file (0, main_input_filename); |
| |
| /* Create Module's for all sources we will load. */ |
| Modules modules; |
| modules.reserve (num_in_fnames); |
| |
| /* In this mode, the first file name is supposed to be a duplicate |
| of one of the input files. */ |
| if (d_option.fonly && strcmp (d_option.fonly, main_input_filename) != 0) |
| error ("-fonly= argument is different from first input file name"); |
| |
| for (size_t i = 0; i < num_in_fnames; i++) |
| { |
| if (strcmp (in_fnames[i], "-") == 0) |
| { |
| /* Handling stdin, generate a unique name for the module. */ |
| obstack buffer; |
| gcc_obstack_init (&buffer); |
| int c; |
| |
| Module *m = Module::create (in_fnames[i], |
| Identifier::generateId ("__stdin"), |
| global.params.doDocComments, |
| global.params.doHdrGeneration); |
| modules.push (m); |
| |
| /* Load the entire contents of stdin into memory. */ |
| while ((c = getc (stdin)) != EOF) |
| obstack_1grow (&buffer, c); |
| |
| if (!obstack_object_size (&buffer)) |
| obstack_1grow (&buffer, '\0'); |
| |
| /* Overwrite the source file for the module, the one created by |
| Module::create would have a forced a `.d' suffix. */ |
| m->srcfile = File::create ("<stdin>"); |
| m->srcfile->len = obstack_object_size (&buffer); |
| m->srcfile->buffer = (unsigned char *) obstack_finish (&buffer); |
| |
| /* Tell the front-end not to free the buffer after parsing. */ |
| m->srcfile->ref = 1; |
| } |
| else |
| { |
| /* Handling a D source file, strip off the path and extension. */ |
| const char *basename = FileName::name (in_fnames[i]); |
| const char *name = FileName::removeExt (basename); |
| |
| Module *m = Module::create (in_fnames[i], Identifier::idPool (name), |
| global.params.doDocComments, |
| global.params.doHdrGeneration); |
| modules.push (m); |
| FileName::free (name); |
| } |
| } |
| |
| /* Read all D source files. */ |
| for (size_t i = 0; i < modules.dim; i++) |
| { |
| Module *m = modules[i]; |
| m->read (Loc ()); |
| } |
| |
| /* Parse all D source files. */ |
| for (size_t i = 0; i < modules.dim; i++) |
| { |
| Module *m = modules[i]; |
| |
| if (global.params.verbose) |
| message ("parse %s", m->toChars ()); |
| |
| if (!Module::rootModule) |
| Module::rootModule = m; |
| |
| m->importedFrom = m; |
| m->parse (); |
| Compiler::loadModule (m); |
| |
| if (m->isDocFile) |
| { |
| gendocfile (m); |
| /* Remove M from list of modules. */ |
| modules.remove (i); |
| i--; |
| } |
| } |
| |
| /* Load the module containing D main. */ |
| if (global.params.addMain) |
| { |
| unsigned errors = global.startGagging (); |
| Module *m = Module::load (Loc (), NULL, Identifier::idPool ("__main")); |
| |
| if (! global.endGagging (errors)) |
| { |
| m->importedFrom = m; |
| modules.push (m); |
| } |
| } |
| |
| if (global.errors) |
| goto had_errors; |
| |
| if (global.params.doHdrGeneration) |
| { |
| /* Generate 'header' import files. Since 'header' import files must be |
| independent of command line switches and what else is imported, they |
| are generated before any semantic analysis. */ |
| for (size_t i = 0; i < modules.dim; i++) |
| { |
| Module *m = modules[i]; |
| if (d_option.fonly && m != Module::rootModule) |
| continue; |
| |
| if (global.params.verbose) |
| message ("import %s", m->toChars ()); |
| |
| genhdrfile (m); |
| } |
| } |
| |
| if (global.errors) |
| goto had_errors; |
| |
| /* Load all unconditional imports for better symbol resolving. */ |
| for (size_t i = 0; i < modules.dim; i++) |
| { |
| Module *m = modules[i]; |
| |
| if (global.params.verbose) |
| message ("importall %s", m->toChars ()); |
| |
| m->importAll (NULL); |
| } |
| |
| if (global.errors) |
| goto had_errors; |
| |
| /* Do semantic analysis. */ |
| doing_semantic_analysis_p = true; |
| |
| for (size_t i = 0; i < modules.dim; i++) |
| { |
| Module *m = modules[i]; |
| |
| if (global.params.verbose) |
| message ("semantic %s", m->toChars ()); |
| |
| m->semantic (NULL); |
| } |
| |
| /* Do deferred semantic analysis. */ |
| Module::dprogress = 1; |
| Module::runDeferredSemantic (); |
| |
| if (Module::deferred.dim) |
| { |
| for (size_t i = 0; i < Module::deferred.dim; i++) |
| { |
| Dsymbol *sd = Module::deferred[i]; |
| error_at (make_location_t (sd->loc), |
| "unable to resolve forward reference in definition"); |
| } |
| } |
| |
| /* Process all built-in modules or functions now for CTFE. */ |
| while (builtin_modules.dim != 0) |
| { |
| Module *m = builtin_modules.pop (); |
| d_maybe_set_builtin (m); |
| } |
| |
| /* Do pass 2 semantic analysis. */ |
| for (size_t i = 0; i < modules.dim; i++) |
| { |
| Module *m = modules[i]; |
| |
| if (global.params.verbose) |
| message ("semantic2 %s", m->toChars ()); |
| |
| m->semantic2 (NULL); |
| } |
| |
| Module::runDeferredSemantic2 (); |
| |
| if (global.errors) |
| goto had_errors; |
| |
| /* Do pass 3 semantic analysis. */ |
| for (size_t i = 0; i < modules.dim; i++) |
| { |
| Module *m = modules[i]; |
| |
| if (global.params.verbose) |
| message ("semantic3 %s", m->toChars ()); |
| |
| m->semantic3 (NULL); |
| } |
| |
| Module::runDeferredSemantic3 (); |
| |
| /* Check again, incase semantic3 pass loaded any more modules. */ |
| while (builtin_modules.dim != 0) |
| { |
| Module *m = builtin_modules.pop (); |
| d_maybe_set_builtin (m); |
| } |
| |
| /* Do not attempt to generate output files if errors or warnings occurred. */ |
| if (global.errors || global.warnings) |
| goto had_errors; |
| |
| /* Generate output files. */ |
| doing_semantic_analysis_p = false; |
| |
| if (Module::rootModule) |
| { |
| /* Declare the name of the root module as the first global name in order |
| to make the middle-end fully deterministic. */ |
| OutBuffer buf; |
| mangleToBuffer (Module::rootModule, &buf); |
| first_global_object_name = buf.extractString (); |
| } |
| |
| /* Make dependencies. */ |
| if (d_option.deps) |
| { |
| OutBuffer buf; |
| |
| for (size_t i = 0; i < modules.dim; i++) |
| deps_write (modules[i], &buf); |
| |
| /* -MF <arg> overrides -M[M]D. */ |
| if (d_option.deps_filename_user) |
| d_option.deps_filename = d_option.deps_filename_user; |
| |
| if (d_option.deps_filename) |
| { |
| File *fdeps = File::create (d_option.deps_filename); |
| fdeps->setbuffer ((void *) buf.data, buf.offset); |
| fdeps->ref = 1; |
| writeFile (Loc (), fdeps); |
| } |
| else |
| message ("%.*s", (int) buf.offset, (char *) buf.data); |
| } |
| |
| /* Generate JSON files. */ |
| if (global.params.doJsonGeneration) |
| { |
| OutBuffer buf; |
| json_generate (&buf, &modules); |
| |
| const char *name = global.params.jsonfilename; |
| |
| if (name && (name[0] != '-' || name[1] != '\0')) |
| { |
| const char *nameext = FileName::defaultExt (name, global.json_ext); |
| File *fjson = File::create (nameext); |
| fjson->setbuffer ((void *) buf.data, buf.offset); |
| fjson->ref = 1; |
| writeFile (Loc (), fjson); |
| } |
| else |
| message ("%.*s", (int) buf.offset, (char *) buf.data); |
| } |
| |
| /* Generate Ddoc files. */ |
| if (global.params.doDocComments && !global.errors && !errorcount) |
| { |
| for (size_t i = 0; i < modules.dim; i++) |
| { |
| Module *m = modules[i]; |
| gendocfile (m); |
| } |
| } |
| |
| /* Handle -fdump-d-original. */ |
| if (global.params.vcg_ast) |
| { |
| for (size_t i = 0; i < modules.dim; i++) |
| { |
| Module *m = modules[i]; |
| OutBuffer buf; |
| buf.doindent = 1; |
| |
| moduleToBuffer (&buf, m); |
| message ("%.*s", (int) buf.offset, (char *) buf.data); |
| } |
| } |
| |
| for (size_t i = 0; i < modules.dim; i++) |
| { |
| Module *m = modules[i]; |
| if (d_option.fonly && m != Module::rootModule) |
| continue; |
| |
| if (global.params.verbose) |
| message ("code %s", m->toChars ()); |
| |
| if (!flag_syntax_only) |
| { |
| if ((entrypoint_module != NULL) && (m == entrypoint_root_module)) |
| build_decl_tree (entrypoint_module); |
| |
| build_decl_tree (m); |
| } |
| } |
| |
| /* And end the main input file, if the debug writer wants it. */ |
| if (debug_hooks->start_end_main_source_file) |
| debug_hooks->end_source_file (0); |
| |
| had_errors: |
| /* Add the D frontend error count to the GCC error count to correctly |
| exit with an error status. */ |
| errorcount += (global.errors + global.warnings); |
| |
| /* Write out globals. */ |
| d_finish_compilation (vec_safe_address (global_declarations), |
| vec_safe_length (global_declarations)); |
| } |
| |
| /* Implements the lang_hooks.types.type_for_mode routine for language D. */ |
| |
| static tree |
| d_type_for_mode (machine_mode mode, int unsignedp) |
| { |
| if (mode == QImode) |
| return unsignedp ? d_ubyte_type : d_byte_type; |
| |
| if (mode == HImode) |
| return unsignedp ? d_ushort_type : d_short_type; |
| |
| if (mode == SImode) |
| return unsignedp ? d_uint_type : d_int_type; |
| |
| if (mode == DImode) |
| return unsignedp ? d_ulong_type : d_long_type; |
| |
| if (mode == TYPE_MODE (d_cent_type)) |
| return unsignedp ? d_ucent_type : d_cent_type; |
| |
| if (mode == TYPE_MODE (float_type_node)) |
| return float_type_node; |
| |
| if (mode == TYPE_MODE (double_type_node)) |
| return double_type_node; |
| |
| if (mode == TYPE_MODE (long_double_type_node)) |
| return long_double_type_node; |
| |
| if (mode == TYPE_MODE (build_pointer_type (char8_type_node))) |
| return build_pointer_type (char8_type_node); |
| |
| if (mode == TYPE_MODE (build_pointer_type (d_int_type))) |
| return build_pointer_type (d_int_type); |
| |
| if (COMPLEX_MODE_P (mode)) |
| { |
| machine_mode inner_mode; |
| tree inner_type; |
| |
| if (mode == TYPE_MODE (complex_float_type_node)) |
| return complex_float_type_node; |
| if (mode == TYPE_MODE (complex_double_type_node)) |
| return complex_double_type_node; |
| if (mode == TYPE_MODE (complex_long_double_type_node)) |
| return complex_long_double_type_node; |
| |
| inner_mode = (machine_mode) GET_MODE_INNER (mode); |
| inner_type = d_type_for_mode (inner_mode, unsignedp); |
| if (inner_type != NULL_TREE) |
| return build_complex_type (inner_type); |
| } |
| else if (VECTOR_MODE_P (mode)) |
| { |
| machine_mode inner_mode = (machine_mode) GET_MODE_INNER (mode); |
| tree inner_type = d_type_for_mode (inner_mode, unsignedp); |
| if (inner_type != NULL_TREE) |
| return build_vector_type_for_mode (inner_type, mode); |
| } |
| |
| return 0; |
| } |
| |
| /* Implements the lang_hooks.types.type_for_size routine for language D. */ |
| |
| static tree |
| d_type_for_size (unsigned bits, int unsignedp) |
| { |
| if (bits <= TYPE_PRECISION (d_byte_type)) |
| return unsignedp ? d_ubyte_type : d_byte_type; |
| |
| if (bits <= TYPE_PRECISION (d_short_type)) |
| return unsignedp ? d_ushort_type : d_short_type; |
| |
| if (bits <= TYPE_PRECISION (d_int_type)) |
| return unsignedp ? d_uint_type : d_int_type; |
| |
| if (bits <= TYPE_PRECISION (d_long_type)) |
| return unsignedp ? d_ulong_type : d_long_type; |
| |
| if (bits <= TYPE_PRECISION (d_cent_type)) |
| return unsignedp ? d_ucent_type : d_cent_type; |
| |
| return 0; |
| } |
| |
| /* Return the signed or unsigned version of TYPE, an integral type, the |
| signedness being specified by UNSIGNEDP. */ |
| |
| static tree |
| d_signed_or_unsigned_type (int unsignedp, tree type) |
| { |
| if (TYPE_UNSIGNED (type) == (unsigned) unsignedp) |
| return type; |
| |
| if (TYPE_PRECISION (type) == TYPE_PRECISION (d_cent_type)) |
| return unsignedp ? d_ucent_type : d_cent_type; |
| |
| if (TYPE_PRECISION (type) == TYPE_PRECISION (d_long_type)) |
| return unsignedp ? d_ulong_type : d_long_type; |
| |
| if (TYPE_PRECISION (type) == TYPE_PRECISION (d_int_type)) |
| return unsignedp ? d_uint_type : d_int_type; |
| |
| if (TYPE_PRECISION (type) == TYPE_PRECISION (d_short_type)) |
| return unsignedp ? d_ushort_type : d_short_type; |
| |
| if (TYPE_PRECISION (type) == TYPE_PRECISION (d_byte_type)) |
| return unsignedp ? d_ubyte_type : d_byte_type; |
| |
| return signed_or_unsigned_type_for (unsignedp, type); |
| } |
| |
| /* Return the unsigned version of TYPE, an integral type. */ |
| |
| tree |
| d_unsigned_type (tree type) |
| { |
| return d_signed_or_unsigned_type (1, type); |
| } |
| |
| /* Return the signed version of TYPE, an integral type. */ |
| |
| tree |
| d_signed_type (tree type) |
| { |
| return d_signed_or_unsigned_type (0, type); |
| } |
| |
| /* Implements the lang_hooks.types.type_promotes_to routine for language D. |
| All promotions for variable arguments are handled by the D frontend. */ |
| |
| static tree |
| d_type_promotes_to (tree type) |
| { |
| return type; |
| } |
| |
| /* Implements the lang_hooks.decls.global_bindings_p routine for language D. |
| Return true if we are in the global binding level. */ |
| |
| static bool |
| d_global_bindings_p (void) |
| { |
| return (current_binding_level == global_binding_level); |
| } |
| |
| /* Return global_context, but create it first if need be. */ |
| |
| static tree |
| get_global_context (void) |
| { |
| if (!global_context) |
| { |
| global_context = build_translation_unit_decl (NULL_TREE); |
| debug_hooks->register_main_translation_unit (global_context); |
| } |
| |
| return global_context; |
| } |
| |
| /* Implements the lang_hooks.decls.pushdecl routine for language D. |
| Record DECL as belonging to the current lexical scope. */ |
| |
| tree |
| d_pushdecl (tree decl) |
| { |
| /* Set the context of the decl. If current_function_decl did not help in |
| determining the context, use global scope. */ |
| if (!DECL_CONTEXT (decl)) |
| { |
| if (current_function_decl) |
| DECL_CONTEXT (decl) = current_function_decl; |
| else |
| DECL_CONTEXT (decl) = get_global_context (); |
| } |
| |
| /* Put decls on list in reverse order. */ |
| if (TREE_STATIC (decl) || d_global_bindings_p ()) |
| vec_safe_push (global_declarations, decl); |
| else |
| { |
| TREE_CHAIN (decl) = current_binding_level->names; |
| current_binding_level->names = decl; |
| } |
| |
| return decl; |
| } |
| |
| /* Implements the lang_hooks.decls.getdecls routine for language D. |
| Return the list of declarations of the current level. */ |
| |
| static tree |
| d_getdecls (void) |
| { |
| if (current_binding_level) |
| return current_binding_level->names; |
| |
| return NULL_TREE; |
| } |
| |
| |
| /* Implements the lang_hooks.get_alias_set routine for language D. |
| Get the alias set corresponding to type or expression T. |
| Return -1 if we don't do anything special. */ |
| |
| static alias_set_type |
| d_get_alias_set (tree) |
| { |
| /* For now in D, assume everything aliases everything else, until we define |
| some solid rules backed by a specification. There are also some parts |
| of code generation routines that don't adhere to C alias rules, such as |
| build_vconvert. In any case, a lot of user code already assumes there |
| is no strict aliasing and will break if we were to change that. */ |
| return 0; |
| } |
| |
| /* Implements the lang_hooks.types_compatible_p routine for language D. |
| Compares two types for equivalence in the D programming language. |
| This routine should only return 1 if it is sure, even though the frontend |
| should have already ensured that all types are compatible before handing |
| over the parsed ASTs to the code generator. */ |
| |
| static int |
| d_types_compatible_p (tree x, tree y) |
| { |
| Type *tx = TYPE_LANG_FRONTEND (x); |
| Type *ty = TYPE_LANG_FRONTEND (y); |
| |
| /* Try validating the types in the frontend. */ |
| if (tx != NULL && ty != NULL) |
| { |
| /* Types are equivalent. */ |
| if (same_type_p (tx, ty)) |
| return true; |
| |
| /* Type system allows implicit conversion between. */ |
| if (tx->implicitConvTo (ty) || ty->implicitConvTo (tx)) |
| return true; |
| } |
| |
| /* Fallback on using type flags for comparison. E.g: all dynamic arrays |
| are distinct types in D, but are VIEW_CONVERT compatible. */ |
| if (TREE_CODE (x) == RECORD_TYPE && TREE_CODE (y) == RECORD_TYPE) |
| { |
| if (TYPE_DYNAMIC_ARRAY (x) && TYPE_DYNAMIC_ARRAY (y)) |
| return true; |
| |
| if (TYPE_DELEGATE (x) && TYPE_DELEGATE (y)) |
| return true; |
| |
| if (TYPE_ASSOCIATIVE_ARRAY (x) && TYPE_ASSOCIATIVE_ARRAY (y)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* Implements the lang_hooks.finish_incomplete_decl routine for language D. */ |
| |
| static void |
| d_finish_incomplete_decl (tree decl) |
| { |
| if (VAR_P (decl)) |
| { |
| /* D allows zero-length declarations. Such a declaration ends up with |
| DECL_SIZE (t) == NULL_TREE which is what the back-end function |
| assembler_variable checks. This could change in later versions, or |
| maybe all of these variables should be aliased to one symbol. */ |
| if (DECL_SIZE (decl) == 0) |
| { |
| DECL_SIZE (decl) = bitsize_zero_node; |
| DECL_SIZE_UNIT (decl) = size_zero_node; |
| } |
| } |
| } |
| |
| /* Implements the lang_hooks.types.classify_record routine for language D. |
| Return the true debug type for TYPE. */ |
| |
| static classify_record |
| d_classify_record (tree type) |
| { |
| Type *t = TYPE_LANG_FRONTEND (type); |
| |
| if (t && t->ty == Tclass) |
| { |
| TypeClass *tc = (TypeClass *) t; |
| |
| /* extern(C++) interfaces get emitted as classes. */ |
| if (tc->sym->isInterfaceDeclaration () |
| && !tc->sym->isCPPinterface ()) |
| return RECORD_IS_INTERFACE; |
| |
| return RECORD_IS_CLASS; |
| } |
| |
| return RECORD_IS_STRUCT; |
| } |
| |
| /* Implements the lang_hooks.tree_size routine for language D. |
| Determine the size of our tcc_constant or tcc_exceptional nodes. */ |
| |
| static size_t |
| d_tree_size (tree_code code) |
| { |
| switch (code) |
| { |
| case FUNCFRAME_INFO: |
| return sizeof (tree_frame_info); |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Implements the lang_hooks.print_xnode routine for language D. */ |
| |
| static void |
| d_print_xnode (FILE *file, tree node, int indent) |
| { |
| switch (TREE_CODE (node)) |
| { |
| case FUNCFRAME_INFO: |
| print_node (file, "frame_type", FRAMEINFO_TYPE (node), indent + 4); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| /* Return which tree structure is used by NODE, or TS_D_GENERIC if NODE |
| is one of the language-independent trees. */ |
| |
| d_tree_node_structure_enum |
| d_tree_node_structure (lang_tree_node *t) |
| { |
| switch (TREE_CODE (&t->generic)) |
| { |
| case IDENTIFIER_NODE: |
| return TS_D_IDENTIFIER; |
| |
| case FUNCFRAME_INFO: |
| return TS_D_FRAMEINFO; |
| |
| default: |
| return TS_D_GENERIC; |
| } |
| } |
| |
| /* Allocate and return a lang specific structure for the frontend type. */ |
| |
| struct lang_type * |
| build_lang_type (Type *t) |
| { |
| struct lang_type *lt = ggc_cleared_alloc<struct lang_type> (); |
| lt->type = t; |
| return lt; |
| } |
| |
| /* Allocate and return a lang specific structure for the frontend decl. */ |
| |
| struct lang_decl * |
| build_lang_decl (Declaration *d) |
| { |
| /* For compiler generated run-time typeinfo, a lang_decl is allocated even if |
| there's no associated frontend symbol to refer to (yet). If the symbol |
| appears later in the compilation, then the slot will be re-used. */ |
| if (d == NULL) |
| return ggc_cleared_alloc<struct lang_decl> (); |
| |
| struct lang_decl *ld = (d->csym) ? DECL_LANG_SPECIFIC (d->csym) : NULL; |
| if (ld == NULL) |
| ld = ggc_cleared_alloc<struct lang_decl> (); |
| |
| if (ld->decl == NULL) |
| ld->decl = d; |
| |
| return ld; |
| } |
| |
| /* Implements the lang_hooks.dup_lang_specific_decl routine for language D. |
| Replace the DECL_LANG_SPECIFIC field of NODE with a copy. */ |
| |
| static void |
| d_dup_lang_specific_decl (tree node) |
| { |
| if (! DECL_LANG_SPECIFIC (node)) |
| return; |
| |
| struct lang_decl *ld = ggc_alloc<struct lang_decl> (); |
| memcpy (ld, DECL_LANG_SPECIFIC (node), sizeof (struct lang_decl)); |
| DECL_LANG_SPECIFIC (node) = ld; |
| } |
| |
| /* This preserves trees we create from the garbage collector. */ |
| |
| static GTY(()) tree d_keep_list = NULL_TREE; |
| |
| void |
| d_keep (tree t) |
| { |
| d_keep_list = tree_cons (NULL_TREE, t, d_keep_list); |
| } |
| |
| /* Implements the lang_hooks.eh_personality routine for language D. |
| Return the GDC personality function decl. */ |
| |
| static GTY(()) tree d_eh_personality_decl; |
| |
| static tree |
| d_eh_personality (void) |
| { |
| if (!d_eh_personality_decl) |
| d_eh_personality_decl = build_personality_function ("gdc"); |
| |
| return d_eh_personality_decl; |
| } |
| |
| /* Implements the lang_hooks.eh_runtime_type routine for language D. */ |
| |
| static tree |
| d_build_eh_runtime_type (tree type) |
| { |
| Type *t = TYPE_LANG_FRONTEND (type); |
| |
| if (t != NULL) |
| t = t->toBasetype (); |
| |
| gcc_assert (t != NULL && t->ty == Tclass); |
| ClassDeclaration *cd = ((TypeClass *) t)->sym; |
| tree decl; |
| |
| if (cd->isCPPclass ()) |
| decl = get_cpp_typeinfo_decl (cd); |
| else |
| decl = get_classinfo_decl (cd); |
| |
| return convert (ptr_type_node, build_address (decl)); |
| } |
| |
| /* Definitions for our language-specific hooks. */ |
| |
| #undef LANG_HOOKS_NAME |
| #undef LANG_HOOKS_INIT |
| #undef LANG_HOOKS_INIT_TS |
| #undef LANG_HOOKS_INIT_OPTIONS |
| #undef LANG_HOOKS_INIT_OPTIONS_STRUCT |
| #undef LANG_HOOKS_OPTION_LANG_MASK |
| #undef LANG_HOOKS_HANDLE_OPTION |
| #undef LANG_HOOKS_POST_OPTIONS |
| #undef LANG_HOOKS_PARSE_FILE |
| #undef LANG_HOOKS_COMMON_ATTRIBUTE_TABLE |
| #undef LANG_HOOKS_ATTRIBUTE_TABLE |
| #undef LANG_HOOKS_GET_ALIAS_SET |
| #undef LANG_HOOKS_TYPES_COMPATIBLE_P |
| #undef LANG_HOOKS_BUILTIN_FUNCTION |
| #undef LANG_HOOKS_BUILTIN_FUNCTION_EXT_SCOPE |
| #undef LANG_HOOKS_REGISTER_BUILTIN_TYPE |
| #undef LANG_HOOKS_FINISH_INCOMPLETE_DECL |
| #undef LANG_HOOKS_GIMPLIFY_EXPR |
| #undef LANG_HOOKS_CLASSIFY_RECORD |
| #undef LANG_HOOKS_TREE_SIZE |
| #undef LANG_HOOKS_PRINT_XNODE |
| #undef LANG_HOOKS_DUP_LANG_SPECIFIC_DECL |
| #undef LANG_HOOKS_EH_PERSONALITY |
| #undef LANG_HOOKS_EH_RUNTIME_TYPE |
| #undef LANG_HOOKS_PUSHDECL |
| #undef LANG_HOOKS_GETDECLS |
| #undef LANG_HOOKS_GLOBAL_BINDINGS_P |
| #undef LANG_HOOKS_TYPE_FOR_MODE |
| #undef LANG_HOOKS_TYPE_FOR_SIZE |
| #undef LANG_HOOKS_TYPE_PROMOTES_TO |
| |
| #define LANG_HOOKS_NAME "GNU D" |
| #define LANG_HOOKS_INIT d_init |
| #define LANG_HOOKS_INIT_TS d_init_ts |
| #define LANG_HOOKS_INIT_OPTIONS d_init_options |
| #define LANG_HOOKS_INIT_OPTIONS_STRUCT d_init_options_struct |
| #define LANG_HOOKS_OPTION_LANG_MASK d_option_lang_mask |
| #define LANG_HOOKS_HANDLE_OPTION d_handle_option |
| #define LANG_HOOKS_POST_OPTIONS d_post_options |
| #define LANG_HOOKS_PARSE_FILE d_parse_file |
| #define LANG_HOOKS_COMMON_ATTRIBUTE_TABLE d_langhook_common_attribute_table |
| #define LANG_HOOKS_ATTRIBUTE_TABLE d_langhook_attribute_table |
| #define LANG_HOOKS_GET_ALIAS_SET d_get_alias_set |
| #define LANG_HOOKS_TYPES_COMPATIBLE_P d_types_compatible_p |
| #define LANG_HOOKS_BUILTIN_FUNCTION d_builtin_function |
| #define LANG_HOOKS_BUILTIN_FUNCTION_EXT_SCOPE d_builtin_function_ext_scope |
| #define LANG_HOOKS_REGISTER_BUILTIN_TYPE d_register_builtin_type |
| #define LANG_HOOKS_FINISH_INCOMPLETE_DECL d_finish_incomplete_decl |
| #define LANG_HOOKS_GIMPLIFY_EXPR d_gimplify_expr |
| #define LANG_HOOKS_CLASSIFY_RECORD d_classify_record |
| #define LANG_HOOKS_TREE_SIZE d_tree_size |
| #define LANG_HOOKS_PRINT_XNODE d_print_xnode |
| #define LANG_HOOKS_DUP_LANG_SPECIFIC_DECL d_dup_lang_specific_decl |
| #define LANG_HOOKS_EH_PERSONALITY d_eh_personality |
| #define LANG_HOOKS_EH_RUNTIME_TYPE d_build_eh_runtime_type |
| #define LANG_HOOKS_PUSHDECL d_pushdecl |
| #define LANG_HOOKS_GETDECLS d_getdecls |
| #define LANG_HOOKS_GLOBAL_BINDINGS_P d_global_bindings_p |
| #define LANG_HOOKS_TYPE_FOR_MODE d_type_for_mode |
| #define LANG_HOOKS_TYPE_FOR_SIZE d_type_for_size |
| #define LANG_HOOKS_TYPE_PROMOTES_TO d_type_promotes_to |
| |
| struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER; |
| |
| #include "gt-d-d-lang.h" |
| #include "gtype-d.h" |