diff options
author | Iain Buclaw <ibuclaw@gcc.gnu.org> | 2018-10-28 19:51:47 +0000 |
---|---|---|
committer | Iain Buclaw <ibuclaw@gcc.gnu.org> | 2018-10-28 19:51:47 +0000 |
commit | b4c522fabd0df7be08882d2207df8b2765026110 (patch) | |
tree | b5ffc312b0a441c1ba24323152aec463fdbe5e9f /gcc/d/toir.cc | |
parent | 01ce9e31a02c8039d88e90f983735104417bf034 (diff) | |
download | gcc-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.cc | 1447 |
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; +} |