aboutsummaryrefslogtreecommitdiff
path: root/gcc/d/toir.cc
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gcc.gnu.org>2018-10-28 19:51:47 +0000
committerIain Buclaw <ibuclaw@gcc.gnu.org>2018-10-28 19:51:47 +0000
commitb4c522fabd0df7be08882d2207df8b2765026110 (patch)
treeb5ffc312b0a441c1ba24323152aec463fdbe5e9f /gcc/d/toir.cc
parent01ce9e31a02c8039d88e90f983735104417bf034 (diff)
downloadgcc-b4c522fabd0df7be08882d2207df8b2765026110.zip
gcc-b4c522fabd0df7be08882d2207df8b2765026110.tar.gz
gcc-b4c522fabd0df7be08882d2207df8b2765026110.tar.bz2
Add D front-end, libphobos library, and D2 testsuite.
ChangeLog: * Makefile.def (target_modules): Add libphobos. (flags_to_pass): Add GDC, GDCFLAGS, GDC_FOR_TARGET and GDCFLAGS_FOR_TARGET. (dependencies): Make libphobos depend on libatomic, libbacktrace configure, and zlib configure. (language): Add language d. * Makefile.in: Rebuild. * Makefile.tpl (BUILD_EXPORTS): Add GDC and GDCFLAGS. (HOST_EXPORTS): Add GDC. (POSTSTAGE1_HOST_EXPORTS): Add GDC and GDC_FOR_BUILD. (BASE_TARGET_EXPORTS): Add GDC. (GDC_FOR_BUILD, GDC, GDCFLAGS): New variables. (GDC_FOR_TARGET, GDC_FLAGS_FOR_TARGET): New variables. (EXTRA_HOST_FLAGS): Add GDC. (STAGE1_FLAGS_TO_PASS): Add GDC. (EXTRA_TARGET_FLAGS): Add GDC and GDCFLAGS. * config-ml.in: Treat GDC and GDCFLAGS like other compiler/flag environment variables. * configure: Rebuild. * configure.ac: Add target-libphobos to target_libraries. Set and substitute GDC_FOR_BUILD and GDC_FOR_TARGET. config/ChangeLog: * multi.m4: Set GDC. gcc/ChangeLog: * Makefile.in (tm_d_file_list, tm_d_include_list): New variables. (TM_D_H, D_TARGET_DEF, D_TARGET_H, D_TARGET_OBJS): New variables. (tm_d.h, cs-tm_d.h, default-d.o): New rules. (d/d-target-hooks-def.h, s-d-target-hooks-def-h): New rules. (s-tm-texi): Also check timestamp on d-target.def. (generated_files): Add TM_D_H and d-target-hooks-def.h. (build/genhooks.o): Also depend on D_TARGET_DEF. * config.gcc (tm_d_file, d_target_objs, target_has_targetdm): New variables. * config/aarch64/aarch64-d.c: New file. * config/aarch64/aarch64-linux.h (GNU_USER_TARGET_D_CRITSEC_SIZE): Define. * config/aarch64/aarch64-protos.h (aarch64_d_target_versions): New prototype. * config/aarch64/aarch64.h (TARGET_D_CPU_VERSIONS): Define. * config/aarch64/t-aarch64 (aarch64-d.o): New rule. * config/arm/arm-d.c: New file. * config/arm/arm-protos.h (arm_d_target_versions): New prototype. * config/arm/arm.h (TARGET_D_CPU_VERSIONS): Define. * config/arm/linux-eabi.h (EXTRA_TARGET_D_OS_VERSIONS): Define. * config/arm/t-arm (arm-d.o): New rule. * config/default-d.c: New file. * config/glibc-d.c: New file. * config/gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/i386/i386-d.c: New file. * config/i386/i386-protos.h (ix86_d_target_versions): New prototype. * config/i386/i386.h (TARGET_D_CPU_VERSIONS): Define. * config/i386/linux-common.h (EXTRA_TARGET_D_OS_VERSIONS): Define. (GNU_USER_TARGET_D_CRITSEC_SIZE): Define. * config/i386/t-i386 (i386-d.o): New rule. * config/kfreebsd-gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/kopensolaris-gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/linux-android.h (ANDROID_TARGET_D_OS_VERSIONS): Define. * config/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/mips/linux-common.h (EXTRA_TARGET_D_OS_VERSIONS): Define. * config/mips/mips-d.c: New file. * config/mips/mips-protos.h (mips_d_target_versions): New prototype. * config/mips/mips.h (TARGET_D_CPU_VERSIONS): Define. * config/mips/t-mips (mips-d.o): New rule. * config/powerpcspe/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/powerpcspe/linux64.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/powerpcspe/powerpcspe-d.c: New file. * config/powerpcspe/powerpcspe-protos.h (rs6000_d_target_versions): New prototype. * config/powerpcspe/powerpcspe.c (rs6000_output_function_epilogue): Support GNU D by using 0 as the language type. * config/powerpcspe/powerpcspe.h (TARGET_D_CPU_VERSIONS): Define. * config/powerpcspe/t-powerpcspe (powerpcspe-d.o): New rule. * config/riscv/riscv-d.c: New file. * config/riscv/riscv-protos.h (riscv_d_target_versions): New prototype. * config/riscv/riscv.h (TARGET_D_CPU_VERSIONS): Define. * config/riscv/t-riscv (riscv-d.o): New rule. * config/rs6000/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/rs6000/linux64.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/rs6000/rs6000-d.c: New file. * config/rs6000/rs6000-protos.h (rs6000_d_target_versions): New prototype. * config/rs6000/rs6000.c (rs6000_output_function_epilogue): Support GNU D by using 0 as the language type. * config/rs6000/rs6000.h (TARGET_D_CPU_VERSIONS): Define. * config/rs6000/t-rs6000 (rs6000-d.o): New rule. * config/s390/s390-d.c: New file. * config/s390/s390-protos.h (s390_d_target_versions): New prototype. * config/s390/s390.h (TARGET_D_CPU_VERSIONS): Define. * config/s390/t-s390 (s390-d.o): New rule. * config/sparc/sparc-d.c: New file. * config/sparc/sparc-protos.h (sparc_d_target_versions): New prototype. * config/sparc/sparc.h (TARGET_D_CPU_VERSIONS): Define. * config/sparc/t-sparc (sparc-d.o): New rule. * config/t-glibc (glibc-d.o): New rule. * configure: Regenerated. * configure.ac (tm_d_file): New variable. (tm_d_file_list, tm_d_include_list, d_target_objs): Add substitutes. * doc/contrib.texi (Contributors): Add self for the D frontend. * doc/frontends.texi (G++ and GCC): Mention D as a supported language. * doc/install.texi (Configuration): Mention libphobos as an option for --enable-shared. Mention d as an option for --enable-languages. (Testing): Mention check-d as a target. * doc/invoke.texi (Overall Options): Mention .d, .dd, and .di as file name suffixes. Mention d as a -x option. * doc/sourcebuild.texi (Top Level): Mention libphobos. * doc/standards.texi (Standards): Add section on D language. * doc/tm.texi: Regenerated. * doc/tm.texi.in: Add @node for D language and ABI, and @hook for TARGET_CPU_VERSIONS, TARGET_D_OS_VERSIONS, and TARGET_D_CRITSEC_SIZE. * dwarf2out.c (is_dlang): New function. (gen_compile_unit_die): Use DW_LANG_D for D. (declare_in_namespace): Return module die for D, instead of adding extra declarations into the namespace. (gen_namespace_die): Generate DW_TAG_module for D. (gen_decl_die): Handle CONST_DECLSs for D. (dwarf2out_decl): Likewise. (prune_unused_types_walk_local_classes): Handle DW_tag_interface_type. (prune_unused_types_walk): Handle DW_tag_interface_type same as other kinds of aggregates. * gcc.c (default_compilers): Add entries for .d, .dd and .di. * genhooks.c: Include d/d-target.def. gcc/po/ChangeLog: * EXCLUDES: Add sources from d/dmd. gcc/testsuite/ChangeLog: * gcc.misc-tests/help.exp: Add D to option descriptions check. * gdc.dg/asan/asan.exp: New file. * gdc.dg/asan/gdc272.d: New test. * gdc.dg/compilable.d: New test. * gdc.dg/dg.exp: New file. * gdc.dg/gdc254.d: New test. * gdc.dg/gdc260.d: New test. * gdc.dg/gdc270a.d: New test. * gdc.dg/gdc270b.d: New test. * gdc.dg/gdc282.d: New test. * gdc.dg/gdc283.d: New test. * gdc.dg/imports/gdc170.d: New test. * gdc.dg/imports/gdc231.d: New test. * gdc.dg/imports/gdc239.d: New test. * gdc.dg/imports/gdc241a.d: New test. * gdc.dg/imports/gdc241b.d: New test. * gdc.dg/imports/gdc251a.d: New test. * gdc.dg/imports/gdc251b.d: New test. * gdc.dg/imports/gdc253.d: New test. * gdc.dg/imports/gdc254a.d: New test. * gdc.dg/imports/gdc256.d: New test. * gdc.dg/imports/gdc27.d: New test. * gdc.dg/imports/gdcpkg256/package.d: New test. * gdc.dg/imports/runnable.d: New test. * gdc.dg/link.d: New test. * gdc.dg/lto/lto.exp: New file. * gdc.dg/lto/ltotests_0.d: New test. * gdc.dg/lto/ltotests_1.d: New test. * gdc.dg/runnable.d: New test. * gdc.dg/simd.d: New test. * gdc.test/gdc-test.exp: New file. * lib/gdc-dg.exp: New file. * lib/gdc.exp: New file. libphobos/ChangeLog: * Makefile.am: New file. * Makefile.in: New file. * acinclude.m4: New file. * aclocal.m4: New file. * config.h.in: New file. * configure: New file. * configure.ac: New file. * d_rules.am: New file. * libdruntime/Makefile.am: New file. * libdruntime/Makefile.in: New file. * libdruntime/__entrypoint.di: New file. * libdruntime/__main.di: New file. * libdruntime/gcc/attribute.d: New file. * libdruntime/gcc/backtrace.d: New file. * libdruntime/gcc/builtins.d: New file. * libdruntime/gcc/config.d.in: New file. * libdruntime/gcc/deh.d: New file. * libdruntime/gcc/libbacktrace.d.in: New file. * libdruntime/gcc/unwind/arm.d: New file. * libdruntime/gcc/unwind/arm_common.d: New file. * libdruntime/gcc/unwind/c6x.d: New file. * libdruntime/gcc/unwind/generic.d: New file. * libdruntime/gcc/unwind/package.d: New file. * libdruntime/gcc/unwind/pe.d: New file. * m4/autoconf.m4: New file. * m4/druntime.m4: New file. * m4/druntime/cpu.m4: New file. * m4/druntime/libraries.m4: New file. * m4/druntime/os.m4: New file. * m4/gcc_support.m4: New file. * m4/gdc.m4: New file. * m4/libtool.m4: New file. * src/Makefile.am: New file. * src/Makefile.in: New file. * src/libgphobos.spec.in: New file. * testsuite/Makefile.am: New file. * testsuite/Makefile.in: New file. * testsuite/config/default.exp: New file. * testsuite/lib/libphobos-dg.exp: New file. * testsuite/lib/libphobos.exp: New file. * testsuite/testsuite_flags.in: New file. From-SVN: r265573
Diffstat (limited to 'gcc/d/toir.cc')
-rw-r--r--gcc/d/toir.cc1447
1 files changed, 1447 insertions, 0 deletions
diff --git a/gcc/d/toir.cc b/gcc/d/toir.cc
new file mode 100644
index 0000000..0d78fa6
--- /dev/null
+++ b/gcc/d/toir.cc
@@ -0,0 +1,1447 @@
+/* toir.cc -- Lower D frontend statements to GCC trees.
+ Copyright (C) 2006-2018 Free Software Foundation, Inc.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "dmd/aggregate.h"
+#include "dmd/declaration.h"
+#include "dmd/expression.h"
+#include "dmd/identifier.h"
+#include "dmd/init.h"
+#include "dmd/statement.h"
+
+#include "tree.h"
+#include "tree-iterator.h"
+#include "options.h"
+#include "stmt.h"
+#include "fold-const.h"
+#include "diagnostic.h"
+#include "stringpool.h"
+#include "function.h"
+#include "toplev.h"
+
+#include "d-tree.h"
+
+
+/* Update data for defined and undefined labels when leaving a scope. */
+
+bool
+pop_binding_label (Statement * const &, d_label_entry *ent, binding_level *bl)
+{
+ binding_level *obl = bl->level_chain;
+
+ if (ent->level == bl)
+ {
+ if (bl->kind == level_try)
+ ent->in_try_scope = true;
+ else if (bl->kind == level_catch)
+ ent->in_catch_scope = true;
+
+ ent->level = obl;
+ }
+ else if (ent->fwdrefs)
+ {
+ for (d_label_use_entry *ref = ent->fwdrefs; ref; ref = ref->next)
+ ref->level = obl;
+ }
+
+ return true;
+}
+
+/* At the end of a function, all labels declared within the function
+ go out of scope. BLOCK is the top-level block for the function. */
+
+bool
+pop_label (Statement * const &s, d_label_entry *ent, tree block)
+{
+ if (!ent->bc_label)
+ {
+ /* Put the labels into the "variables" of the top-level block,
+ so debugger can see them. */
+ if (DECL_NAME (ent->label))
+ {
+ gcc_assert (DECL_INITIAL (ent->label) != NULL_TREE);
+ DECL_CHAIN (ent->label) = BLOCK_VARS (block);
+ BLOCK_VARS (block) = ent->label;
+ }
+ }
+
+ d_function_chain->labels->remove (s);
+
+ return true;
+}
+
+/* The D front-end does not use the 'binding level' system for a symbol table,
+ however it has been the goto structure for tracking code flow.
+ Primarily it is only needed to get debugging information for local variables
+ and otherwise support the back-end. */
+
+void
+push_binding_level (level_kind kind)
+{
+ /* Add it to the front of currently active scopes stack. */
+ binding_level *new_level = ggc_cleared_alloc<binding_level> ();
+ new_level->level_chain = current_binding_level;
+ new_level->kind = kind;
+
+ current_binding_level = new_level;
+}
+
+tree
+pop_binding_level (void)
+{
+ binding_level *level = current_binding_level;
+ current_binding_level = level->level_chain;
+
+ tree block = make_node (BLOCK);
+ BLOCK_VARS (block) = level->names;
+ BLOCK_SUBBLOCKS (block) = level->blocks;
+
+ /* In each subblock, record that this is its superior. */
+ for (tree t = level->blocks; t; t = BLOCK_CHAIN (t))
+ BLOCK_SUPERCONTEXT (t) = block;
+
+ if (level->kind == level_function)
+ {
+ /* Dispose of the block that we just made inside some higher level. */
+ DECL_INITIAL (current_function_decl) = block;
+ BLOCK_SUPERCONTEXT (block) = current_function_decl;
+
+ /* Pop all the labels declared in the function. */
+ if (d_function_chain->labels)
+ d_function_chain->labels->traverse<tree, &pop_label> (block);
+ }
+ else
+ {
+ /* Any uses of undefined labels, and any defined labels, now operate
+ under constraints of next binding contour. */
+ if (d_function_chain && d_function_chain->labels)
+ {
+ language_function *f = d_function_chain;
+ f->labels->traverse<binding_level *, &pop_binding_label> (level);
+ }
+
+ current_binding_level->blocks
+ = block_chainon (current_binding_level->blocks, block);
+ }
+
+ TREE_USED (block) = 1;
+ return block;
+}
+
+/* Create an empty statement tree rooted at T. */
+
+void
+push_stmt_list (void)
+{
+ tree t = alloc_stmt_list ();
+ vec_safe_push (d_function_chain->stmt_list, t);
+ d_keep (t);
+}
+
+/* Finish the statement tree rooted at T. */
+
+tree
+pop_stmt_list (void)
+{
+ tree t = d_function_chain->stmt_list->pop ();
+
+ /* If the statement list is completely empty, just return it. This is just
+ as good as build_empty_stmt, with the advantage that statement lists
+ are merged when they are appended to one another. So using the
+ STATEMENT_LIST avoids pathological buildup of EMPTY_STMT_P statements. */
+ if (TREE_SIDE_EFFECTS (t))
+ {
+ /* If the statement list contained exactly one statement, then extract
+ it immediately. */
+ tree_stmt_iterator i = tsi_start (t);
+
+ if (tsi_one_before_end_p (i))
+ {
+ tree u = tsi_stmt (i);
+ tsi_delink (&i);
+ free_stmt_list (t);
+ t = u;
+ }
+ }
+
+ return t;
+}
+
+/* T is an expression statement. Add it to the statement-tree. */
+
+void
+add_stmt (tree t)
+{
+ /* Ignore (void) 0; expression statements received from the frontend.
+ Likewise void_node is used when contracts become nops in release code. */
+ if (t == void_node || IS_EMPTY_STMT (t))
+ return;
+
+ /* At this point, we no longer care about the value of expressions,
+ so if there's no side-effects, then don't add it. */
+ if (!TREE_SIDE_EFFECTS (t))
+ return;
+
+ if (TREE_CODE (t) == COMPOUND_EXPR)
+ {
+ /* Push out each comma expressions as separate statements. */
+ add_stmt (TREE_OPERAND (t, 0));
+ add_stmt (TREE_OPERAND (t, 1));
+ }
+ else
+ {
+ /* Force the type to be void so we don't need to create a temporary
+ variable to hold the inner expression. */
+ if (TREE_CODE (t) == CLEANUP_POINT_EXPR)
+ TREE_TYPE (t) = void_type_node;
+
+ /* Append the expression to the statement list.
+ Make sure it has a proper location. */
+ if (EXPR_P (t) && !EXPR_HAS_LOCATION (t))
+ SET_EXPR_LOCATION (t, input_location);
+
+ tree stmt_list = d_function_chain->stmt_list->last ();
+ append_to_statement_list_force (t, &stmt_list);
+ }
+}
+
+/* Implements the visitor interface to build the GCC trees of all Statement
+ AST classes emitted from the D Front-end.
+ All visit methods accept one parameter S, which holds the frontend AST
+ of the statement to compile. They also don't return any value, instead
+ generated code are pushed to add_stmt(), which appends them to the
+ statement list in the current_binding_level. */
+
+class IRVisitor : public Visitor
+{
+ using Visitor::visit;
+
+ FuncDeclaration *func_;
+
+ /* Stack of labels which are targets for "break" and "continue",
+ linked through TREE_CHAIN. */
+ tree break_label_;
+ tree continue_label_;
+
+public:
+ IRVisitor (FuncDeclaration *fd)
+ {
+ this->func_ = fd;
+ this->break_label_ = NULL_TREE;
+ this->continue_label_ = NULL_TREE;
+ }
+
+ /* Helper for generating code for the statement AST class S.
+ Sets up the location of the statement before lowering. */
+
+ void build_stmt (Statement *s)
+ {
+ location_t saved_location = input_location;
+ input_location = make_location_t (s->loc);
+ s->accept (this);
+ input_location = saved_location;
+ }
+
+ /* Start a new scope for a KIND statement.
+ Each user-declared variable will have a binding contour that begins
+ where the variable is declared and ends at its containing scope. */
+
+ void start_scope (level_kind kind)
+ {
+ push_binding_level (kind);
+ push_stmt_list ();
+ }
+
+ /* Leave scope pushed by start_scope, returning a new bind_expr if
+ any variables where declared in the scope. */
+
+ tree end_scope (void)
+ {
+ tree block = pop_binding_level ();
+ tree body = pop_stmt_list ();
+
+ if (! BLOCK_VARS (block))
+ return body;
+
+ tree bind = build3 (BIND_EXPR, void_type_node,
+ BLOCK_VARS (block), body, block);
+ TREE_SIDE_EFFECTS (bind) = 1;
+ return bind;
+ }
+
+ /* Like end_scope, but also push it into the outer statement-tree. */
+
+ void finish_scope (void)
+ {
+ tree scope = this->end_scope ();
+ add_stmt (scope);
+ }
+
+ /* Return TRUE if IDENT is the current function return label. */
+
+ bool is_return_label (Identifier *ident)
+ {
+ if (this->func_->returnLabel)
+ return this->func_->returnLabel->ident == ident;
+
+ return false;
+ }
+
+ /* Define a label, specifying the location in the source file.
+ Return the LABEL_DECL node for the label. */
+
+ tree define_label (Statement *s, Identifier *ident = NULL)
+ {
+ tree label = this->lookup_label (s, ident);
+ gcc_assert (DECL_INITIAL (label) == NULL_TREE);
+
+ d_label_entry *ent = d_function_chain->labels->get (s);
+ gcc_assert (ent != NULL);
+
+ /* Mark label as having been defined. */
+ DECL_INITIAL (label) = error_mark_node;
+
+ ent->level = current_binding_level;
+
+ for (d_label_use_entry *ref = ent->fwdrefs; ref ; ref = ref->next)
+ this->check_previous_goto (ent->statement, ref);
+ ent->fwdrefs = NULL;
+
+ return label;
+ }
+
+ /* Emit a LABEL expression. */
+
+ void do_label (tree label)
+ {
+ /* Don't write out label unless it is marked as used by the frontend.
+ This makes auto-vectorization possible in conditional loops.
+ The only excemption to this is in the LabelStatement visitor,
+ in which all computed labels are marked regardless. */
+ if (TREE_USED (label))
+ add_stmt (build1 (LABEL_EXPR, void_type_node, label));
+ }
+
+ /* Emit a goto expression to LABEL. */
+
+ void do_jump (tree label)
+ {
+ add_stmt (fold_build1 (GOTO_EXPR, void_type_node, label));
+ TREE_USED (label) = 1;
+ }
+
+ /* Check that a new jump at statement scope FROM to a label declared in
+ statement scope TO is valid. */
+
+ void check_goto (Statement *from, Statement *to)
+ {
+ d_label_entry *ent = d_function_chain->labels->get (to);
+ gcc_assert (ent != NULL);
+
+ /* If the label hasn't been defined yet, defer checking. */
+ if (! DECL_INITIAL (ent->label))
+ {
+ d_label_use_entry *fwdref = ggc_alloc<d_label_use_entry> ();
+ fwdref->level = current_binding_level;
+ fwdref->statement = from;
+ fwdref->next = ent->fwdrefs;
+ ent->fwdrefs = fwdref;
+ return;
+ }
+
+ if (ent->in_try_scope)
+ error_at (make_location_t (from->loc), "cannot goto into try block");
+ else if (ent->in_catch_scope)
+ error_at (make_location_t (from->loc), "cannot goto into catch block");
+ }
+
+ /* Check that a previously seen jump to a newly defined label is valid.
+ S is the label statement; FWDREF is the jump context. This is called
+ for both user-defined and case labels. */
+
+ void check_previous_goto (Statement *s, d_label_use_entry *fwdref)
+ {
+ for (binding_level *b = current_binding_level; b ; b = b->level_chain)
+ {
+ if (b == fwdref->level)
+ break;
+
+ if (b->kind == level_try || b->kind == level_catch)
+ {
+ location_t location;
+
+ if (s->isLabelStatement ())
+ {
+ location = make_location_t (fwdref->statement->loc);
+ if (b->kind == level_try)
+ error_at (location, "cannot goto into try block");
+ else
+ error_at (location, "cannot goto into catch block");
+ }
+ else if (s->isCaseStatement ())
+ {
+ location = make_location_t (s->loc);
+ error_at (location, "case cannot be in different "
+ "try block level from switch");
+ }
+ else if (s->isDefaultStatement ())
+ {
+ location = make_location_t (s->loc);
+ error_at (location, "default cannot be in different "
+ "try block level from switch");
+ }
+ else
+ gcc_unreachable ();
+ }
+ }
+ }
+
+ /* Get or build LABEL_DECL using the IDENT and statement block S given. */
+
+ tree lookup_label (Statement *s, Identifier *ident = NULL)
+ {
+ /* You can't use labels at global scope. */
+ if (d_function_chain == NULL)
+ {
+ error ("label %s referenced outside of any function",
+ ident ? ident->toChars () : "(unnamed)");
+ return NULL_TREE;
+ }
+
+ /* Create the label htab for the function on demand. */
+ if (!d_function_chain->labels)
+ {
+ d_function_chain->labels
+ = hash_map<Statement *, d_label_entry>::create_ggc (13);
+ }
+
+ d_label_entry *ent = d_function_chain->labels->get (s);
+ if (ent != NULL)
+ return ent->label;
+ else
+ {
+ tree name = ident ? get_identifier (ident->toChars ()) : NULL_TREE;
+ tree decl = build_decl (make_location_t (s->loc), LABEL_DECL,
+ name, void_type_node);
+ DECL_CONTEXT (decl) = current_function_decl;
+ DECL_MODE (decl) = VOIDmode;
+
+ /* Create new empty slot. */
+ ent = ggc_cleared_alloc<d_label_entry> ();
+ ent->statement = s;
+ ent->label = decl;
+
+ bool existed = d_function_chain->labels->put (s, *ent);
+ gcc_assert (!existed);
+
+ return decl;
+ }
+ }
+
+ /* Get the LABEL_DECL to represent a break or continue for the
+ statement S given. BC indicates which. */
+
+ tree lookup_bc_label (Statement *s, bc_kind bc)
+ {
+ tree vec = this->lookup_label (s);
+
+ /* The break and continue labels are put into a TREE_VEC. */
+ if (TREE_CODE (vec) == LABEL_DECL)
+ {
+ d_label_entry *ent = d_function_chain->labels->get (s);
+ gcc_assert (ent != NULL);
+
+ vec = make_tree_vec (2);
+ TREE_VEC_ELT (vec, bc_break) = ent->label;
+
+ /* Build the continue label. */
+ tree label = build_decl (make_location_t (s->loc), LABEL_DECL,
+ NULL_TREE, void_type_node);
+ DECL_CONTEXT (label) = current_function_decl;
+ DECL_MODE (label) = VOIDmode;
+ TREE_VEC_ELT (vec, bc_continue) = label;
+
+ ent->label = vec;
+ ent->bc_label = true;
+ }
+
+ return TREE_VEC_ELT (vec, bc);
+ }
+
+ /* Set and return the current break label for the current block. */
+
+ tree push_break_label (Statement *s)
+ {
+ tree label = this->lookup_bc_label (s->getRelatedLabeled (), bc_break);
+ DECL_CHAIN (label) = this->break_label_;
+ this->break_label_ = label;
+ return label;
+ }
+
+ /* Finish with the current break label. */
+
+ void pop_break_label (tree label)
+ {
+ gcc_assert (this->break_label_ == label);
+ this->break_label_ = DECL_CHAIN (this->break_label_);
+ this->do_label (label);
+ }
+
+ /* Set and return the continue label for the current block. */
+
+ tree push_continue_label (Statement *s)
+ {
+ tree label = this->lookup_bc_label (s->getRelatedLabeled (), bc_continue);
+ DECL_CHAIN (label) = this->continue_label_;
+ this->continue_label_ = label;
+ return label;
+ }
+
+ /* Finish with the current continue label. */
+
+ void pop_continue_label (tree label)
+ {
+ gcc_assert (this->continue_label_ == label);
+ this->continue_label_ = DECL_CHAIN (this->continue_label_);
+ this->do_label (label);
+ }
+
+ /* Visitor interfaces. */
+
+
+ /* This should be overridden by each statement class. */
+
+ void visit (Statement *)
+ {
+ gcc_unreachable ();
+ }
+
+ /* The frontend lowers `scope (exit/failure/success)' statements as
+ try/catch/finally. At this point, this statement is just an empty
+ placeholder. Maybe the frontend shouldn't leak these. */
+
+ void visit (OnScopeStatement *)
+ {
+ }
+
+ /* If statements provide simple conditional execution of statements. */
+
+ void visit (IfStatement *s)
+ {
+ this->start_scope (level_cond);
+
+ /* Build the outer 'if' condition, which may produce temporaries
+ requiring scope destruction. */
+ tree ifcond = convert_for_condition (build_expr_dtor (s->condition),
+ s->condition->type);
+ tree ifbody = void_node;
+ tree elsebody = void_node;
+
+ /* Build the 'then' branch. */
+ if (s->ifbody)
+ {
+ push_stmt_list ();
+ this->build_stmt (s->ifbody);
+ ifbody = pop_stmt_list ();
+ }
+
+ /* Now build the 'else' branch, which may have nested 'else if' parts. */
+ if (s->elsebody)
+ {
+ push_stmt_list ();
+ this->build_stmt (s->elsebody);
+ elsebody = pop_stmt_list ();
+ }
+
+ /* Wrap up our constructed if condition into a COND_EXPR. */
+ tree cond = build_vcondition (ifcond, ifbody, elsebody);
+ add_stmt (cond);
+
+ /* Finish the if-then scope. */
+ this->finish_scope ();
+ }
+
+ /* Should there be any `pragma (...)' statements requiring code generation,
+ here would be the place to do it. For now, all pragmas are handled
+ by the frontend. */
+
+ void visit (PragmaStatement *)
+ {
+ }
+
+ /* The frontend lowers `while (...)' statements as `for (...)' loops.
+ This visitor is not strictly required other than to enforce that
+ these kinds of statements never reach here. */
+
+ void visit (WhileStatement *)
+ {
+ gcc_unreachable ();
+ }
+
+ /* Do while statments implement simple loops. The body is executed, then
+ the condition is evaluated. */
+
+ void visit (DoStatement *s)
+ {
+ tree lbreak = this->push_break_label (s);
+
+ this->start_scope (level_loop);
+ if (s->_body)
+ {
+ tree lcontinue = this->push_continue_label (s);
+ this->build_stmt (s->_body);
+ this->pop_continue_label (lcontinue);
+ }
+
+ /* Build the outer 'while' condition, which may produce temporaries
+ requiring scope destruction. */
+ tree exitcond = convert_for_condition (build_expr_dtor (s->condition),
+ s->condition->type);
+ add_stmt (build_vcondition (exitcond, void_node,
+ build1 (GOTO_EXPR, void_type_node, lbreak)));
+ TREE_USED (lbreak) = 1;
+
+ tree body = this->end_scope ();
+ add_stmt (build1 (LOOP_EXPR, void_type_node, body));
+
+ this->pop_break_label (lbreak);
+ }
+
+ /* For statements implement loops with initialization, test, and
+ increment clauses. */
+
+ void visit (ForStatement *s)
+ {
+ tree lbreak = this->push_break_label (s);
+ this->start_scope (level_loop);
+
+ if (s->_init)
+ this->build_stmt (s->_init);
+
+ if (s->condition)
+ {
+ tree exitcond = convert_for_condition (build_expr_dtor (s->condition),
+ s->condition->type);
+ add_stmt (build_vcondition (exitcond, void_node,
+ build1 (GOTO_EXPR, void_type_node,
+ lbreak)));
+ TREE_USED (lbreak) = 1;
+ }
+
+ if (s->_body)
+ {
+ tree lcontinue = this->push_continue_label (s);
+ this->build_stmt (s->_body);
+ this->pop_continue_label (lcontinue);
+ }
+
+ if (s->increment)
+ {
+ /* Force side effects? */
+ add_stmt (build_expr_dtor (s->increment));
+ }
+
+ tree body = this->end_scope ();
+ add_stmt (build1 (LOOP_EXPR, void_type_node, body));
+
+ this->pop_break_label (lbreak);
+ }
+
+ /* The frontend lowers `foreach (...)' statements as `for (...)' loops.
+ This visitor is not strictly required other than to enforce that
+ these kinds of statements never reach here. */
+
+ void visit (ForeachStatement *)
+ {
+ gcc_unreachable ();
+ }
+
+ /* The frontend lowers `foreach (...; [x..y])' statements as `for (...)'
+ loops. This visitor is not strictly required other than to enforce that
+ these kinds of statements never reach here. */
+
+ void visit (ForeachRangeStatement *)
+ {
+ gcc_unreachable ();
+ }
+
+ /* Jump to the associated exit label for the current loop. If IDENT
+ for the Statement is not null, then the label is user defined. */
+
+ void visit (BreakStatement *s)
+ {
+ if (s->ident)
+ {
+ /* The break label may actually be some levels up.
+ eg: on a try/finally wrapping a loop. */
+ LabelStatement *label = this->func_->searchLabel (s->ident)->statement;
+ gcc_assert (label != NULL);
+ Statement *stmt = label->statement->getRelatedLabeled ();
+ this->do_jump (this->lookup_bc_label (stmt, bc_break));
+ }
+ else
+ this->do_jump (this->break_label_);
+ }
+
+ /* Jump to the associated continue label for the current loop. If IDENT
+ for the Statement is not null, then the label is user defined. */
+
+ void visit (ContinueStatement *s)
+ {
+ if (s->ident)
+ {
+ LabelStatement *label = this->func_->searchLabel (s->ident)->statement;
+ gcc_assert (label != NULL);
+ this->do_jump (this->lookup_bc_label (label->statement,
+ bc_continue));
+ }
+ else
+ this->do_jump (this->continue_label_);
+ }
+
+ /* A goto statement jumps to the statement identified by the given label. */
+
+ void visit (GotoStatement *s)
+ {
+ gcc_assert (s->label->statement != NULL);
+ gcc_assert (s->tf == s->label->statement->tf);
+
+ /* If no label found, there was an error. */
+ tree label = this->lookup_label (s->label->statement, s->label->ident);
+ this->do_jump (label);
+
+ /* Need to error if the goto is jumping into a try or catch block. */
+ this->check_goto (s, s->label->statement);
+ }
+
+ /* Statements can be labeled. A label is an identifier that precedes
+ a statement. */
+
+ void visit (LabelStatement *s)
+ {
+ LabelDsymbol *sym;
+
+ if (this->is_return_label (s->ident))
+ sym = this->func_->returnLabel;
+ else
+ sym = this->func_->searchLabel (s->ident);
+
+ /* If no label found, there was an error. */
+ tree label = this->define_label (sym->statement, sym->ident);
+ TREE_USED (label) = 1;
+
+ this->do_label (label);
+
+ if (this->is_return_label (s->ident) && this->func_->fensure != NULL)
+ this->build_stmt (this->func_->fensure);
+ else if (s->statement)
+ this->build_stmt (s->statement);
+ }
+
+ /* A switch statement goes to one of a collection of case statements
+ depending on the value of the switch expression. */
+
+ void visit (SwitchStatement *s)
+ {
+ this->start_scope (level_switch);
+ tree lbreak = this->push_break_label (s);
+
+ tree condition = build_expr_dtor (s->condition);
+ Type *condtype = s->condition->type->toBasetype ();
+
+ /* A switch statement on a string gets turned into a library call,
+ which does a binary lookup on list of string cases. */
+ if (s->condition->type->isString ())
+ {
+ Type *etype = condtype->nextOf ()->toBasetype ();
+ libcall_fn libcall;
+
+ switch (etype->ty)
+ {
+ case Tchar:
+ libcall = LIBCALL_SWITCH_STRING;
+ break;
+
+ case Twchar:
+ libcall = LIBCALL_SWITCH_USTRING;
+ break;
+
+ case Tdchar:
+ libcall = LIBCALL_SWITCH_DSTRING;
+ break;
+
+ default:
+ ::error ("switch statement value must be an array of "
+ "some character type, not %s", etype->toChars ());
+ gcc_unreachable ();
+ }
+
+ /* Apparently the backend is supposed to sort and set the indexes
+ on the case array, have to change them to be usable. */
+ Type *satype = condtype->sarrayOf (s->cases->dim);
+ vec<constructor_elt, va_gc> *elms = NULL;
+
+ s->cases->sort ();
+
+ for (size_t i = 0; i < s->cases->dim; i++)
+ {
+ CaseStatement *cs = (*s->cases)[i];
+ cs->index = i;
+
+ if (cs->exp->op != TOKstring)
+ s->error ("case '%s' is not a string", cs->exp->toChars ());
+ else
+ {
+ tree exp = build_expr (cs->exp, true);
+ CONSTRUCTOR_APPEND_ELT (elms, size_int (i), exp);
+ }
+ }
+
+ /* Build static declaration to reference constructor. */
+ tree ctor = build_constructor (build_ctype (satype), elms);
+ tree decl = build_artificial_decl (TREE_TYPE (ctor), ctor);
+ TREE_READONLY (decl) = 1;
+ d_pushdecl (decl);
+ rest_of_decl_compilation (decl, 1, 0);
+
+ /* Pass it as a dynamic array. */
+ decl = d_array_value (build_ctype (condtype->arrayOf ()),
+ size_int (s->cases->dim),
+ build_address (decl));
+
+ condition = build_libcall (libcall, Type::tint32, 2, decl, condition);
+ }
+ else if (!condtype->isscalar ())
+ {
+ error ("cannot handle switch condition of type %s",
+ condtype->toChars ());
+ gcc_unreachable ();
+ }
+
+ condition = fold (condition);
+
+ /* Build LABEL_DECLs now so they can be refered to by goto case.
+ Also checking the jump from the switch to the label is allowed. */
+ if (s->cases)
+ {
+ for (size_t i = 0; i < s->cases->dim; i++)
+ {
+ CaseStatement *cs = (*s->cases)[i];
+ tree caselabel = this->lookup_label (cs);
+
+ /* Write cases as a series of if-then-else blocks.
+ if (condition == case)
+ goto caselabel; */
+ if (s->hasVars)
+ {
+ tree ifcase = build2 (EQ_EXPR, build_ctype (condtype),
+ condition, build_expr_dtor (cs->exp));
+ tree ifbody = fold_build1 (GOTO_EXPR, void_type_node,
+ caselabel);
+ tree cond = build_vcondition (ifcase, ifbody, void_node);
+ TREE_USED (caselabel) = 1;
+ LABEL_VARIABLE_CASE (caselabel) = 1;
+ add_stmt (cond);
+ }
+
+ this->check_goto (s, cs);
+ }
+
+ if (s->sdefault)
+ {
+ tree defaultlabel = this->lookup_label (s->sdefault);
+
+ /* The default label is the last 'else' block. */
+ if (s->hasVars)
+ {
+ this->do_jump (defaultlabel);
+ LABEL_VARIABLE_CASE (defaultlabel) = 1;
+ }
+
+ this->check_goto (s, s->sdefault);
+ }
+ }
+
+ /* Switch body goes in its own statement list. */
+ push_stmt_list ();
+ if (s->_body)
+ this->build_stmt (s->_body);
+
+ tree casebody = pop_stmt_list ();
+
+ /* Wrap up constructed body into a switch_expr, unless it was
+ converted to an if-then-else expression. */
+ if (s->hasVars)
+ add_stmt (casebody);
+ else
+ {
+ tree switchexpr = build2 (SWITCH_EXPR, TREE_TYPE (condition),
+ condition, casebody);
+ add_stmt (switchexpr);
+ SWITCH_ALL_CASES_P (switchexpr) = 1;
+ }
+
+ SWITCH_BREAK_LABEL_P (lbreak) = 1;
+
+ /* If the switch had any 'break' statements, emit the label now. */
+ this->pop_break_label (lbreak);
+ this->finish_scope ();
+ }
+
+ /* Declare the case label associated with the current SwitchStatement. */
+
+ void visit (CaseStatement *s)
+ {
+ /* Emit the case label. */
+ tree label = this->define_label (s);
+
+ if (LABEL_VARIABLE_CASE (label))
+ this->do_label (label);
+ else
+ {
+ tree casevalue;
+ if (s->exp->type->isscalar ())
+ casevalue = build_expr (s->exp);
+ else
+ casevalue = build_integer_cst (s->index, build_ctype (Type::tint32));
+
+ tree caselabel = build_case_label (casevalue, NULL_TREE, label);
+ add_stmt (caselabel);
+ }
+
+ /* Now do the body. */
+ if (s->statement)
+ this->build_stmt (s->statement);
+ }
+
+ /* Declare the default label associated with the current SwitchStatement. */
+
+ void visit (DefaultStatement *s)
+ {
+ /* Emit the default case label. */
+ tree label = this->define_label (s);
+
+ if (LABEL_VARIABLE_CASE (label))
+ this->do_label (label);
+ else
+ {
+ tree caselabel = build_case_label (NULL_TREE, NULL_TREE, label);
+ add_stmt (caselabel);
+ }
+
+ /* Now do the body. */
+ if (s->statement)
+ this->build_stmt (s->statement);
+ }
+
+ /* Implements 'goto default' by jumping to the label associated with
+ the DefaultStatement in a switch block. */
+
+ void visit (GotoDefaultStatement *s)
+ {
+ tree label = this->lookup_label (s->sw->sdefault);
+ this->do_jump (label);
+ }
+
+ /* Implements 'goto case' by jumping to the label associated with the
+ CaseStatement in a switch block. */
+
+ void visit (GotoCaseStatement *s)
+ {
+ tree label = this->lookup_label (s->cs);
+ this->do_jump (label);
+ }
+
+ /* Throw a SwitchError exception, called when a switch statement has
+ no DefaultStatement, yet none of the cases match. */
+
+ void visit (SwitchErrorStatement *s)
+ {
+ add_stmt (d_assert_call (s->loc, LIBCALL_SWITCH_ERROR));
+ }
+
+ /* A return statement exits the current function and supplies its return
+ value, if the return type is not void. */
+
+ void visit (ReturnStatement *s)
+ {
+ if (s->exp == NULL || s->exp->type->toBasetype ()->ty == Tvoid)
+ {
+ /* Return has no value. */
+ add_stmt (return_expr (NULL_TREE));
+ return;
+ }
+
+ TypeFunction *tf = (TypeFunction *)this->func_->type;
+ Type *type = this->func_->tintro != NULL
+ ? this->func_->tintro->nextOf () : tf->nextOf ();
+
+ if ((this->func_->isMain () || this->func_->isCMain ())
+ && type->toBasetype ()->ty == Tvoid)
+ type = Type::tint32;
+
+ if (this->func_->nrvo_can && this->func_->nrvo_var)
+ {
+ /* Just refer to the DECL_RESULT; this differs from using
+ NULL_TREE in that it indicates that we care about the value
+ of the DECL_RESULT. */
+ tree decl = DECL_RESULT (get_symbol_decl (this->func_));
+ add_stmt (return_expr (decl));
+ }
+ else
+ {
+ /* Convert for initializing the DECL_RESULT. */
+ tree expr = build_return_dtor (s->exp, type, tf);
+ add_stmt (expr);
+ }
+ }
+
+ /* Evaluate the enclosed expression, and add it to the statement list. */
+
+ void visit (ExpStatement *s)
+ {
+ if (s->exp)
+ {
+ /* Expression may produce temporaries requiring scope destruction. */
+ tree exp = build_expr_dtor (s->exp);
+ add_stmt (exp);
+ }
+ }
+
+ /* Evaluate all enclosed statements. */
+
+ void visit (CompoundStatement *s)
+ {
+ if (s->statements == NULL)
+ return;
+
+ for (size_t i = 0; i < s->statements->dim; i++)
+ {
+ Statement *statement = (*s->statements)[i];
+
+ if (statement != NULL)
+ this->build_stmt (statement);
+ }
+ }
+
+ /* The frontend lowers `foreach (Tuple!(...))' statements as an unrolled loop.
+ These are compiled down as a `do ... while (0)', where each unrolled loop
+ is nested inside and given their own continue label to jump to. */
+
+ void visit (UnrolledLoopStatement *s)
+ {
+ if (s->statements == NULL)
+ return;
+
+ tree lbreak = this->push_break_label (s);
+ this->start_scope (level_loop);
+
+ for (size_t i = 0; i < s->statements->dim; i++)
+ {
+ Statement *statement = (*s->statements)[i];
+
+ if (statement != NULL)
+ {
+ tree lcontinue = this->push_continue_label (statement);
+ this->build_stmt (statement);
+ this->pop_continue_label (lcontinue);
+ }
+ }
+
+ this->do_jump (this->break_label_);
+
+ tree body = this->end_scope ();
+ add_stmt (build1 (LOOP_EXPR, void_type_node, body));
+
+ this->pop_break_label (lbreak);
+ }
+
+ /* Start a new scope and visit all nested statements, wrapping
+ them up into a BIND_EXPR at the end of the scope. */
+
+ void visit (ScopeStatement *s)
+ {
+ if (s->statement == NULL)
+ return;
+
+ this->start_scope (level_block);
+ this->build_stmt (s->statement);
+ this->finish_scope ();
+ }
+
+ /* A with statement is a way to simplify repeated references to the same
+ object, where the handle is either a class or struct instance. */
+
+ void visit (WithStatement *s)
+ {
+ this->start_scope (level_with);
+
+ if (s->wthis)
+ {
+ /* Perform initialisation of the 'with' handle. */
+ ExpInitializer *ie = s->wthis->_init->isExpInitializer ();
+ gcc_assert (ie != NULL);
+
+ declare_local_var (s->wthis);
+ tree init = build_expr_dtor (ie->exp);
+ add_stmt (init);
+ }
+
+ if (s->_body)
+ this->build_stmt (s->_body);
+
+ this->finish_scope ();
+ }
+
+ /* Implements 'throw Object'. Frontend already checks that the object
+ thrown is a class type, but does not check if it is derived from
+ Object. Foreign objects are not currently supported at run-time. */
+
+ void visit (ThrowStatement *s)
+ {
+ ClassDeclaration *cd = s->exp->type->toBasetype ()->isClassHandle ();
+ InterfaceDeclaration *id = cd->isInterfaceDeclaration ();
+ tree arg = build_expr_dtor (s->exp);
+
+ if (!flag_exceptions)
+ {
+ static int warned = 0;
+ if (!warned)
+ {
+ error_at (make_location_t (s->loc), "exception handling disabled, "
+ "use -fexceptions to enable");
+ warned = 1;
+ }
+ }
+
+ if (cd->isCPPclass () || (id != NULL && id->isCPPclass ()))
+ error_at (make_location_t (s->loc), "cannot throw C++ classes");
+ else if (cd->com || (id != NULL && id->com))
+ error_at (make_location_t (s->loc), "cannot throw COM objects");
+ else
+ arg = build_nop (build_ctype (get_object_type ()), arg);
+
+ add_stmt (build_libcall (LIBCALL_THROW, Type::tvoid, 1, arg));
+ }
+
+ /* Build a try-catch statement, one of the building blocks for exception
+ handling generated by the frontend. This is also used to implement
+ `scope (failure)' statements. */
+
+ void visit (TryCatchStatement *s)
+ {
+ this->start_scope (level_try);
+ if (s->_body)
+ this->build_stmt (s->_body);
+
+ tree trybody = this->end_scope ();
+
+ /* Try handlers go in their own statement list. */
+ push_stmt_list ();
+
+ if (s->catches)
+ {
+ for (size_t i = 0; i < s->catches->dim; i++)
+ {
+ Catch *vcatch = (*s->catches)[i];
+
+ this->start_scope (level_catch);
+
+ tree ehptr = builtin_decl_explicit (BUILT_IN_EH_POINTER);
+ tree catchtype = build_ctype (vcatch->type);
+ tree object = NULL_TREE;
+
+ ehptr = build_call_expr (ehptr, 1, integer_zero_node);
+
+ /* Retrieve the internal exception object, which could be for a
+ D or C++ catch handler. This is different from the generic
+ exception pointer returned from gcc runtime. */
+ Type *tcatch = vcatch->type->toBasetype ();
+ ClassDeclaration *cd = tcatch->isClassHandle ();
+
+ libcall_fn libcall = (cd->isCPPclass ()) ? LIBCALL_CXA_BEGIN_CATCH
+ : LIBCALL_BEGIN_CATCH;
+ object = build_libcall (libcall, vcatch->type, 1, ehptr);
+
+ if (vcatch->var)
+ {
+ tree var = get_symbol_decl (vcatch->var);
+ tree init = build_assign (INIT_EXPR, var, object);
+
+ declare_local_var (vcatch->var);
+ add_stmt (init);
+ }
+ else
+ {
+ /* Still need to emit a call to __gdc_begin_catch() to
+ remove the object from the uncaught exceptions list. */
+ add_stmt (object);
+ }
+
+ if (vcatch->handler)
+ this->build_stmt (vcatch->handler);
+
+ tree catchbody = this->end_scope ();
+
+ /* Need to wrap C++ handlers in a try/finally block to signal
+ the end catch callback. */
+ if (cd->isCPPclass ())
+ {
+ tree endcatch = build_libcall (LIBCALL_CXA_END_CATCH,
+ Type::tvoid, 0);
+ catchbody = build2 (TRY_FINALLY_EXPR, void_type_node,
+ catchbody, endcatch);
+ }
+
+ add_stmt (build2 (CATCH_EXPR, void_type_node,
+ catchtype, catchbody));
+ }
+ }
+
+ tree catches = pop_stmt_list ();
+
+ /* Back-end expects all catches in a TRY_CATCH_EXPR to be enclosed in a
+ statement list, however pop_stmt_list may optimize away the list
+ if there is only a single catch to push. */
+ if (TREE_CODE (catches) != STATEMENT_LIST)
+ {
+ tree stmt_list = alloc_stmt_list ();
+ append_to_statement_list_force (catches, &stmt_list);
+ catches = stmt_list;
+ }
+
+ add_stmt (build2 (TRY_CATCH_EXPR, void_type_node, trybody, catches));
+ }
+
+ /* Build a try-finally statement, one of the building blocks for exception
+ handling generated by the frontend. This is also used to implement
+ `scope (exit)' statements. */
+
+ void visit (TryFinallyStatement *s)
+ {
+ this->start_scope (level_try);
+ if (s->_body)
+ this->build_stmt (s->_body);
+
+ tree trybody = this->end_scope ();
+
+ this->start_scope (level_finally);
+ if (s->finalbody)
+ this->build_stmt (s->finalbody);
+
+ tree finally = this->end_scope ();
+
+ add_stmt (build2 (TRY_FINALLY_EXPR, void_type_node, trybody, finally));
+ }
+
+ /* The frontend lowers `synchronized (...)' statements as a call to
+ monitor/critical enter and exit wrapped around try/finally.
+ This visitor is not strictly required other than to enforce that
+ these kinds of statements never reach here. */
+
+ void visit (SynchronizedStatement *)
+ {
+ gcc_unreachable ();
+ }
+
+ /* D Inline Assembler is not implemented, as it would require writing
+ an assembly parser for each supported target. Instead we leverage
+ GCC extended assembler using the GccAsmStatement class. */
+
+ void visit (AsmStatement *)
+ {
+ sorry ("D inline assembler statements are not supported in GDC.");
+ }
+
+ /* Build a GCC extended assembler expression, whose components are
+ an INSN string, some OUTPUTS, some INPUTS, and some CLOBBERS. */
+
+ void visit (GccAsmStatement *s)
+ {
+ StringExp *insn = (StringExp *)s->insn;
+ tree outputs = NULL_TREE;
+ tree inputs = NULL_TREE;
+ tree clobbers = NULL_TREE;
+ tree labels = NULL_TREE;
+
+ /* Collect all arguments, which may be input or output operands. */
+ if (s->args)
+ {
+ for (size_t i = 0; i < s->args->dim; i++)
+ {
+ Identifier *name = (*s->names)[i];
+ const char *sname = name ? name->toChars () : NULL;
+ tree id = name ? build_string (strlen (sname), sname) : NULL_TREE;
+
+ StringExp *constr = (StringExp *)(*s->constraints)[i];
+ const char *cstring = (const char *)(constr->len
+ ? constr->string : "");
+ tree str = build_string (constr->len, cstring);
+
+ Expression *earg = (*s->args)[i];
+ tree val = build_expr (earg);
+
+ if (i < s->outputargs)
+ {
+ tree arg = build_tree_list (id, str);
+ outputs = chainon (outputs, build_tree_list (arg, val));
+ }
+ else
+ {
+ tree arg = build_tree_list (id, str);
+ inputs = chainon (inputs, build_tree_list (arg, val));
+ }
+ }
+ }
+
+ /* Collect all clobber arguments. */
+ if (s->clobbers)
+ {
+ for (size_t i = 0; i < s->clobbers->dim; i++)
+ {
+ StringExp *clobber = (StringExp *)(*s->clobbers)[i];
+ const char *cstring = (const char *)(clobber->len
+ ? clobber->string : "");
+
+ tree val = build_string (clobber->len, cstring);
+ clobbers = chainon (clobbers, build_tree_list (0, val));
+ }
+ }
+
+ /* Collect all goto labels, these should have been already checked
+ by the front-end, so pass down the label symbol to the back-end. */
+ if (s->labels)
+ {
+ for (size_t i = 0; i < s->labels->dim; i++)
+ {
+ Identifier *ident = (*s->labels)[i];
+ GotoStatement *gs = (*s->gotos)[i];
+
+ gcc_assert (gs->label->statement != NULL);
+ gcc_assert (gs->tf == gs->label->statement->tf);
+
+ const char *sident = ident->toChars ();
+ tree name = build_string (strlen (sident), sident);
+ tree label = this->lookup_label (gs->label->statement,
+ gs->label->ident);
+ TREE_USED (label) = 1;
+
+ labels = chainon (labels, build_tree_list (name, label));
+ }
+ }
+
+ /* Do some extra validation on all input and output operands. */
+ const char *insnstring = (const char *)(insn->len ? insn->string : "");
+ tree string = build_string (insn->len, insnstring);
+ string = resolve_asm_operand_names (string, outputs, inputs, labels);
+
+ if (s->args)
+ {
+ unsigned noutputs = s->outputargs;
+ unsigned ninputs = (s->args->dim - noutputs);
+ const char **oconstraints = XALLOCAVEC (const char *, noutputs);
+ bool allows_mem, allows_reg, is_inout;
+ size_t i;
+ tree t;
+
+ for (i = 0, t = outputs; t != NULL_TREE; t = TREE_CHAIN (t), i++)
+ {
+ tree output = TREE_VALUE (t);
+ const char *constraint
+ = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
+
+ oconstraints[i] = constraint;
+
+ if (parse_output_constraint (&constraint, i, ninputs, noutputs,
+ &allows_mem, &allows_reg, &is_inout))
+ {
+ /* If the output argument is going to end up in memory. */
+ if (!allows_reg)
+ d_mark_addressable (output);
+ }
+ else
+ output = error_mark_node;
+
+ TREE_VALUE (t) = output;
+ }
+
+ for (i = 0, t = inputs; t != NULL_TREE; t = TREE_CHAIN (t), i++)
+ {
+ tree input = TREE_VALUE (t);
+ const char *constraint
+ = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
+
+ if (parse_input_constraint (&constraint, i, ninputs, noutputs, 0,
+ oconstraints, &allows_mem, &allows_reg))
+ {
+ /* If the input argument is going to end up in memory. */
+ if (!allows_reg && allows_mem)
+ d_mark_addressable (input);
+ }
+ else
+ input = error_mark_node;
+
+ TREE_VALUE (t) = input;
+ }
+ }
+
+ tree exp = build5 (ASM_EXPR, void_type_node, string,
+ outputs, inputs, clobbers, labels);
+ SET_EXPR_LOCATION (exp, make_location_t (s->loc));
+
+ /* If the extended syntax was not used, mark the ASM_EXPR. */
+ if (s->args == NULL && s->clobbers == NULL)
+ ASM_INPUT_P (exp) = 1;
+
+ /* Asm statements are treated as volatile unless 'pure'. */
+ ASM_VOLATILE_P (exp) = !(s->stc & STCpure);
+
+ add_stmt (exp);
+ }
+
+ /* Import symbols from another module. */
+
+ void visit (ImportStatement *s)
+ {
+ if (s->imports == NULL)
+ return;
+
+ for (size_t i = 0; i < s->imports->dim; i++)
+ {
+ Dsymbol *dsym = (*s->imports)[i];
+
+ if (dsym != NULL)
+ build_decl_tree (dsym);
+ }
+ }
+};
+
+/* Main entry point for the IRVisitor interface to generate
+ code for the body of function FD. */
+
+void
+build_function_body (FuncDeclaration *fd)
+{
+ IRVisitor v = IRVisitor (fd);
+ location_t saved_location = input_location;
+ input_location = make_location_t (fd->loc);
+ v.build_stmt (fd->fbody);
+ input_location = saved_location;
+}