From 7dfcf8e3dc169b61646d9e4e680c6fa36e69f4b5 Mon Sep 17 00:00:00 2001 From: Roberto Costa Date: Thu, 7 Sep 2006 07:37:27 +0000 Subject: Initial support for CLI back-end git-svn-id: https://gcc.gnu.org/svn/gcc/branches/st/cli@116745 138bc75d-0d04-0410-961f-82ee72b054a4 --- cil32-crosstool.sh | 187 ++ config.sub | 5 + configure | 4 + configure.in | 4 + gcc/config.gcc | 9 + gcc/config/cil32/cil32-modes.def | 42 + gcc/config/cil32/cil32-protos.h | 29 + gcc/config/cil32/cil32.c | 232 +++ gcc/config/cil32/cil32.h | 539 ++++++ gcc/config/cil32/cil32.md | 211 +++ gcc/config/cil32/cil32.opt | 53 + gcc/config/cil32/gcc4net.cs | 106 ++ gcc/config/cil32/gen-cil.c | 3562 ++++++++++++++++++++++++++++++++++++++ gcc/config/cil32/gen-cil.h | 47 + gcc/config/cil32/t-cil32 | 60 + gcc/config/cil32/tree-simp-cil.c | 2166 +++++++++++++++++++++++ gcc/config/cil32/tree-simp-cil.h | 39 + gcc/opts.c | 3 +- gcc/passes.c | 9 + gcc/pointer-set.c | 27 + gcc/pointer-set.h | 12 + gcc/varasm.c | 5 + 22 files changed, 7350 insertions(+), 1 deletion(-) create mode 100755 cil32-crosstool.sh create mode 100644 gcc/config/cil32/cil32-modes.def create mode 100644 gcc/config/cil32/cil32-protos.h create mode 100644 gcc/config/cil32/cil32.c create mode 100644 gcc/config/cil32/cil32.h create mode 100644 gcc/config/cil32/cil32.md create mode 100644 gcc/config/cil32/cil32.opt create mode 100644 gcc/config/cil32/gcc4net.cs create mode 100644 gcc/config/cil32/gen-cil.c create mode 100644 gcc/config/cil32/gen-cil.h create mode 100644 gcc/config/cil32/t-cil32 create mode 100644 gcc/config/cil32/tree-simp-cil.c create mode 100644 gcc/config/cil32/tree-simp-cil.h diff --git a/cil32-crosstool.sh b/cil32-crosstool.sh new file mode 100755 index 00000000000..53cf09e75e2 --- /dev/null +++ b/cil32-crosstool.sh @@ -0,0 +1,187 @@ +#!/bin/sh + + +INSTALL_GCC=yes + +TARGET=cil32 + +BUILD_DIR=${BUILD_DIR-`pwd`} +SOURCE_DIR=${SOURCE_DIR-`dirname $0`} +SOURCE_DIR=`cd $SOURCE_DIR; pwd` +PREFIX=${PREFIX-`pwd`/newbuild} +SYSROOT=${PREFIX}/${TARGET} +LANGUAGES=c + +_help() +{ + echo "Options:" + echo " -nogcc : build only binutils (no compiler)" + echo " -cpp : build also C++ compiler" + echo " -help : this message" +} + +while [ $# -gt 0 ] ; do + +if [ "x$1" == x-help ] ; then + _help ; + exit 0 +elif [ "x$1" == x-cpp ] ; then + LANGUAGES=c,c++ +elif [ "x$1" == x-nogcc ] ; then + INSTALL_GCC=no +else + echo "Unrecognized option: " $1 + _help ; + exit 1 +fi + +shift 1 +done + +echo "DIRECTORIES:" +echo " BUILD_DIR: " ${BUILD_DIR} +echo " SOURCE_DIR: " ${SOURCE_DIR} +echo " SYSROOT: " ${SYSROOT} +echo " PREFIX: " ${PREFIX} + + +PATH="${PREFIX}/bin:${PATH}" +export PATH + +# Create directories + +mkdir -p ${PREFIX} +mkdir -p ${PREFIX}/bin +mkdir -p ${PREFIX}/include +mkdir -p ${PREFIX}/lib +# SYSROOT must be = ${PREFIX}/${TARGET} +mkdir -p ${SYSROOT} +mkdir -p ${SYSROOT}/bin +( cd ${SYSROOT} ; ln -fs ../include . ) +( cd ${SYSROOT} ; ln -fs ../lib . ) + +# Install Binutils + +CIL_AS=${CIL_AS-`which ilasm`} +CIL_LD=${CIL_LD-`which ilalink`} + +echo "Tools:" +echo " CIL ASSEMBLER: " ${CIL_AS} +echo " CIL LINKER: " ${CIL_LD} + +echo +echo "Installing Bin Utils" + +cat > ${SYSROOT}/bin/tool_not_exist <<_EOF_ +#!/bin/sh +echo "Tool " \`basename \$0\` " not provided" +exit 1 +_EOF_ +chmod 755 ${SYSROOT}/bin/tool_not_exist + +cat > ${SYSROOT}/bin/as <<_EOF_ +#!/bin/sh + +${CIL_AS} "\$@" +_EOF_ +chmod 755 ${SYSROOT}/bin/as + +cat > ${SYSROOT}/bin/ld <<_EOF_ +#!/bin/sh + +PARAM= + +for i in "\$@" ; do + case "\$i" in + -lm) + ;; + /tmp*) + PARAM="\${PARAM} /\${i}"; + ;; + *) + PARAM="\${PARAM} \${i}"; + ;; + esac +done + +${CIL_LD} \$PARAM + +_EOF_ +chmod 755 ${SYSROOT}/bin/ld + +cp ${SYSROOT}/bin/tool_not_exist ${SYSROOT}/bin/addr2line +cp ${SYSROOT}/bin/tool_not_exist ${SYSROOT}/bin/ar +cp ${SYSROOT}/bin/tool_not_exist ${SYSROOT}/bin/nm +cp ${SYSROOT}/bin/tool_not_exist ${SYSROOT}/bin/objcopy +cp ${SYSROOT}/bin/tool_not_exist ${SYSROOT}/bin/objdump +cp ${SYSROOT}/bin/tool_not_exist ${SYSROOT}/bin/ranlib +cp ${SYSROOT}/bin/tool_not_exist ${SYSROOT}/bin/readelf +cp ${SYSROOT}/bin/tool_not_exist ${SYSROOT}/bin/size +cp ${SYSROOT}/bin/tool_not_exist ${SYSROOT}/bin/strings +cp ${SYSROOT}/bin/tool_not_exist ${SYSROOT}/bin/strip + + +cp ${SYSROOT}/bin/addr2line ${PREFIX}/bin/${TARGET}-addr2line +cp ${SYSROOT}/bin/ar ${PREFIX}/bin/${TARGET}-ar +cp ${SYSROOT}/bin/as ${PREFIX}/bin/${TARGET}-as +cp ${SYSROOT}/bin/ld ${PREFIX}/bin/${TARGET}-ld +cp ${SYSROOT}/bin/nm ${PREFIX}/bin/${TARGET}-nm +cp ${SYSROOT}/bin/objcopy ${PREFIX}/bin/${TARGET}-objcopy +cp ${SYSROOT}/bin/objdump ${PREFIX}/bin/${TARGET}-objdump +cp ${SYSROOT}/bin/ranlib ${PREFIX}/bin/${TARGET}-ranlib +cp ${SYSROOT}/bin/readelf ${PREFIX}/bin/${TARGET}-readelf +cp ${SYSROOT}/bin/size ${PREFIX}/bin/${TARGET}-size +cp ${SYSROOT}/bin/strings ${PREFIX}/bin/${TARGET}-strings +cp ${SYSROOT}/bin/strip ${PREFIX}/bin/${TARGET}-strip + +echo +echo "Installing vm wrappers" + +cat > ${PREFIX}/bin/${TARGET}-ilrun <<_EOF_ +#!/bin/sh + +ilrun -L ${PREFIX}/lib "\$@" +_EOF_ +chmod 755 ${PREFIX}/bin/${TARGET}-ilrun + +cat > ${PREFIX}/bin/${TARGET}-mono <<_EOF_ +#!/bin/sh + +export MONO_PATH=${PREFIX}/lib + +mono "\$@" +_EOF_ +chmod 755 ${PREFIX}/bin/${TARGET}-mono + +# ... To Be Completed with needed binutil tools + +# ... To Be Completed with libraries + +if test "x${INSTALL_GCC}" == "xno" +then + exit 0 +fi + +# Configure gcc + +echo +echo "Configuring GCC" + +mkdir -p ${BUILD_DIR}/build-gcc +cd ${BUILD_DIR}/build-gcc + +${SOURCE_DIR}/configure --target=${TARGET} \ + --enable-languages=${LANGUAGES} \ + --disable-bootstrap \ + --prefix=${PREFIX} \ + --with-local-prefix=${SYSROOT} + +# Build and install gcc + +echo +echo "Building GCC" +make all + +echo +echo "Installing GCC" +make install diff --git a/config.sub b/config.sub index 4d936e23942..03e8910a9ca 100755 --- a/config.sub +++ b/config.sub @@ -288,6 +288,9 @@ case $basic_machine in | z8k) basic_machine=$basic_machine-unknown ;; + cil32) + basic_machine=$basic_machine-unknown + ;; m6811 | m68hc11 | m6812 | m68hc12) # Motorola 68HC11/12. basic_machine=$basic_machine-unknown @@ -372,6 +375,8 @@ case $basic_machine in | ymp-* \ | z8k-*) ;; + cil32-*) + ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) diff --git a/configure b/configure index 22cdb87e473..0780b324d03 100755 --- a/configure +++ b/configure @@ -1342,6 +1342,10 @@ case "${target}" in c54x*-*-* | tic54x-*-*) noconfigdirs="$noconfigdirs target-libstdc++-v3 target-libgloss ${libgcj} gcc gdb newlib" ;; + cil32-*-*) + unsupported_languages="$unsupported_languages ada fortran java" + noconfigdirs="$noconfigdirs target-libssp target-libiberty target-libmudflap" + ;; cris-*-* | crisv32-*-*) unsupported_languages="$unsupported_languages java" case "${target}" in diff --git a/configure.in b/configure.in index 0c83351863c..2ab1abd1512 100644 --- a/configure.in +++ b/configure.in @@ -518,6 +518,10 @@ case "${target}" in c54x*-*-* | tic54x-*-*) noconfigdirs="$noconfigdirs target-libstdc++-v3 target-libgloss ${libgcj} gcc gdb newlib" ;; + cil32-*-*) + unsupported_languages="$unsupported_languages ada fortran java" + noconfigdirs="$noconfigdirs target-libssp target-libiberty target-libmudflap" + ;; cris-*-* | crisv32-*-*) unsupported_languages="$unsupported_languages java" case "${target}" in diff --git a/gcc/config.gcc b/gcc/config.gcc index 7ea15d34b5e..571dba31047 100644 --- a/gcc/config.gcc +++ b/gcc/config.gcc @@ -257,6 +257,9 @@ arm*-*-*) bfin*-*) cpu_type=bfin ;; +cil32-*-*) + cpu_type=cil32 + ;; ep9312*-*-*) cpu_type=arm ;; @@ -799,6 +802,12 @@ c4x-* | tic4x-*) c_target_objs="c4x-c.o" cxx_target_objs="c4x-c.o" ;; +cil32-*-*) + gas=no + use_collect2=no + extra_objs="tree-simp-cil.o gen-cil.o" + target_gtfiles="\$(srcdir)/config/cil32/gen-cil.c" + ;; cris-*-aout) tm_file="dbxelf.h ${tm_file} cris/aout.h" gas=yes diff --git a/gcc/config/cil32/cil32-modes.def b/gcc/config/cil32/cil32-modes.def new file mode 100644 index 00000000000..db119f94fda --- /dev/null +++ b/gcc/config/cil32/cil32-modes.def @@ -0,0 +1,42 @@ +/* Definitions of target machine for GCC for CIL32. + + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GCC. + +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 2, 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 COPYING. If not, write to the Free +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. + +Authors: + Andrea Bona + Andrea Ornstein + Erven Rohou + Roberto Costa + +Contact information at STMicroelectronics: +Roberto Costa */ + +/* Vector modes. */ +VECTOR_MODES (INT, 4); /* V4QI V2HI */ +VECTOR_MODES (INT, 8); /* V8QI V4HI V2SI */ +VECTOR_MODES (INT, 16); /* V16QI V8HI V4SI V2DI */ +VECTOR_MODES (FLOAT, 8); /* V4HF V2SF */ +VECTOR_MODES (FLOAT, 16); /* V8HF V4SF V2DF */ +VECTOR_MODE (INT, DI, 4); /* V4DI */ +VECTOR_MODE (INT, SI, 8); /* V8SI */ +VECTOR_MODE (INT, HI, 16); /* V16HI */ +VECTOR_MODE (INT, QI, 32); /* V32QI */ +VECTOR_MODE (FLOAT, DF, 4); /* V4DF */ +VECTOR_MODE (FLOAT, SF, 8); /* V8SF */ diff --git a/gcc/config/cil32/cil32-protos.h b/gcc/config/cil32/cil32-protos.h new file mode 100644 index 00000000000..57cbd7c05f1 --- /dev/null +++ b/gcc/config/cil32/cil32-protos.h @@ -0,0 +1,29 @@ +/* Definitions for GCC. Part of the machine description for cil32. + + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GCC. + +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 2, 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 COPYING. If not, write to the Free +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. + +Authors: + Andrea Bona + Andrea Ornstein + Erven Rohou + Roberto Costa + +Contact information at STMicroelectronics: +Roberto Costa */ diff --git a/gcc/config/cil32/cil32.c b/gcc/config/cil32/cil32.c new file mode 100644 index 00000000000..7cf4938b285 --- /dev/null +++ b/gcc/config/cil32/cil32.c @@ -0,0 +1,232 @@ +/* Definitions for GCC. Part of the machine description for cil32. + + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GCC. + +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 2, 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 COPYING. If not, write to the Free +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. + +Authors: + Andrea Bona + Andrea Ornstein + Erven Rohou + Roberto Costa + +Contact information at STMicroelectronics: +Roberto Costa */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "real.h" +#include "insn-config.h" +#include "conditions.h" +#include "insn-attr.h" +#include "flags.h" +#include "tree.h" +#include "expr.h" +#include "except.h" +#include "function.h" +#include "toplev.h" +#include "recog.h" +#include "reload.h" +#include "tm_p.h" +#include "debug.h" +#include "output.h" +#include "target.h" +#include "target-def.h" +#include "ggc.h" +#include "optabs.h" +#include "langhooks.h" +#include "gen-cil.h" + +/* Per-function machine data. */ +struct machine_function GTY(()) + { + char dummy; + }; + +static tree cil32_handle_function_attribute (tree *, tree, tree, int, bool *); +static void cil32_file_start (void); +static void cil32_file_end (void); +static void cil32_named_section (const char *, unsigned int, tree); +static void cil32_globalize_label (FILE *, const char *); +static bool cil32_assemble_integer (rtx, unsigned int, int); + +static void cil32_init_builtins (void); +static tree cil32_build_builtin_va_list (void); +static tree cil32_gimplify_va_arg (tree, tree, tree*, tree*); + +static bool cil32_vector_mode_supported_p (enum machine_mode); + +const struct attribute_spec cil32_attribute_table[]; + +/* Initialize the GCC target structure. */ +#undef TARGET_ATTRIBUTE_TABLE +#define TARGET_ATTRIBUTE_TABLE cil32_attribute_table + +#undef TARGET_INIT_BUILTINS +#define TARGET_INIT_BUILTINS cil32_init_builtins + + +#undef TARGET_ASM_FILE_START +#define TARGET_ASM_FILE_START cil32_file_start +#undef TARGET_ASM_FILE_END +#define TARGET_ASM_FILE_END cil32_file_end +#undef TARGET_ASM_NAMED_SECTION +#define TARGET_ASM_NAMED_SECTION cil32_named_section +#undef TARGET_ASM_GLOBALIZE_LABEL +#define TARGET_ASM_GLOBALIZE_LABEL cil32_globalize_label +#undef TARGET_ASM_INTEGER +#define TARGET_ASM_INTEGER cil32_assemble_integer + +#undef TARGET_BUILD_BUILTIN_VA_LIST +#define TARGET_BUILD_BUILTIN_VA_LIST cil32_build_builtin_va_list +#undef TARGET_GIMPLIFY_VA_ARG_EXPR +#define TARGET_GIMPLIFY_VA_ARG_EXPR cil32_gimplify_va_arg + +#undef TARGET_VECTOR_MODE_SUPPORTED_P +#define TARGET_VECTOR_MODE_SUPPORTED_P cil32_vector_mode_supported_p + + +struct gcc_target targetm = TARGET_INITIALIZER; + +/* CIL32 Code */ + +static tree +cil32_handle_function_attribute (tree *node, tree name, + tree args ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED, + bool *no_add_attrs) +{ + if (TREE_CODE (*node) != FUNCTION_DECL) + { + warning (OPT_Wattributes, "%qs attribute only applies to functions", + IDENTIFIER_POINTER (name)); + *no_add_attrs = true; + return NULL_TREE; + } + + if (strcmp (IDENTIFIER_POINTER (name), "pinvoke") == 0) + cil_add_pinvoke(*node); + + return NULL_TREE; +} + +/* Table of valid machine attributes. */ +const struct attribute_spec cil32_attribute_table[] = +{ + /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ + { "assembly_name", 1,1,false,false,false, cil32_handle_function_attribute }, + { "cil_name", 1,1,false,false,false, cil32_handle_function_attribute }, + { "pinvoke", 1,2,false,false,false, cil32_handle_function_attribute }, + { NULL, 0,0,false,false,false, NULL } +}; + +static void +cil32_file_start (void) +{ + gen_cil_init (); +} + +static void +cil32_file_end (void) +{ + gen_cil_fini (); +} + +static void +cil32_named_section (const char *name ATTRIBUTE_UNUSED, + unsigned int flags ATTRIBUTE_UNUSED, + tree decl ATTRIBUTE_UNUSED) +{ +} + +static void +cil32_globalize_label (FILE *stream ATTRIBUTE_UNUSED, + const char *name ATTRIBUTE_UNUSED) +{ +} + +static bool +cil32_assemble_integer (rtx x ATTRIBUTE_UNUSED, + unsigned int size ATTRIBUTE_UNUSED, + int aligned_p ATTRIBUTE_UNUSED) +{ + return true; +} + +static tree cil32_builtin_va_arg_decl; + +static void +cil32_init_builtins (void) +{ + + tree arglist = build_tree_list (NULL_TREE, ptr_type_node); + arglist = tree_cons (NULL_TREE, va_list_type_node, arglist); + cil32_builtin_va_arg_decl = lang_hooks.builtin_function ("__builtin_va_arg", + build_function_type (ptr_type_node, + arglist), + CIL32_BUILTIN_VA_ARG, + BUILT_IN_MD, + NULL, + NULL_TREE); +} + +static tree +cil32_build_builtin_va_list (void) +{ + tree f_dummy, record, type_decl; + + record = (*lang_hooks.types.make_type) (RECORD_TYPE); + type_decl = build_decl (TYPE_DECL, get_identifier ("__va_list"), record); + + f_dummy = build_decl (FIELD_DECL, get_identifier ("__dummy"), ptr_type_node); + + DECL_FIELD_CONTEXT (f_dummy) = record; + + TREE_CHAIN (record) = type_decl; + TYPE_NAME (record) = type_decl; + TYPE_FIELDS (record) = f_dummy; + + layout_type (record); + return record; +} + +static tree +cil32_gimplify_va_arg (tree valist, tree type, tree *pre_p ATTRIBUTE_UNUSED, tree *post_p ATTRIBUTE_UNUSED) +{ + tree fcall; + tree ptr_type = build_pointer_type(type); + tree arglist = build_tree_list (NULL_TREE, build_int_cstu (ptr_type,0)); + arglist = tree_cons (NULL_TREE, valist, arglist); + fcall = build_function_call_expr (cil32_builtin_va_arg_decl, arglist); + TREE_TYPE (fcall) = ptr_type; + return build1(INDIRECT_REF,type,fcall); +} + +/* Target hook for vector_mode_supported_p. */ +static bool +cil32_vector_mode_supported_p (enum machine_mode mode ATTRIBUTE_UNUSED) +{ + return true; +} + +#include "gt-cil32.h" diff --git a/gcc/config/cil32/cil32.h b/gcc/config/cil32/cil32.h new file mode 100644 index 00000000000..43af5cf5eda --- /dev/null +++ b/gcc/config/cil32/cil32.h @@ -0,0 +1,539 @@ +/* Definitions for GCC. Part of the machine description for cil32. + + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GCC. + +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 2, 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 COPYING. If not, write to the Free +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. + +Authors: + Andrea Bona + Andrea Ornstein + Erven Rohou + Roberto Costa + +Contact information at STMicroelectronics: +Roberto Costa */ + +#define DISABLE_RTL_PASSES 1 + +#define IN_NAMED_SECTION(DECL) 0 +#undef ASM_OUTPUT_ALIGN +#define ASM_OUTPUT_ALIGN(FILE,LOG) do { } while(0) +#undef ASM_DECLARE_OBJECT_NAME +#define ASM_DECLARE_OBJECT_NAME(FILE, NAME, DECL) do { } while(0) + +/* make_decl_cil (stream, decl); */ +#define TARGET_DECLARE_VARIABLE(STREAM,DECL) make_decl_cil(STREAM,DECL) + +/* Node: Driver */ + +#define CC1_SPEC "%{!mnoopensystemc:-mopensystemc}" +#define LIB_SPEC "" +#define LIBGCC_SPEC "" +#define LINK_SPEC "--no-stdlib %{!nostdlib:-l gcc4net.dll} %{shared:--format dll}" +#define STARTFILE_SPEC "" +#define ENDFILE_SPEC "" + +/* Node: Run-time Target */ + +#define TARGET_CPU_CPP_BUILTINS() \ + do \ + { \ + builtin_define_std ("__cil32__"); \ + builtin_define_std ("__GNU_CIL__"); \ + builtin_assert ("cpu=cil32"); \ + builtin_assert ("machine=cil32"); \ + } \ + while(0) + +/* This needs to be at least 32 bits. */ +extern int target_flags; + +/* Print subsidiary information on the compiler version in use. + Do not use VD.D syntax (D=digit), since this will cause confusion + with the base gcc version among users, when we ask which version of + gcc-cil32 they are using. Please use some flavor of "R" for + the version (no need for major.minor versions, I believe). */ +#define TARGET_VERSION \ + fprintf (stderr, " [cil32]") + +/* Node: Storage Layout */ + +#define BITS_BIG_ENDIAN 0 +#define BYTES_BIG_ENDIAN 0 +#define WORDS_BIG_ENDIAN 0 + +#define UNITS_PER_WORD 4 +#define UNITS_PER_SIMD_WORD 4 + +/* Unused by cil32 machine */ +#define PARM_BOUNDARY 32 + +/* Unused by cil32 machine */ +#define STACK_BOUNDARY 64 + +/* Unused by cil32 machine */ +#define FUNCTION_BOUNDARY 16 + +#define BIGGEST_ALIGNMENT 64 + +#define STRICT_ALIGNMENT 1 + +#define PCC_BITFIELD_TYPE_MATTERS 1 + +#define MAX_FIXED_MODE_SIZE 64 + + +/* Node: Type Layout */ + +/* Note that DOUBLE_TYPE_SIZE is not defined anymore, since the default + value gives a 64-bit double, which is what we now use. */ + +/* For compatibility and historical reasons, a char should be signed. */ +#define DEFAULT_SIGNED_CHAR 1 + +/* Note that WCHAR_TYPE_SIZE is used in cexp.y, + where TARGET_SHORT is not available. */ +#undef WCHAR_TYPE +#define WCHAR_TYPE "int" + + +/* Node: Register Basics */ + +#define FIRST_PSEUDO_REGISTER (1) +#define FIXED_REGISTERS {0} +#define CALL_USED_REGISTERS {0} + +/* Node: Allocation Order */ + +#define REG_ALLOC_ORDER {0} + + +/* Node: Values in Registers */ + +/* The VOIDmode test is so we can omit mode on anonymous insns. FIXME: + Still needed in 2.9x, at least for Axis-20000319. */ +#define HARD_REGNO_NREGS(REGNO, MODE) \ + (MODE == VOIDmode \ + ? 1 : ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)) + +#define HARD_REGNO_MODE_OK(REGNO, MODE) 1 + +#define MODES_TIEABLE_P(MODE1, MODE2) 1 + + +/* Node: Leaf Functions */ +/* (no definitions) */ + +/* Node: Stack Registers */ +/* (no definitions) */ + + +/* Node: Register Classes */ + +enum reg_class + { + NO_REGS, + GENERAL_REGS, ALL_REGS, + LIM_REG_CLASSES + }; + +#define N_REG_CLASSES (int) LIM_REG_CLASSES + +#define REG_CLASS_NAMES \ + {"NO_REGS", \ + "GENERAL_REGS", "ALL_REGS"} + +/* Count in the faked argument register in GENERAL_REGS. Keep out SRP. */ +#define REG_CLASS_CONTENTS \ + { \ + {0}, \ + {1}, \ + {1} \ + } + +#define REGNO_REG_CLASS(REGNO) GENERAL_REGS + +#define BASE_REG_CLASS GENERAL_REGS + +#define INDEX_REG_CLASS GENERAL_REGS + +#define REG_CLASS_FROM_LETTER(C) NO_REGS + +#define REGNO_OK_FOR_BASE_P(REGNO) \ + ((REGNO) == CIL32_FAKE_REG) + +#define REGNO_OK_FOR_INDEX_P(REGNO) REGNO_OK_FOR_BASE_P(REGNO) + +#define PREFERRED_RELOAD_CLASS(X, CLASS) GENERAL_REGS + +#define SECONDARY_RELOAD_CLASS(CLASS, MODE, X) NO_REGS + +#define CLASS_MAX_NREGS(CLASS, MODE) \ + ((MODE) == VOIDmode \ + ? 1 \ + : ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)) + +#define CONST_OK_FOR_CONSTRAINT_P(VALUE, C, STR) 0 + +#define CONST_DOUBLE_OK_FOR_CONSTRAINT_P(VALUE, C, STR) 0 + +/* We want the unary operators visible for constraint checking. + (will always be the case in 4.2). */ +#define KEEP_UNARY_OPERATORS_AT_CONSTRAINT_CHECKING + + +/* Node: Frame Layout */ + +#define STACK_GROWS_DOWNWARD +#define FRAME_GROWS_DOWNWARD 1 + +/* It seems to be indicated in the code (at least 2.1) that this is + better a constant, and best 0. */ +#define STARTING_FRAME_OFFSET 0 + +#define FIRST_PARM_OFFSET(FNDECL) 0 + +#define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (Pmode, CIL32_FAKE_REG) + +#define EH_RETURN_DATA_REGNO(N) CIL32_FAKE_REG + +#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (SImode, CIL32_FAKE_REG) + +#define DWARF_FRAME_RETURN_COLUMN DWARF_FRAME_REGNUM (CIL32_FAKE_REG) + +/* FIXME: Move this to right node (it's not documented properly yet). + FIXME: Check what alignment we can assume regarding + TARGET_STACK_ALIGN and TARGET_ALIGN_BY_32. */ +#define DWARF_CIE_DATA_ALIGNMENT -1 + +/* If we would ever need an exact mapping between canonical register + number and dwarf frame register, we would either need to include all + registers in the gcc description (with some marked fixed of course), or + an inverse mapping from dwarf register to gcc register. There is one + need in dwarf2out.c:expand_builtin_init_dwarf_reg_sizes. Right now, I + don't see that we need exact correspondence between DWARF *frame* + registers and DBX_REGISTER_NUMBER, so map them onto GCC registers. */ +#define DWARF_FRAME_REGNUM(REG) (REG) + +/* Node: Stack Checking */ +/* (no definitions) */ + +/* Node: Frame Registers */ + +#define STACK_POINTER_REGNUM CIL32_FAKE_REG + +/* Register used for frame pointer. This is also the last of the saved + registers, when a frame pointer is not used. */ +#define FRAME_POINTER_REGNUM CIL32_FAKE_REG + +/* Faked register, is always eliminated. We need it to eliminate + allocating stack slots for the return address and the frame pointer. */ +#define ARG_POINTER_REGNUM CIL32_FAKE_REG + +#define STATIC_CHAIN_REGNUM CIL32_FAKE_REG + + +/* Node: Elimination */ + +/* Really only needed if the stack frame has variable length (alloca + or variable sized local arguments (GNU C extension). */ +#define FRAME_POINTER_REQUIRED 0 + +#define INITIAL_FRAME_POINTER_OFFSET(n) \ + do { n = 0; } while(0) + +/* We need not worry about when the frame-pointer is required for other + reasons. */ +#define CAN_ELIMINATE(FROM, TO) 1 + +#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) (gcc_unreachable ()) + + +/* Node: Stack Arguments */ + +#define ACCUMULATE_OUTGOING_ARGS 1 + +#define RETURN_POPS_ARGS(FUNDECL, FUNTYPE, STACKSIZE) 0 + + +/* Node: Register Arguments */ + +/* The void_type_node is sent as a "closing" call. */ +#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ + (NULL_RTX) + +/* This no longer *needs* to be a structure; but keeping it as such should + not hurt (and hacking the ABI is simpler). */ +#define CUMULATIVE_ARGS struct cum_args +struct cum_args {int regs;}; + +/* The regs member is an integer, the number of arguments got into + registers so far. */ +#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, FNDECL, N_NAMED_ARGS) \ + ((CUM).regs = 0) + +#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ + ((CUM).regs += 1) + +#define FUNCTION_ARG_REGNO_P(REGNO) 0 + + +/* Node: Scalar Return */ + +#define FUNCTION_VALUE(VALTYPE, FUNC) \ + gen_rtx_REG (TYPE_MODE (VALTYPE), CIL32_FAKE_REG) + +#define LIBCALL_VALUE(MODE) gen_rtx_REG (MODE, CIL32_FAKE_REG) + +#define FUNCTION_VALUE_REGNO_P(N) ((N) == CIL32_FAKE_REG) + + +/* Node: Aggregate Return */ +/* (no definitions) */ + +/* Node: Caller Saves */ +/* (no definitions) */ + +/* Node: Function entry */ +/* (no definitions) */ + +/* Node: Profiling */ + +#define FUNCTION_PROFILER(FILE, LABELNO) \ + error ("no FUNCTION_PROFILER for CIL32 yet") + +/* FIXME: Some of the undefined macros might be mandatory. If so, fix + documentation. */ + + +/* Node: Trampolines */ +/* TODO */ + +#define TRAMPOLINE_SIZE 0 + +#define TRAMPOLINE_ALIGNMENT 0 + +#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \ + do {} while(0) + +/* Node: Library Calls */ + +/* If you change this, you have to check whatever libraries and systems + that use it. */ +#define TARGET_EDOM 33 + + +/* Node: Addressing Modes */ + +#define CONSTANT_ADDRESS_P(X) CONSTANT_P (X) + +#define MAX_REGS_PER_ADDRESS 2 + +#define CONSTANT_INDEX_P(X) \ + (CONSTANT_P (X)) + +#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ + { \ + if (REG_P (X)) \ + goto ADDR; \ + if (CONSTANT_INDEX_P (X)) \ + goto ADDR; \ + if (GET_CODE (X) == PLUS) \ + { \ + rtx x1 = XEXP (X, 0); \ + rtx x2 = XEXP (X, 1); \ + /* BDAP o, Rd. */ \ + if ((REG_P (x1) || CONSTANT_INDEX_P (x1)) \ + && (REG_P (x2) && CONSTANT_INDEX_P (x2))) \ + goto ADDR; \ + } \ + } + +#ifndef REG_OK_STRICT + /* Nonzero if X is a hard reg that can be used as a base reg + or if it is a pseudo reg. */ +# define REG_OK_FOR_BASE_P(X) 1 +#else + /* Nonzero if X is a hard reg that can be used as a base reg. */ +# define REG_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_P (REGNO (X)) +#endif + +#ifndef REG_OK_STRICT + /* Nonzero if X is a hard reg that can be used as an index + or if it is a pseudo reg. */ +# define REG_OK_FOR_INDEX_P(X) REG_OK_FOR_BASE_P (X) +#else + /* Nonzero if X is a hard reg that can be used as an index. */ +# define REG_OK_FOR_INDEX_P(X) REGNO_OK_FOR_INDEX_P (REGNO (X)) +#endif + +#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR, LABEL) \ + do {} while(0) + +#define LEGITIMATE_CONSTANT_P(X) 1 + + +/* Node: Condition Code */ + +#define NOTICE_UPDATE_CC(EXP, INSN) (gcc_unreachable ()) + + +/* Node: Costs */ + +#define SLOW_BYTE_ACCESS 0 + + +/* Node: Sections */ + +#define TEXT_SECTION_ASM_OP "" + +#define DATA_SECTION_ASM_OP "" + + +/* Node: PIC */ +/* (no definitions) */ + +/* Node: File Framework */ + +#undef ASM_OUTPUT_SKIP +#define ASM_OUTPUT_SKIP(FILE, SIZE) \ + do {} while(0) + +#define ASM_APP_ON "" +#define ASM_APP_OFF "" + + +/* Node: Data Output */ + +#define IS_ASM_LOGICAL_LINE_SEPARATOR(C) (C) == '@' + +/* Node: Uninitialized Data */ + +#define ASM_OUTPUT_ALIGNED_DECL_COMMON(FILE, DECL, NAME, SIZE, ALIGN) \ + do {} while(0) + +#define ASM_OUTPUT_ALIGNED_DECL_LOCAL(FILE, DECL, NAME, SIZE, ALIGN) \ + do {} while(0) + +/* Node: Label Output */ + +/* Globalizing directive for a label. */ +#define GLOBAL_ASM_OP "\t.global " + +#define SUPPORTS_WEAK 1 + +/* Remove any previous definition (elfos.h). */ +#undef ASM_GENERATE_INTERNAL_LABEL +#define ASM_GENERATE_INTERNAL_LABEL(LABEL, PREFIX, NUM) \ + sprintf (LABEL, "*%s%s%ld", LOCAL_LABEL_PREFIX, PREFIX, (long) NUM) + +/* Node: Initialization */ +/* (no definitions) */ + +/* Node: Macros for Initialization */ +/* (no definitions) */ + +/* Node: Instruction Output */ + +#define REGISTER_NAMES {"fake"} + +#define PRINT_OPERAND(FILE, X, CODE) (gcc_unreachable ()) + +/* For delay-slot handling. */ +#define PRINT_OPERAND_PUNCT_VALID_P(CODE) 0 + +#define PRINT_OPERAND_ADDRESS(FILE, ADDR) (gcc_unreachable ()) + +#define LOCAL_LABEL_PREFIX "." + +#define REGISTER_PREFIX "$" + +#define ASM_OUTPUT_REG_PUSH(FILE, REGNO) (gcc_unreachable ()) +#define ASM_OUTPUT_REG_POP(FILE, REGNO) (gcc_unreachable ()) + + +/* Node: Dispatch Tables */ +/* (no definitions) */ + +/* Node: Exception Region Output */ +/* (no definitions) */ + +/* Node: Alignment Output */ + +/* Node: All Debuggers */ + +#define DBX_REGISTER_NUMBER(REGNO) (REGNO) + + +/* Node: DBX Options */ +/* (no definitions) */ + +/* Node: DBX Hooks */ +/* (no definitions) */ + +/* Node: File names and DBX */ +/* (no definitions) */ + + +/* Node: SDB and DWARF */ +/* (no definitions) */ + +/* Node: Misc */ + +#define CASE_VECTOR_MODE HImode + +/* The maximum number of bytes that a single instruction can move quickly + between memory and registers or between two memory locations. */ +#define MOVE_MAX 8 +#define MOVE_MAX_PIECES 4 + +/* Determine whether to use move_by_pieces or block move insn. */ +#define MOVE_BY_PIECES_P(SIZE, ALIGN) \ + ( (SIZE) == 1 || (SIZE) == 2 || (SIZE) == 4 || (SIZE) == 8 ) + +/* Determine whether to use clear_by_pieces or block clear insn. */ +#define CLEAR_BY_PIECES_P(SIZE, ALIGN) MOVE_BY_PIECES_P (SIZE, ALIGN) + +/* This macro is used to determine whether store_by_pieces should be + called to "memset" storage with byte values other than zero, or + to "memcpy" storage when the source is a constant string. */ +#define STORE_BY_PIECES_P(SIZE, ALIGN) MOVE_BY_PIECES_P (SIZE, ALIGN) + +#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 + +#define Pmode SImode + +#define FUNCTION_MODE QImode + +#define NO_IMPLICIT_EXTERN_C + +extern struct tree_opt_pass pass_gen_cil; +extern struct tree_opt_pass pass_simp_cil; + +/* cil32 builtin ID */ +enum cil32_builtin +{ + CIL32_BUILTIN_VA_ARG +}; + +/* + * Local variables: + * eval: (c-set-style "gnu") + * indent-tabs-mode: t + * End: + */ diff --git a/gcc/config/cil32/cil32.md b/gcc/config/cil32/cil32.md new file mode 100644 index 00000000000..6b6587ff76b --- /dev/null +++ b/gcc/config/cil32/cil32.md @@ -0,0 +1,211 @@ +;; GCC machine description for cil32. +;; +;; Copyright (C) 2006 Free Software Foundation, Inc. +;; +;; This file is part of GCC. +;; +;; 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 2, 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 COPYING. If not, write to the Free +;; Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +;; 02110-1301, USA. +;; +;; Authors: +;; Andrea Bona +;; Andrea Ornstein +;; Erven Rohou +;; Roberto Costa +;; +;; Contact information at STMicroelectronics: +;; Roberto Costa + +;; See files "md.texi" and "rtl.def" for documentation on define_insn, +;; match_*, et. al. + +;; Register numbers. +(define_constants + [(CIL32_FAKE_REG 0)] +) + + +(define_mode_macro ALLMODES [SI HI QI DI SF DF]) +(define_mode_macro ALLEVALMODES [SI DI SF DF]) +(define_mode_macro ALLINTEVALMODES [SI DI]) + +(define_mode_macro VECMODES [V8QI V4HI V2SI V4QI V2HI]) + + +;; Jump and branch insns. + +(define_insn "jump" + [(set (pc) + (label_ref (match_operand 0 "" "")))] + "1" + "" + []) + +(define_insn "indirect_jump" + [(set (pc) (match_operand:SI 0 "nonimmediate_operand" "rm"))] + "" + "") + +;; Move insns. + +(define_insn "mov" + [(set (match_operand:ALLMODES 0 "nonimmediate_operand" "") + (match_operand:ALLMODES 1 "general_operand" ""))] + "" + "") + +;; Move for vectors +(define_insn "mov" + [(set (match_operand:VECMODES 0 "nonimmediate_operand" "") + (match_operand:VECMODES 1 "general_operand" ""))] + "" + "") + + +;; Add insns. + +(define_insn "add3" + [(set (match_operand:ALLEVALMODES 0 "nonimmediate_operand" "") + (plus:ALLEVALMODES + (match_operand:ALLEVALMODES 1 "general_operand" "") + (match_operand:ALLEVALMODES 2 "general_operand" "")))] + "" + "add") + +(define_insn "sub3" + [(set (match_operand:ALLEVALMODES 0 "nonimmediate_operand" "") + (minus:ALLEVALMODES + (match_operand:ALLEVALMODES 1 "general_operand" "") + (match_operand:ALLEVALMODES 2 "general_operand" "")))] + "" + "sub") + +(define_insn "mul3" + [(set (match_operand:ALLEVALMODES 0 "nonimmediate_operand" "") + (mult:ALLEVALMODES + (match_operand:ALLEVALMODES 1 "general_operand" "") + (match_operand:ALLEVALMODES 2 "general_operand" "")))] + "" + "mul") + +(define_insn "div3" + [(set (match_operand:ALLEVALMODES 0 "nonimmediate_operand" "") + (div:ALLEVALMODES + (match_operand:ALLEVALMODES 1 "general_operand" "") + (match_operand:ALLEVALMODES 2 "general_operand" "")))] + "" + "div") + +(define_insn "udiv3" + [(set (match_operand:ALLEVALMODES 0 "nonimmediate_operand" "") + (udiv:ALLEVALMODES + (match_operand:ALLEVALMODES 1 "general_operand" "") + (match_operand:ALLEVALMODES 2 "general_operand" "")))] + "" + "div.un") + +(define_insn "mod3" + [(set (match_operand:ALLEVALMODES 0 "nonimmediate_operand" "") + (mod:ALLEVALMODES + (match_operand:ALLEVALMODES 1 "general_operand" "") + (match_operand:ALLEVALMODES 2 "general_operand" "")))] + "" + "rem") + +(define_insn "umod3" + [(set (match_operand:ALLEVALMODES 0 "nonimmediate_operand" "") + (umod:ALLEVALMODES + (match_operand:ALLEVALMODES 1 "general_operand" "") + (match_operand:ALLEVALMODES 2 "general_operand" "")))] + "" + "rem.un") + +(define_insn "and3" + [(set (match_operand:ALLINTEVALMODES 0 "nonimmediate_operand" "") + (and:ALLINTEVALMODES + (match_operand:ALLINTEVALMODES 1 "general_operand" "") + (match_operand:ALLINTEVALMODES 2 "general_operand" "")))] + "" + "and") + +;; and works in SIMD mode +(define_insn "and3" + [(set (match_operand:VECMODES 0 "nonimmediate_operand" "") + (and:VECMODES + (match_operand:VECMODES 1 "general_operand" "") + (match_operand:VECMODES 2 "general_operand" "")))] + "" + "and") + +(define_insn "ior3" + [(set (match_operand:ALLINTEVALMODES 0 "nonimmediate_operand" "") + (ior:ALLINTEVALMODES + (match_operand:ALLINTEVALMODES 1 "general_operand" "") + (match_operand:ALLINTEVALMODES 2 "general_operand" "")))] + "" + "or") + +;; or works in SIMD mode +(define_insn "ior3" + [(set (match_operand:VECMODES 0 "nonimmediate_operand" "") + (ior:VECMODES + (match_operand:VECMODES 1 "general_operand" "") + (match_operand:VECMODES 2 "general_operand" "")))] + "" + "or") + +(define_insn "xor3" + [(set (match_operand:ALLINTEVALMODES 0 "nonimmediate_operand" "") + (xor:ALLINTEVALMODES + (match_operand:ALLINTEVALMODES 1 "general_operand" "") + (match_operand:ALLINTEVALMODES 2 "general_operand" "")))] + "" + "xor") + +;; xor works in SIMD mode +(define_insn "xor3" + [(set (match_operand:VECMODES 0 "nonimmediate_operand" "") + (xor:VECMODES + (match_operand:VECMODES 1 "general_operand" "") + (match_operand:VECMODES 2 "general_operand" "")))] + "" + "xor") + + +;; Call insns. + +(define_insn "call" + [(call (match_operand 0 "general_operand" "") + (match_operand 1 "general_operand" ""))] + "" + "") + +(define_insn "call_value" + [(set (match_operand 0 "" "") + (call (match_operand 1 "general_operand" "") + (match_operand 2 "general_operand" "")))] + "" + "") + +;; Local variables: +;; mode:emacs-lisp +;; comment-start: ";; " +;; eval: (set-syntax-table (copy-sequence (syntax-table))) +;; eval: (modify-syntax-entry ?[ "(]") +;; eval: (modify-syntax-entry ?] ")[") +;; eval: (modify-syntax-entry ?{ "(}") +;; eval: (modify-syntax-entry ?} "){") +;; eval: (setq indent-tabs-mode t) +;; End: diff --git a/gcc/config/cil32/cil32.opt b/gcc/config/cil32/cil32.opt new file mode 100644 index 00000000000..79fdcc0291d --- /dev/null +++ b/gcc/config/cil32/cil32.opt @@ -0,0 +1,53 @@ +; Options for the CIL32 port of the compiler. +; +; Copyright (C) 2006 Free Software Foundation, Inc. +; +; This file is part of GCC. +; +; 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 2, 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 COPYING. If not, write to the Free +; Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +; 02110-1301, USA. +; +; Authors: +; Andrea Bona +; Andrea Ornstein +; Erven Rohou +; Roberto Costa +; +; Contact information at STMicroelectronics: +; Roberto Costa + +mopensystemc +Target Undocumented Mask(OPENSYSTEMC) +Use OpenSystem.C Attributes + +mnoopensystemc +Target InverseMask(OPENSYSTEMC) +Disable use of OpenSystem.C Attributes + +mexpand-abs +Target Mask(EXPAND_ABS) +Expand abs (absolute value) instead of generating a call to Crt::abs + +mexpand-minmax +Target Mask(EXPAND_MINMAX) +Expand min and max instead of generating a call to Crt::{min,max} + +memit-gimple-comments +Target Mask(EMIT_GIMPLE_COMMENTS) +Emits the GIMPLE node as a CIL comment before the CIL sequence + +memit-external-assembly +Target Mask(EMIT_EXTERNAL_ASSEMBLY) +Emits all external symbols as if defined in an assembly called ExternalAssembly diff --git a/gcc/config/cil32/gcc4net.cs b/gcc/config/cil32/gcc4net.cs new file mode 100644 index 00000000000..fff17f545a9 --- /dev/null +++ b/gcc/config/cil32/gcc4net.cs @@ -0,0 +1,106 @@ +/* Run-time support required by CIL binaries. + + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GCC. + +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 2, 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 COPYING. If not, write to the Free +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. + +Authors: + Andrea Bona + Andrea Ornstein + Erven Rohou + Roberto Costa + +Contact information at STMicroelectronics: +Roberto Costa */ + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace gcc4net { + public unsafe sealed class StartupHelper { + + public unsafe static IntPtr GetArgv(out int argc) { + String[] argv = Environment.GetCommandLineArgs(); + argc = argv.Length; + int ptr_size = (int)(sizeof(void*)); + int size = (argc + 1) * ptr_size; + IntPtr res = Marshal.AllocHGlobal(size); + + for (int i=0; i < argc; ++i) { + Marshal.WriteIntPtr(res, ptr_size * i, + Marshal.StringToHGlobalAnsi(argv[i])); + } + Marshal.WriteIntPtr(res, ptr_size * argc, IntPtr.Zero); + + return res; + } + + public unsafe static void Startup() { + Module mainModule; + Assembly assembly; + Type type; + + // Find the module that contains the "main" function. + mainModule = null; + assembly = Assembly.GetEntryAssembly(); + type = assembly.GetType(""); + // Invoke the application's ".init" function, if present. + MethodInfo initMethod = type.GetMethod(".init"); + if(initMethod != null) + initMethod.Invoke(null, null); + } + + public unsafe static void Shutdown(int status) { + } + + } + + public sealed class Crt { + public static int __minsi3(int a, int b) { return (a<=b) ? a : b; } + public static long __minti3(long a, long b) { return (a<=b) ? a : b; } + public static uint __uminsi3(uint a, uint b) { return (a<=b) ? a : b; } + public static ulong __uminti3(ulong a, ulong b) { return (a<=b) ? a : b; } + public static float __minsf3(float a, float b) { return (a<=b) ? a : b; } + public static double __mindf3(double a, double b) { return (a<=b) ? a : b; } + + public static int __maxsi3(int a, int b) { return (a>=b) ? a : b; } + public static long __maxti3(long a, long b) { return (a>=b) ? a : b; } + public static uint __umaxsi3(uint a, uint b) { return (a>=b) ? a : b; } + public static ulong __umaxti3(ulong a, ulong b) { return (a>=b) ? a : b; } + public static float __maxsf3(float a, float b) { return (a>=b) ? a : b; } + public static double __maxdf3(double a, double b) { return (a>=b) ? a : b; } + + public static int __abssi2(int a) { return (a>=0) ? a : -a; } + public static long __absti2(long a) { return (a>=0) ? a : -a; } + public static float __abssf2(float a) { return (a>=0) ? a : -a; } + public static double __absdf2(double a) { return (a>=0) ? a : -a; } + + unsafe public static void memset(void* dest , byte val, int len) { + byte* ptr = (byte*)dest; + while (len-- > 0) + *ptr++ = val; + } + unsafe public static void memcpy (void* dest, void* src, int len) { + byte* d = (byte*)dest; + byte* s = (byte*)src; + while (len-- > 0) + *d++ = *s++; + } + } +} diff --git a/gcc/config/cil32/gen-cil.c b/gcc/config/cil32/gen-cil.c new file mode 100644 index 00000000000..0f74ce6ac88 --- /dev/null +++ b/gcc/config/cil32/gen-cil.c @@ -0,0 +1,3562 @@ +/* Dump of the GENERIC trees in CIL. + + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GCC. + +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 2, 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 COPYING. If not, write to the Free +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. + +Authors: + Andrea Bona + Andrea Ornstein + Erven Rohou + Roberto Costa + +Contact information at STMicroelectronics: +Roberto Costa */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "diagnostic.h" +#include "real.h" +#include "hashtab.h" +#include "tree-flow.h" +#include "langhooks.h" +#include "tree-iterator.h" +#include "tree-chrec.h" +#include "tree-pass.h" +#include "timevar.h" +#include "assert.h" +#include "toplev.h" +#include "pointer-set.h" +#include "output.h" +#include "varray.h" +#include "ggc.h" +#include "tree-simp-cil.h" +#include "gen-cil.h" + +/* Nonzero for a type which is at file scope. */ +#define TYPE_FILE_SCOPE_P(EXP) \ + (! TYPE_CONTEXT (EXP) \ + || TREE_CODE (TYPE_CONTEXT (EXP)) == TRANSLATION_UNIT_DECL) + +struct fnct_attr { + const char *assembly_name; + const char *cil_name; + const char *pinvoke_assembly; + const char *pinvoke_fname; +}; + +/* Local functions, macros and variables. */ +static void decode_function_attrs (tree, struct fnct_attr *); +static void mark_var_defs_uses (tree); +static void remove_stloc_ldloc (block_stmt_iterator, tree *, bool *); +static void mark_referenced_type (tree); +static void mark_referenced_string (tree); +static void dump_decl_name (FILE *, tree); +static void dump_string_name (FILE *, tree); +static void dump_label_name (FILE *, tree); +static void dump_fun_type (FILE *, tree, tree, const char *, bool); +static void dump_valuetype_name (FILE *, tree); +static void compute_addr_expr (FILE *, tree); +static void print_type_suffix (FILE *, tree, tree, bool); +static void gen_cil_modify_expr (FILE *, tree); +static char * append_string (char *, const char *, + unsigned int *, unsigned int *); +static char * append_coded_type (char *, tree, unsigned int *, unsigned int *); +static char * get_md5 (const char *, size_t, size_t *); +static tree make_valuetype_identifier (tree); +static void print_valuetype_decl (FILE *, tree); +static void dump_type (FILE *, tree, bool); +static void dump_type_promotion (FILE *, tree, bool); +static void dump_type_promoted_type_def (FILE *, tree, bool); +static void dump_type_for_builtin (FILE *, tree, bool); +static void dump_type_eval_mode (FILE *, tree, bool); + +static void stack_set (unsigned int) ATTRIBUTE_UNUSED; +static void stack_reset (void); +static void stack_push (unsigned int); +static void stack_pop (unsigned int); +static void gen_start_function (FILE *); +static unsigned int gen_cil (void); +static void gen_cil_1 (FILE *); +static void gen_cil_node (FILE *, tree); +static bool gen_cil_gate (void); +static void create_init_method(tree); + +static struct pointer_set_t *defd_vars; +static struct pointer_set_t *defd_more_than_once_vars; +static struct pointer_set_t *used_vars; +static struct pointer_set_t *used_more_than_once_vars; +static struct pointer_set_t *useless_vars; +static struct pointer_set_t *referenced_types; +static GTY(()) varray_type referenced_strings; +static struct pointer_set_t *referenced_string_ptrs; +static struct pointer_set_t *referenced_pinvoke; +static GTY(()) varray_type pending_ctors; +static unsigned int stack; +static unsigned int max_stack; + + +/* Recursively mark the definitions and the uses of the non-static + non-volatile local variables used in tree NODE. */ + +static void +mark_var_defs_uses (tree node) +{ + if (node == NULL_TREE) + return; /* ER: was spc */ + + switch (TREE_CODE (node)) + { + case COND_EXPR: + mark_var_defs_uses (COND_EXPR_COND (node)); + break; + + case SWITCH_EXPR: + mark_var_defs_uses (SWITCH_COND (node)); + break; + + case CALL_EXPR: + { + tree args = TREE_OPERAND (node, 1); + + mark_var_defs_uses (TREE_OPERAND (node, 0)); + + while (args) + { + mark_var_defs_uses (TREE_VALUE (args)); + args = TREE_CHAIN (args); + } + } + break; + + case MULT_EXPR: + case PLUS_EXPR: + case MINUS_EXPR: + case RDIV_EXPR: + case BIT_IOR_EXPR: + case BIT_XOR_EXPR: + case BIT_AND_EXPR: + case TRUTH_AND_EXPR: + case TRUTH_OR_EXPR: + case TRUTH_XOR_EXPR: + case LT_EXPR: + case GT_EXPR: + case EQ_EXPR: + case NE_EXPR: + case LE_EXPR: + case GE_EXPR: + case EXACT_DIV_EXPR: + case FLOOR_DIV_EXPR: + case TRUNC_DIV_EXPR: + case TRUNC_MOD_EXPR: + case LSHIFT_EXPR: + case RSHIFT_EXPR: + case MAX_EXPR: + case MIN_EXPR: + case UNORDERED_EXPR: + case ORDERED_EXPR: + mark_var_defs_uses (TREE_OPERAND (node, 0)); + mark_var_defs_uses (TREE_OPERAND (node, 1)); + break; + + case INIT_EXPR: + case MODIFY_EXPR: + { + tree lhs = TREE_OPERAND (node, 0); + tree rhs = TREE_OPERAND (node, 1); + + if (TREE_CODE (lhs) == VAR_DECL) + { + if (! TREE_ADDRESSABLE (lhs) + && ! TREE_STATIC (lhs) + && ! TREE_THIS_VOLATILE (lhs) + && ! DECL_FILE_SCOPE_P (lhs)) + { + if (!pointer_set_contains (defd_vars, lhs)) + pointer_set_insert (defd_vars, lhs); + else if (!pointer_set_contains (defd_more_than_once_vars, lhs)) + pointer_set_insert (defd_more_than_once_vars, lhs); + } + } + else + mark_var_defs_uses (lhs); + + mark_var_defs_uses (rhs); + gcc_assert (TREE_CODE (rhs) != CONSTRUCTOR + && TREE_CODE (rhs) != STRING_CST); + } + break; + + case NEGATE_EXPR: + case BIT_NOT_EXPR: + case TRUTH_NOT_EXPR: + case CONVERT_EXPR: + case NOP_EXPR: + case FLOAT_EXPR: + case FIX_TRUNC_EXPR: + case INDIRECT_REF: + case BIT_FIELD_REF: + case ADDR_EXPR: + case COMPONENT_REF: + case ABS_EXPR: + case RETURN_EXPR: + case WITH_SIZE_EXPR: + mark_var_defs_uses (TREE_OPERAND (node, 0)); + break; + + case INTEGER_CST: + case REAL_CST: + case STRING_CST: + case VECTOR_CST: + case PARM_DECL: + case FUNCTION_DECL: + case LABEL_DECL: + case LABEL_EXPR: + case GOTO_EXPR: + case ASM_EXPR: + break; + + case VAR_DECL: + if (! TREE_ADDRESSABLE (node) + && ! TREE_STATIC (node) + && ! TREE_THIS_VOLATILE (node) + && ! DECL_FILE_SCOPE_P (node)) + { + if (! pointer_set_contains (used_vars, node)) + pointer_set_insert (used_vars, node); + else if (! pointer_set_contains (used_more_than_once_vars, node)) + pointer_set_insert (used_more_than_once_vars, node); + } + break; + + default: + fprintf (stderr, "mark_var_defs_uses: Cannot handle "); + debug_generic_expr (node); + gcc_assert (0); + } +} + +/* Avoids couple of useless stloc - ldloc from being emitted by removing + non-static non-volatile local variables used exactly once + throughout the entire function from tree NODE_PTR. + BSI is the statement iterator pointing to the statement containing NODE_PTR. + The bool pointed by MOD is set if the function changed anything, + otherwise its value is not modified. + This function works recursively, looking for VAR_DECLs corresponding + to local non-static non-volatile local variables used exactly once + in the function. + When such a variable is found, if its only definition is in + the statement immediately preceding BSI, then the use of the variable is + replaced by its definition (and that statement is removed). + By doing so, the code exits GIMPLE form; however, the CIL emission + mechanism is able to handle such a hybrid GIMPLE representation. */ + +static void +remove_stloc_ldloc (block_stmt_iterator bsi, tree *node_ptr, bool *mod) +{ + tree node = *node_ptr; + block_stmt_iterator prev_bsi; + + if (node == NULL_TREE) + return; /* ER: was spc */ + + /* Get iterator for the previous statememt */ + prev_bsi = bsi; + bsi_prev (&prev_bsi); + + /* No remotion is possible if the statement is the first in the block */ + if (bsi_end_p (prev_bsi)) + return; + + switch (TREE_CODE (node)) + { + case COND_EXPR: + remove_stloc_ldloc (bsi, &COND_EXPR_COND (node), mod); + break; + + case SWITCH_EXPR: + remove_stloc_ldloc (bsi, &SWITCH_COND (node), mod); + break; + + case CALL_EXPR: + { + tree args = TREE_OPERAND (node, 1); + + remove_stloc_ldloc (bsi, &TREE_OPERAND (node, 0), mod); + + while (args) + { + remove_stloc_ldloc (bsi, &TREE_VALUE (args), mod); + args = TREE_CHAIN (args); + } + } + break; + + case MULT_EXPR: + case PLUS_EXPR: + case MINUS_EXPR: + case RDIV_EXPR: + case BIT_IOR_EXPR: + case BIT_XOR_EXPR: + case BIT_AND_EXPR: + case TRUTH_AND_EXPR: + case TRUTH_OR_EXPR: + case TRUTH_XOR_EXPR: + case LT_EXPR: + case GT_EXPR: + case EQ_EXPR: + case NE_EXPR: + case LE_EXPR: + case GE_EXPR: + case EXACT_DIV_EXPR: + case FLOOR_DIV_EXPR: + case TRUNC_DIV_EXPR: + case TRUNC_MOD_EXPR: + case LSHIFT_EXPR: + case RSHIFT_EXPR: + case MAX_EXPR: + case MIN_EXPR: + case UNORDERED_EXPR: + case ORDERED_EXPR: + remove_stloc_ldloc (bsi, &TREE_OPERAND (node, 0), mod); + remove_stloc_ldloc (bsi, &TREE_OPERAND (node, 1), mod); + break; + + case INIT_EXPR: + case MODIFY_EXPR: + if (! AGGREGATE_TYPE_P (TREE_TYPE (TREE_OPERAND (node, 0)))) + remove_stloc_ldloc (bsi, &TREE_OPERAND (node, 1), mod); + gcc_assert (TREE_CODE (TREE_OPERAND (node, 1)) != CONSTRUCTOR + && TREE_CODE (TREE_OPERAND (node, 1)) != STRING_CST); + break; + + case NEGATE_EXPR: + case BIT_NOT_EXPR: + case TRUTH_NOT_EXPR: + case CONVERT_EXPR: + case NOP_EXPR: + case FLOAT_EXPR: + case FIX_TRUNC_EXPR: + case BIT_FIELD_REF: + case ABS_EXPR: + case RETURN_EXPR: + case WITH_SIZE_EXPR: + remove_stloc_ldloc (bsi, &TREE_OPERAND (node, 0), mod); + break; + + case ADDR_EXPR: + case COMPONENT_REF: + if (! AGGREGATE_TYPE_P (TREE_TYPE (TREE_OPERAND (node, 0)))) + remove_stloc_ldloc (bsi, &TREE_OPERAND (node, 0), mod); + break; + + case INDIRECT_REF: + if (! AGGREGATE_TYPE_P (TREE_TYPE (node))) + remove_stloc_ldloc (bsi, &TREE_OPERAND (node, 0), mod); + break; + + case VAR_DECL: + if (! TREE_ADDRESSABLE (node) + && ! TREE_STATIC (node) + && ! TREE_THIS_VOLATILE (node) + && ! DECL_FILE_SCOPE_P (node)) + { + tree prev_stmt; + + /* Get the previous statement */ + prev_stmt = bsi_stmt (prev_bsi); + + /* If the variable is used exactly once and the definition + is in the previous statement, then remove it */ + if (TREE_CODE (prev_stmt) == MODIFY_EXPR + && TREE_OPERAND (prev_stmt, 0) == node + && pointer_set_contains (used_vars, node) + && ! pointer_set_contains (used_more_than_once_vars, node)) + { + /* If the definition is unique, set that + the variable has become useless. */ + if (! pointer_set_contains (defd_more_than_once_vars, node)) + pointer_set_insert (useless_vars, node); + + /* Replace variable's use with the definition */ + *node_ptr = TREE_OPERAND (prev_stmt, 1); + + /* Remove the previous statement */ + bsi_remove (&prev_bsi, true); + + /* Set that the function changed something */ + *mod = true; + } + } + break; + + default: + ; + } +} + + +/* stack_* functions are used to keep track of the number of elements + in the evaluation stack, in order to record the maximum number. + This is emitted in .maxstack directive for each function. */ + +/* Set that the evaluation stack contains n elements. */ + +static void +stack_set (unsigned int n) +{ + stack = n; + if (stack > max_stack) + max_stack = stack; +} + +/* Set that the evaluation stack is empty. */ + +static inline void +stack_reset (void) +{ + stack = 0; +} + +/* Add n elements to the evaluation stack. */ + +static void +stack_push (unsigned int n) +{ + stack += n; + if (stack > max_stack) + max_stack = stack; +} + +/* Remove n elements from the evaluation stack. */ + +static inline void +stack_pop (unsigned int n) +{ + gcc_assert (stack >= n); + stack -= n; +} + +/* Given the FUNCTION_DECL tree T, decode its CIL-specific function + attributes and record them in ATTRS. */ + +static void +decode_function_attrs (tree t, struct fnct_attr *attrs) +{ + tree tmp; + + gcc_assert (TREE_CODE (t) == FUNCTION_DECL); + + attrs->assembly_name = 0; + attrs->cil_name = 0; + attrs->pinvoke_assembly = 0; + attrs->pinvoke_fname = 0; + + tmp = DECL_ATTRIBUTES (t); + while (tmp) + { + const char *attr_name = IDENTIFIER_POINTER (TREE_PURPOSE (tmp)); + tree params = TREE_VALUE (tmp); + + if (strcmp (attr_name, "assembly_name") == 0) + attrs->assembly_name = TREE_STRING_POINTER (TREE_VALUE (params)); + else if (strcmp (attr_name, "cil_name") == 0) + attrs->cil_name = TREE_STRING_POINTER (TREE_VALUE (params)); + else if (strcmp (attr_name, "pinvoke") == 0) + { + attrs->pinvoke_assembly = TREE_STRING_POINTER (TREE_VALUE (params)); + if (TREE_CHAIN (params)) + attrs->pinvoke_fname = TREE_STRING_POINTER (TREE_VALUE (TREE_CHAIN (params))); + } + tmp = TREE_CHAIN (tmp); + } +} + +/* Mark the type represented by tree T as referenced. + This function works recursively, since types referenced by type T + itself are also marked as referenced. + Referenced types are emitted at the end of the compilation unit, + non-referenced types are not. + T must be a type node. */ + +static void +mark_referenced_type (tree t) +{ + t = TYPE_MAIN_VARIANT (t); + + /* If the type was already referenced, nothing else to do */ + if (pointer_set_contains (referenced_types, t)) + return; + + /* Give the aggregate a name unless it has it already */ + switch (TREE_CODE (t)) + { + case ENUMERAL_TYPE: + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + case ARRAY_TYPE: + if (TYPE_NAME (t) == 0) + { + tree type_decl = build0 (TYPE_DECL, t); + DECL_NAME (type_decl) = make_valuetype_identifier (t); + TYPE_NAME (t) = type_decl; + } + break; + + default: + /* Nothing to do for the other types */ + ; + } + + /* Transform local-scope types into global-scope types */ + if (!TYPE_FILE_SCOPE_P (t)) + { + tree type_name = TYPE_NAME (t); + const char *orig_name; + size_t tmp_name_max_len = 256; + size_t tmp_name_len = 0; + char *tmp_name; + char suffix[32]; + + gcc_assert (type_name != 0); + gcc_assert (DECL_P (type_name) + || TREE_CODE (type_name) == IDENTIFIER_NODE); + + if (TREE_CODE (type_name) == IDENTIFIER_NODE) + orig_name = IDENTIFIER_POINTER (type_name); + else + orig_name = IDENTIFIER_POINTER (DECL_NAME (type_name)); + + snprintf (suffix, 31, "?vt%lu", (unsigned long)t); + + tmp_name = (char *)xmalloc (tmp_name_max_len); + tmp_name = append_string (tmp_name, orig_name, + &tmp_name_len, &tmp_name_max_len); + tmp_name = append_string (tmp_name, suffix, + &tmp_name_len, &tmp_name_max_len); + + TYPE_NAME (t) = get_identifier_with_length (tmp_name, tmp_name_len); + TYPE_CONTEXT (t) = 0; + free (tmp_name); + } + + switch (TREE_CODE (t)) + { + case ENUMERAL_TYPE: + pointer_set_insert (referenced_types, t); + break; + + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + { + tree tmp; + + pointer_set_insert (referenced_types, t); + tmp = TYPE_FIELDS (t); + + while (tmp) + { + mark_referenced_type (TREE_TYPE (tmp)); + tmp = TREE_CHAIN (tmp); + } + } + break; + + case POINTER_TYPE: + case REFERENCE_TYPE: + mark_referenced_type (TREE_TYPE (t)); + break; + + case ARRAY_TYPE: + pointer_set_insert (referenced_types, t); + mark_referenced_type (TREE_TYPE (t)); + break; + + case FUNCTION_TYPE: + { + tree args_type; + mark_referenced_type (TREE_TYPE (t)); + args_type = TYPE_ARG_TYPES (t); + while (args_type) + { + mark_referenced_type (TREE_VALUE (args_type)); + args_type = TREE_CHAIN (args_type); + } + } + break; + + default: + /* Nothing to do for the other types */ + ; + } +} + +/* Mark the string represented by tree T as referenced. + Referenced strings are emitted at the end of the compilation unit, + non-referenced strings are not. + T must be a STRING_CST node. */ + +static void +mark_referenced_string (tree t) +{ + gcc_assert (TREE_CODE (t) == STRING_CST); + if (!pointer_set_contains (referenced_string_ptrs, + (void *)(TREE_STRING_POINTER (t)))) + { + VARRAY_PUSH_TREE (referenced_strings, t); + pointer_set_insert (referenced_string_ptrs, + (void *)(TREE_STRING_POINTER (t))); + mark_referenced_type (TREE_TYPE (t)); + } +} + +/* Mark the function represented by tree T as a pinvoke. + T must be a FUNCTION_DECL node. */ + +void +cil_add_pinvoke (tree t) +{ + gcc_assert (TREE_CODE (t) == FUNCTION_DECL); + pointer_set_insert (referenced_pinvoke, t); + mark_referenced_type (TREE_TYPE (t)); +} + +/* Dump the name of a _DECL node. */ + +static void +dump_decl_name (FILE* file, tree node) +{ + gcc_assert (DECL_P (node) || TREE_CODE (node) == IDENTIFIER_NODE); + fputs ("'", file); + + if (DECL_ASSEMBLER_NAME_SET_P (node)) + { + fprintf (file, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (node))); + } + else if (TREE_CODE (node) == IDENTIFIER_NODE) + fprintf (file, IDENTIFIER_POINTER (node)); + else if (DECL_NAME (node)) + { + mark_decl_referenced (node); + fprintf (file, IDENTIFIER_POINTER (DECL_NAME (node))); + } + else + fprintf (file, "?UNNAMED%d", DECL_UID (node)); + + fputs ("'", file); +} + +/* Dump the name of a STRING_CST node. */ + +static void +dump_string_name (FILE* file, tree node) +{ + gcc_assert (TREE_CODE (node) == STRING_CST); + + fprintf (file, "'?string%lu'", (unsigned long)TREE_STRING_POINTER (node)); +} + +/* Dump the name of a label. */ + +static void +dump_label_name (FILE* file, tree node) +{ + /* Always print the label id. */ + fprintf (file, "?L" HOST_WIDE_INT_PRINT_DEC, LABEL_DECL_UID (node)); + + /* For convenience, also print the identifier when available. Note that the + identifier alone is incorrect: in case of inlining, several labels can + end up with the same id. */ + if (DECL_NAME (node)) + { + fputs ("_", file); + fprintf (file, IDENTIFIER_POINTER (DECL_NAME (node))); + } +} + +/* Dump the name of a valuetype. + T must be an aggregate type or an enumeral type, since these are + the types emitted as CIL valuetypes. */ + +static void +dump_valuetype_name (FILE *file, tree t) +{ + tree type_name; + + gcc_assert (AGGREGATE_TYPE_P (t) || TREE_CODE (t) == ENUMERAL_TYPE); + gcc_assert (TYPE_MAIN_VARIANT (t) == t); + gcc_assert (TYPE_FILE_SCOPE_P (t)); + gcc_assert (TYPE_NAME (t)); + + type_name = TYPE_NAME (t); + gcc_assert (DECL_P (type_name) || TREE_CODE (type_name) == IDENTIFIER_NODE); + + fputs ("'", file); + if (TREE_CODE (type_name) == IDENTIFIER_NODE) + fprintf (file, IDENTIFIER_POINTER (type_name)); + else if (DECL_NAME (type_name)) + fprintf (file, IDENTIFIER_POINTER (DECL_NAME (type_name))); + else + fprintf (file, "?UNNAMED%d", DECL_UID (type_name)); + fputs ("'", file); +} + +/* Dump the signature of function type FUN_TYPE. + The function name that is dumped is taken from function_decl FUN + or from NAME. Only and exactly one of the two must be non-null. + REF tells whether the function type (and the types of the return value + and of the incoming parameters) have to be marked as referenced. + FUN_TYPE must be a FUNCTION_TYPE. + FUN, if not null, must be a FUNCTION_DECL. */ + +static void +dump_fun_type (FILE *stream, tree fun_type, tree fun, const char *name, bool ref) +{ + tree args_type; + tree last_arg_type = NULL; + bool varargs = FALSE; + + gcc_assert (! (fun && name)); + + mark_referenced_type (fun_type); + + args_type = TYPE_ARG_TYPES (fun_type); + + if (args_type == NULL) + warning (0, + "Missing function %s prototype, guessing it, you should fix the code", + fun?IDENTIFIER_POINTER (DECL_NAME (fun)):""); + else + { + last_arg_type = args_type; + while (TREE_CHAIN (last_arg_type)) + last_arg_type = TREE_CHAIN (last_arg_type); + + if (TREE_VALUE (last_arg_type) != void_type_node) + { + last_arg_type = NULL; + varargs = TRUE; + } + } + + if (varargs) + fputs ("vararg ", stream); + + dump_type (stream, TREE_TYPE (fun_type), ref); + + fputs (" ", stream); + if (fun) + { + struct fnct_attr attrs; + + decode_function_attrs (fun, &attrs); + + if (attrs.assembly_name) + fprintf (stream, "[%s]", attrs.assembly_name); + else if (TARGET_EMIT_EXTERNAL_ASSEMBLY && DECL_EXTERNAL (fun)) + fputs ("[ExternalAssembly]ExternalAssembly::", stream); + + if (attrs.cil_name) + fprintf (stream, "%s", attrs.cil_name); + else + dump_decl_name (stream, fun); + } + + if (name) + fputs (name, stream); + + fputs ("(", stream); + + while (args_type != last_arg_type) + { + dump_type (stream, TREE_VALUE (args_type), ref); + args_type = TREE_CHAIN (args_type); + + if (args_type != last_arg_type) + fputs (", ", stream); + } + + if (varargs) + fputs (", ...", stream); + + fputs (")", stream); +} + +/* Dump type NODE. + REF tells whether the function type (and the types of the return value + and of the incoming parameters) have to be marked as referenced. + NODE must be a type node. */ + +static void +dump_type (FILE *file, tree node, bool ref) +{ +/* node = TYPE_MAIN_VARIANT (node); */ + + if (TYPE_MAIN_VARIANT (node) == va_list_type_node) { + fputs ("valuetype [mscorlib]System.ArgIterator", file); + return; + } + + switch (TREE_CODE (node)) + { + case ENUMERAL_TYPE: + case ARRAY_TYPE: + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + node = TYPE_MAIN_VARIANT (node); + + /* Reference the type if told to do so */ + if (ref) + mark_referenced_type (node); + + /* Print the name of the structure. */ + fputs ("valuetype ", file); + dump_valuetype_name (file, node); + break; + + case VOID_TYPE: + fputs ("void", file); + break; + + case INTEGER_TYPE: + { + int type_size = GET_MODE_BITSIZE (TYPE_MODE (node)); + + if (TYPE_UNSIGNED (node)) + fputs ("unsigned ", file); + + switch (type_size) + { + case 8: fputs ("int8", file); break; + case 16: fputs ("int16", file); break; + case 32: fputs ("int32", file); break; + case 64: fputs ("int64", file); break; + default: + fprintf (stderr, "Unsupported integer size %d\n", type_size); + gcc_assert (0); + } + } + break; + + case REAL_TYPE: + { + int type_size = GET_MODE_BITSIZE (TYPE_MODE (node)); + + switch (type_size) + { + case 32: fputs ("float32", file); break; + case 64: fputs ("float64", file); break; + default: + fprintf (stderr, "Unsupported floating point size %d\n", type_size); + gcc_assert (0); + } + } + break; + + case BOOLEAN_TYPE: + fputs ("int8", file); + break; + + case POINTER_TYPE: + if (TREE_CODE (TREE_TYPE (node)) == FUNCTION_TYPE) + { + fputs ("method ", file); + dump_fun_type (file, TREE_TYPE (node), NULL, " * ", ref); + } + else + { + dump_type (file, TREE_TYPE (node), ref); + fputs (" *", file); + } + break; + + case FUNCTION_TYPE: +/* dump_fun_type (file, node, NULL, NULL, ref); */ + gcc_assert (0); + break; + + case VECTOR_TYPE: + { + int type_size = GET_MODE_BITSIZE (TYPE_MODE (node)); + tree innertype = TREE_TYPE (node); + enum machine_mode innermode = TYPE_MODE (innertype); + + /* Only expect integer vectors */ + gcc_assert (GET_MODE_CLASS (innermode) == MODE_INT); + + /* and emit as corresponding same-size integer mode */ + if (TYPE_UNSIGNED (node)) + fputs ("unsigned ", file); + + switch (type_size) + { + case 8: fputs ("int8", file); break; + case 16: fputs ("int16", file); break; + case 32: fputs ("int32", file); break; + case 64: fputs ("int64", file); break; + default: + fprintf (stderr, "Unsupported integer size %d\n", type_size); + gcc_assert (0); + } + } + break; + + case COMPLEX_TYPE: + case REFERENCE_TYPE: + + default: + fprintf (stderr, "%s: %s\n", __func__, tree_code_name[TREE_CODE (node)]); + gcc_assert (0); + break; + } +} + +/* Dump type NODE, promoted following C conventions for var args. + REF tells whether the function type (and the types of the return value + and of the incoming parameters) have to be marked as referenced. + NODE must be a type node. */ + +static void +dump_type_promotion (FILE *file, tree node, bool ref) +{ + + switch (TREE_CODE (node)) + { + case ARRAY_TYPE: + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + node = TYPE_MAIN_VARIANT (node); + + /* Reference the type if told to do so */ + if (ref) + mark_referenced_type (node); + + /* Print the name of the structure. */ + fputs ("valuetype ", file); + dump_valuetype_name (file, node); + break; + + case ENUMERAL_TYPE: + case INTEGER_TYPE: + { + int type_size = GET_MODE_BITSIZE (TYPE_MODE (node)); + + if (TYPE_UNSIGNED (node)) + { + switch (type_size) + { + case 8: + case 16: fputs ("int32", file); break; + case 32: fputs ("unsigned int32", file); break; + case 64: fputs ("unsigned int64", file); break; + default: + fprintf (stderr, "Unsupported integer size %d\n", type_size); + gcc_assert (0); + } + } + else + { + switch (type_size) + { + case 8: + case 16: + case 32: fputs ("int32", file); break; + case 64: fputs ("int64", file); break; + default: + fprintf (stderr, "Unsupported integer size %d\n", type_size); + gcc_assert (0); + } + } + } + break; + + case REAL_TYPE: + fputs ("float64", file); + break; + + case BOOLEAN_TYPE: + fputs ("int32", file); + break; + + case POINTER_TYPE: + fputs ("native int", file); + break; + + default: + fprintf (stderr, "%s: %s\n", __func__, tree_code_name[TREE_CODE (node)]); + gcc_assert (0); + break; + } +} + +/* Dump the type def of type NODE, promoted following C conventions + for var args. + REF tells whether the function type (and the types of the return value + and of the incoming parameters) have to be marked as referenced. + NODE must be a type node. */ + +static void +dump_type_promoted_type_def (FILE *stream, tree node, bool ref) +{ + switch (TREE_CODE (node)) + { + case ARRAY_TYPE: + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + node = TYPE_MAIN_VARIANT (node); + + /* Reference the type if told to do so */ + if (ref) + mark_referenced_type (node); + + /* Print the name of the structure. */ + fputs ("valuetype ", stream); + dump_valuetype_name (stream, node); + break; + + case ENUMERAL_TYPE: + case INTEGER_TYPE: + { + int type_size = GET_MODE_BITSIZE (TYPE_MODE (node)); + + if (TYPE_UNSIGNED (node)) + { + switch (type_size) + { + case 8: + case 16: fputs ("class [mscorlib]System.Int32", stream); break; + case 32: fputs ("class [mscorlib]System.UInt32", stream); break; + case 64: fputs ("class [mscorlib]System.UInt64", stream); break; + default: + fprintf (stderr, "Unsupported integer size %d\n", type_size); + gcc_assert (0); + } + } + else + { + switch (type_size) + { + case 8: + case 16: + case 32: fputs ("class [mscorlib]System.Int32", stream); break; + case 64: fputs ("class [mscorlib]System.Int64", stream); break; + default: + fprintf (stderr, "Unsupported integer size %d\n", type_size); + gcc_assert (0); + } + } + } + break; + + case REAL_TYPE: + fputs ("class [mscorlib]System.Double", stream); + break; + + case BOOLEAN_TYPE: + fputs ("class [mscorlib]System.Int32", stream); + break; + + case POINTER_TYPE: + fputs ("class [mscorlib]System.IntPtr", stream); + break; + + default: + fprintf (stderr, "%s: %s\n", __func__, tree_code_name[TREE_CODE (node)]); + gcc_assert (0); + break; + } +} + +static void +dump_type_for_builtin (FILE *file, tree node, bool all_types) +{ +/* node = TYPE_MAIN_VARIANT (node); */ + + switch (TREE_CODE (node)) + { + case ENUMERAL_TYPE: + case INTEGER_TYPE: + { + int type_size = GET_MODE_BITSIZE (TYPE_MODE (node)); + + if (TYPE_UNSIGNED (node)) + fputs ("unsigned ", file); + + switch (type_size) + { + case 8: if (all_types) { fputs ("int8", file); return; } + case 16: if (all_types) { fputs ("int16", file); return; } + case 32: fputs ("int32", file); break; + case 64: fputs ("int64", file); break; + default: + fprintf (stderr, "Unsupported integer size %d\n", type_size); + gcc_assert (0); + } + } + break; + + case REAL_TYPE: + { + int type_size = GET_MODE_BITSIZE (TYPE_MODE (node)); + + switch (type_size) + { + case 32: fputs ("float32", file); break; + case 64: fputs ("float64", file); break; + default: + fprintf (stderr, "Unsupported floating point size %d\n", type_size); + gcc_assert (0); + } + } + break; + + default: + fprintf (stderr, "%s: %s\n", __func__, tree_code_name[TREE_CODE (node)]); + gcc_assert (0); + break; + } +} + +static void +dump_type_eval_mode (FILE *stream, tree node, bool all_types) +{ + switch (TREE_CODE (node)) + { + case ENUMERAL_TYPE: + case INTEGER_TYPE: + { + int type_size = GET_MODE_BITSIZE (TYPE_MODE (node)); + + switch (type_size) + { + case 8: if (all_types) { fputs ("qi", stream); break; } + case 16: if (all_types) { fputs ("hi", stream); break; } + case 32: fputs ("si", stream); break; + case 64: fputs ("ti", stream); break; + default: + fprintf (stderr, "Unsupported integer size %d\n", type_size); + gcc_assert (0); + } + } + break; + + case REAL_TYPE: + { + int type_size = GET_MODE_BITSIZE (TYPE_MODE (node)); + + switch (type_size) + { + case 32: fputs ("sf", stream); break; + case 64: fputs ("df", stream); break; + default: + fprintf (stderr, "Unsupported floating point size %d\n", + type_size); + gcc_assert (0); + } + } + break; + + default: + fprintf (stderr, "%s: %s\n", __func__, tree_code_name[TREE_CODE (node)]); + gcc_assert (0); + break; + } +} + +static void +compute_addr_expr (FILE *file, tree t) +{ + switch (TREE_CODE (t)) + { + case STRING_CST: + mark_referenced_string (t); + fputs ("\n\tldsflda\t", file); + dump_type (file, TREE_TYPE (t), true); + fputs (" ", file); + dump_string_name (file, t); + stack_push (1); + break; + + case VAR_DECL: + if (!DECL_FILE_SCOPE_P (t)) + fputs ("\n\tldloca\t", file); + else + { + fputs ("\n\tldsflda\t", file); + if (COMPLETE_TYPE_P (TREE_TYPE (t))) + dump_type (file, TREE_TYPE (t), true); + else + fputs ("native int", file); + fputs (" ", file); + if (TARGET_EMIT_EXTERNAL_ASSEMBLY && DECL_EXTERNAL (t)) + fputs ("[ExternalAssembly]ExternalAssembly::", file); + } + + dump_decl_name (file, t); + stack_push (1); + break; + + case PARM_DECL: + fputs ("\n\tldarga\t", file); + dump_decl_name (file, t); + stack_push (1); + break; + + case FUNCTION_DECL: + fputs ("\n\tldftn\t", file); + dump_fun_type (file, TREE_TYPE (t), t, NULL, true); + stack_push (1); + break; + + case INDIRECT_REF: + gen_cil_node (file, TREE_OPERAND (t, 0)); + break; + + case COMPONENT_REF: + { + tree obj = TREE_OPERAND (t, 0); + tree fld = TREE_OPERAND (t, 1); + tree obj_type = TYPE_MAIN_VARIANT (TREE_TYPE (obj)); + + gcc_assert (! DECL_BIT_FIELD (fld)); + + compute_addr_expr (file, obj); + fputs ("\n\tldflda\t", file); + dump_type (file, TREE_TYPE (fld), true); + fputs (" ", file); + mark_referenced_type (obj_type); + dump_valuetype_name (file, obj_type); + fputs ("::", file); + dump_decl_name (file, fld); + } + break; + + case BIT_FIELD_REF: + { + tree obj = TREE_OPERAND (t, 0); + unsigned int off = TREE_INT_CST_LOW (TREE_OPERAND (t, 2)); + + gcc_assert (off % 8 == 0); + compute_addr_expr (file, obj); + if (off / 8 > 0) + { + fprintf (file, "\n\tldc.i4\t%d", off / 8); + fputs ("\n\tadd", file); + stack_push (1); + stack_pop (1); + } + } + break; + + default: + fprintf (stderr, "%s: %s\n", + __func__, tree_code_name[TREE_CODE (t)]); + gcc_assert (0); + } + + if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE) + fputs ("\n\tconv.i", file); +} + +static void +print_type_suffix (FILE *file, tree type_node, tree conv_in_type, bool unsign) +{ + int type_size = GET_MODE_BITSIZE (TYPE_MODE (type_node)); + + switch (TREE_CODE (type_node)) + { + case ENUMERAL_TYPE: + case INTEGER_TYPE: + { + bool uns = false; + + if (TYPE_UNSIGNED (type_node) + || (conv_in_type != NULL_TREE + && TYPE_UNSIGNED (conv_in_type) + && type_size > GET_MODE_BITSIZE (TYPE_MODE (conv_in_type)))) + uns = unsign; + + switch (type_size) + { + case 8: fputs ((uns)?"u1":"i1", file); break; + case 16: fputs ((uns)?"u2":"i2", file); break; + case 32: fputs ((uns)?"u4":"i4", file); break; + case 64: fputs ((uns)?"u8":"i8", file); break; + default: + fprintf (stderr, "Unsupported integer size %d\n", type_size); + gcc_assert (0); + break; + } + } + break; + + case REAL_TYPE: + switch (type_size) + { + case 32: fputs ("r4", file); break; + case 64: fputs ("r8", file); break; + default: + fprintf (stderr, "Unsupported floating point size %d\n", type_size); + gcc_assert (0); + break; + } + break; + + case POINTER_TYPE: + fputs ("i", file); + break; + + case VECTOR_TYPE: + { + bool uns = false; + tree innertype = TREE_TYPE (type_node); + enum machine_mode innermode = TYPE_MODE (innertype); + + /* Only expect integer vectors */ + gcc_assert (GET_MODE_CLASS (innermode) == MODE_INT); + + /* and then emit as corresponding same-size interger mode */ + if (TYPE_UNSIGNED (type_node) && unsign) + uns = true; + + switch (type_size) + { + case 8: fputs ((uns)?"u1":"i1", file); break; + case 16: fputs ((uns)?"u2":"i2", file); break; + case 32: fputs ((uns)?"u4":"i4", file); break; + case 64: fputs ((uns)?"u8":"i8", file); break; + default: + fprintf (stderr, "Unsupported integer size %d\n", type_size); + gcc_assert (0); + break; + } + break; + } + + default: + internal_error ("print_type_suffix %s\n", + tree_code_name[TREE_CODE (type_node)]); + gcc_assert (0); + break; + } +} + +/* Dump the node NODE in CIL on the file FILE. */ +static void +gen_cil_node (FILE *file, tree node) +{ + tree op0, op1; + + if (node == NULL_TREE) + return; /* ER: was spc */ + + if (TARGET_EMIT_GIMPLE_COMMENTS && EXPR_HAS_LOCATION (node)) + { + expanded_location xloc = expand_location (EXPR_LOCATION (node)); + if (xloc.file) + fprintf (file, "\n\t/* [file:%s,line:%d] */", xloc.file, xloc.line); + else + fprintf (file, "\n\t/* [line:%d] */", xloc.line); + } + + switch (TREE_CODE (node)) + { + case INTEGER_CST: + { + int type_size = GET_MODE_BITSIZE (TYPE_MODE (TREE_TYPE (node))); + + switch (type_size) + { + case 8: + case 16: + case 32: + fprintf (file, "\n\tldc.i4\t%ld", TREE_INT_CST_LOW (node)); + break; + + case 64: + fprintf (file, "\n\tldc.i8\t%ld", TREE_INT_CST_LOW (node)); + break; + + default: + internal_error ("\nldc: unsupported int size %d\n", type_size); + break; + } + if (TREE_CODE (TREE_TYPE (node)) == POINTER_TYPE) + fputs ("\n\tconv.i", file); + stack_push (1); + } + break; + + case REAL_CST: + { + REAL_VALUE_TYPE d; + tree type_tree; + int type_size; + enum machine_mode mode; + char string[100]; + /* we have 32 and 64 bit reals, real_to_format fills 32 bits per long */ + long buf[2]; + + d = TREE_REAL_CST (node); + type_tree = TREE_TYPE (node); + mode = TYPE_MODE (type_tree); + real_to_target (buf, &d, mode); + real_to_decimal (string, &d, sizeof (string), 5, 1); + type_size = GET_MODE_BITSIZE (TYPE_MODE (type_tree)); + + switch (type_size) + { + case 32: + fprintf (file, "\n\tldc.r4\tfloat32(%#08lx)", buf[0]); + break; + + case 64: + fprintf (file, "\n\tldc.r8\tfloat64(%#08lx%08lx)", buf[1], buf[0]); + break; + + default: + internal_error ("\nldc.r: unsupported float size %d\n", type_size); + break; + } + fprintf (file, " /* %s */", string); + stack_push (1); + break; + } + + case VECTOR_CST: + { + tree elt; + unsigned int val = 0; + tree vector_type = TREE_TYPE (node); + int vector_bitsize = GET_MODE_BITSIZE (TYPE_MODE (vector_type)); + tree unit_type = TREE_TYPE (vector_type); + int unit_bitsize = GET_MODE_BITSIZE (TYPE_MODE (unit_type)); + + /* At this time, support only 32 bit vectors */ + if (vector_bitsize != 32) + internal_error ("\nVECTOR_CST size %d\n", vector_bitsize); + + for (elt = TREE_VECTOR_CST_ELTS (node); elt; elt = TREE_CHAIN (elt)) + { + tree elt_val = TREE_VALUE (elt); + switch (TREE_CODE (elt_val)) + { + case INTEGER_CST: + val = (val << unit_bitsize) | TREE_INT_CST_LOW (elt_val); + break; + + default: + internal_error ("\nldc: unsupported VECTOR_CST type\n"); + break; + } + } + + fprintf (file, "\n\tldc.i4\t%#0x", val); + stack_push (1); + break; + } + + case LABEL_DECL: + internal_error ("use dump_label_name instead of gen_cil_node\n"); + break; + + case INIT_EXPR: + case MODIFY_EXPR: + gen_cil_modify_expr (file, node); + break; + + case GOTO_EXPR: + fputs ("\n\tbr ", file); + dump_label_name (file, GOTO_DESTINATION (node)); + gcc_assert (stack == 0); + break; + + case COND_EXPR: + /* At this point, both clauses of COND_EXPR must contain simple gotos */ + gcc_assert (TREE_CODE (COND_EXPR_THEN (node)) == GOTO_EXPR + && TREE_CODE (COND_EXPR_ELSE (node)) == GOTO_EXPR); + + op0 = COND_EXPR_COND (node); + if (TREE_CODE (op0) == LE_EXPR + || TREE_CODE (op0) == LT_EXPR + || TREE_CODE (op0) == GE_EXPR + || TREE_CODE (op0) == GT_EXPR + || TREE_CODE (op0) == EQ_EXPR + || TREE_CODE (op0) == NE_EXPR) + + { + tree op00 = TREE_OPERAND (op0, 0); + tree op01 = TREE_OPERAND (op0, 1); + bool is_unsigned = TYPE_UNSIGNED (TREE_TYPE (op00)); + gen_cil_node (file, op00); + gen_cil_node (file, op01); + switch (TREE_CODE (op0)) + { + case LE_EXPR: fputs ("\n\tble", file); break; + case LT_EXPR: fputs ("\n\tblt", file); break; + case GE_EXPR: fputs ("\n\tbge", file); break; + case GT_EXPR: fputs ("\n\tbgt", file); break; + case EQ_EXPR: fputs ("\n\tbeq", file); is_unsigned = FALSE; break; + case NE_EXPR: fputs ("\n\tbne", file); is_unsigned = TRUE; break; + default: + gcc_unreachable (); + } + + if (is_unsigned) + fputs (".un\t", file); + else + fputs ("\t", file); + dump_label_name (file, GOTO_DESTINATION (COND_EXPR_THEN (node))); + stack_pop (2); + } + else + { + gen_cil_node (file, op0); + fputs ("\n\tldc.i4.0" + "\n\tbne.un\t", file); + dump_label_name (file, GOTO_DESTINATION (COND_EXPR_THEN (node))); + stack_push (1); + stack_pop (2); + } + + fputs ("\n\tbr\t", file); + dump_label_name (file, GOTO_DESTINATION (COND_EXPR_ELSE (node))); + gcc_assert (stack == 0); + break; + + case SWITCH_EXPR: + { + tree vec = SWITCH_LABELS (node); + unsigned int vec_len, i; + bool first_case = true; + tree min_val = 0, max_val = 0; + double_int max_min_diff; + unsigned int switch_n; + tree default_label; + tree *labels; + + /* The switch body is lowered in gimplify.c, we should never have + switches with a non-NULL SWITCH_BODY here. */ + gcc_assert (vec && !SWITCH_BODY (node)); + vec_len = TREE_VEC_LENGTH (vec); + + /* Compute range of cases */ + for (i = 0; i < vec_len - 1 ; ++i) + { + tree elt = TREE_VEC_ELT (vec, i); + tree low = CASE_LOW (elt); + tree high = CASE_HIGH (elt); + + if (!high) + high = low; + + gcc_assert (low && high); + + /* Discard empty ranges. */ + if (INT_CST_LT (high, low)) + continue; + + if (first_case) + { + min_val = low; + max_val = high; + first_case = false; + } + else + { + if (INT_CST_LT (low, min_val)) + min_val = low; + + if (INT_CST_LT (max_val, high)) + max_val = high; + } + } + gcc_assert (!INT_CST_LT (max_val, min_val)); + + /* Get the default label */ + gcc_assert (!CASE_HIGH (TREE_VEC_ELT (vec, TREE_VEC_LENGTH (vec)- 1))); + gcc_assert (!CASE_LOW (TREE_VEC_ELT (vec, TREE_VEC_LENGTH (vec) - 1))); + default_label = CASE_LABEL (TREE_VEC_ELT (vec, + TREE_VEC_LENGTH (vec) - 1)); + + /* Prepare a table with the label for each value of the condition */ + max_min_diff = double_int_add (double_int_neg (TREE_INT_CST (min_val)), + TREE_INT_CST (max_val)); + gcc_assert (max_min_diff.high == 0 && max_min_diff.low < 8192); + switch_n = max_min_diff.low + 1; + gcc_assert (switch_n > 0); + labels = (tree*)xmalloc (switch_n * sizeof (tree)); + + for (i=0 ; i < switch_n; ++i) + labels[i] = default_label; + + for (i = 0; i < vec_len - 1 ; ++i) + { + tree elt = TREE_VEC_ELT (vec, i); + tree low = CASE_LOW (elt); + tree high = CASE_HIGH (elt); + double_int low_val, high_val, end, j; + double_int minus_min_val = double_int_neg (TREE_INT_CST (min_val)); + + if (!high) + high = low; + + gcc_assert (low && high); + + /* Discard empty ranges. */ + if (INT_CST_LT (high, low)) + continue; + + low_val = TREE_INT_CST (low); + high_val = TREE_INT_CST (high); + end = double_int_add (high_val, double_int_one); + + for (j = low_val; + !double_int_equal_p (j, end); + j = double_int_add (j, double_int_one) + ) + { + double_int norm_j = double_int_add (j, minus_min_val); + + gcc_assert (norm_j.high == 0 && norm_j.low < 8192); + labels[norm_j.low] = CASE_LABEL (elt); + } + } + + /* Emit switch condition */ + gen_cil_node (file, SWITCH_COND (node)); + + /* Emit subtraction to normalize the condition */ + if (!double_int_equal_p (TREE_INT_CST (min_val), double_int_zero)) + { + double_int min = TREE_INT_CST (min_val); + + fputs ("\n\tldc.i4\t", file); + + if (double_int_negative_p (min)) + { + dump_double_int (file, double_int_neg (min), false); + fputs ("\n\tadd", file); + } + else + { + dump_double_int (file, min, false); + fputs ("\n\tsub", file); + } + + stack_push (1); + stack_pop (1); + } + + /* Emit switch instruction */ + fputs ("\n\tswitch\t(", file); + for (i=0 ; i < switch_n; ++i) + { + dump_label_name (file, labels[i]); + if (i < switch_n - 1) + fputs (", ", file); + } + fputs (")", file); + stack_pop (1); + + /* Emit branch for default label */ + fputs ("\n\tbr\t", file); + dump_label_name (file, default_label); + gcc_assert (stack == 0); + } + break; + + case CALL_EXPR: + { + tree fun_expr = TREE_OPERAND (node, 0); + tree fun_type = TREE_TYPE (TREE_TYPE (fun_expr)); + bool direct_call = (TREE_CODE (fun_expr) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND (fun_expr, 0)) == FUNCTION_DECL); + tree args; + tree args_type; + tree last_arg_type; + bool varargs; + unsigned int nargs = 0; + tree dfun = 0; + + if (direct_call) + dfun = TREE_OPERAND (fun_expr, 0); + + /* Built-in functions must be handled in a special way */ + if (dfun && DECL_BUILT_IN (dfun)) + { + bool done = false; + gcc_assert (direct_call); + + if (DECL_BUILT_IN_CLASS (dfun) == BUILT_IN_MD) + { + switch (DECL_FUNCTION_CODE (dfun)) + { + case CIL32_BUILTIN_VA_ARG: + { + tree args = TREE_OPERAND (node, 1); + tree va = TREE_VALUE (args); + tree dummy = TREE_VALUE (TREE_CHAIN (args)); + tree type = TREE_TYPE (TREE_TYPE (dummy)); + + gcc_assert (TYPE_MAIN_VARIANT (TREE_TYPE (va)) == va_list_type_node); + compute_addr_expr (file, va); + fputs ("\n\tcall\tinstance typedref [mscorlib]System.ArgIterator::GetNextArg()" + "\n\trefanyval ", + file); + + dump_type_promoted_type_def (file, type, true); + done = true; + } + break; + + default: + gcc_assert (0); + } + } + else + { + switch (DECL_FUNCTION_CODE (dfun)) + { + case BUILT_IN_VA_START: + { + tree va = TREE_VALUE (TREE_OPERAND (node, 1)); + + /* A lowering phase should have checked that va + has no side effects. */ + gcc_assert (! TREE_SIDE_EFFECTS (va)); + + gcc_assert (TREE_CODE (va) == ADDR_EXPR); + compute_addr_expr (file, TREE_OPERAND (va, 0)); + fputs ("\n\tinitobj\tvaluetype [mscorlib]System.ArgIterator", + file); + + stack_pop (1); + + compute_addr_expr (file, TREE_OPERAND (va, 0)); + fputs ("\n\targlist" + "\n\tcall\tinstance void " + "[mscorlib]System.ArgIterator::.ctor(valuetype " + "[mscorlib]System.RuntimeArgumentHandle)", + file); + + stack_push (1); + stack_pop (2); + done = true; + } + break; + + case BUILT_IN_VA_END: + { + tree va = TREE_VALUE (TREE_OPERAND (node, 1)); + + gcc_assert (TREE_CODE (va) == ADDR_EXPR); + compute_addr_expr (file, TREE_OPERAND (va, 0)); + fputs ("\n\tcall\tinstance void [mscorlib]System.ArgIterator::End()", + file); + + stack_pop (1); + done = true; + } + break; + + case BUILT_IN_VA_COPY: + { + tree args = TREE_OPERAND (node, 1); + tree va_dest = TREE_VALUE (args); + tree va_src = TREE_VALUE (TREE_CHAIN (args)); + + gen_cil_node (file, va_src); + + /* A lowering phase should have checked that the destination + in va_copy is a local variable. */ + gcc_assert (TREE_CODE (va_dest) == ADDR_EXPR); + gcc_assert (TREE_CODE (TREE_OPERAND (va_dest, 0)) == VAR_DECL + && !DECL_FILE_SCOPE_P (TREE_OPERAND (va_dest, 0))); + fputs ("\n\tstloc\t", file); + dump_decl_name (file, TREE_OPERAND (va_dest, 0)); + + stack_pop (1); + done = true; + } + break; + + case BUILT_IN_MEMSET: + { + tree args = TREE_OPERAND (node, 1); + + tree ptr = TREE_VALUE (args); + tree value = TREE_VALUE (TREE_CHAIN (args)); + tree size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args))); + + gen_cil_node (file, ptr); + fputs ("\n\tdup", file); + stack_push (1); + + gen_cil_node (file, value); + gen_cil_node (file, size); + + fputs ("\n\tunaligned. 1" + "\n\tinitblk", file); + stack_pop (3); + done = true; + } + break; + + case BUILT_IN_MEMCPY: + { + tree args = TREE_OPERAND (node, 1); + + tree ptr_dst = TREE_VALUE (args); + tree ptr_src = TREE_VALUE (TREE_CHAIN (args)); + tree size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args))); + + gen_cil_node (file, ptr_dst); + fputs ("\n\tdup", file); + stack_push (1); + + gen_cil_node (file, ptr_src); + gen_cil_node (file, size); + + fputs ("\n\tunaligned. 1" + "\n\tcpblk", file); + stack_pop (3); + done = true; + } + break; + + case BUILT_IN_ALLOCA: + { + tree args = TREE_OPERAND (node, 1); + tree size = TREE_VALUE (args); + + gen_cil_node (file, size); + fputs ("\n\tlocalloc", file); + + done = true; + } + break; + + default: + if (DECL_ASSEMBLER_NAME_SET_P (node)) + { + /* Go Ahead as a normal function call */ + } +/* else */ +/* { */ +/* fprintf (stderr, */ +/* "unsupported builtin: %s\n", */ +/* IDENTIFIER_POINTER (DECL_NAME (dfun))); */ +/* gcc_assert (0); */ +/* } */ + } + } + + if (done) + break; + } + + /* Print parameters. */ + args = TREE_OPERAND (node, 1); + while (args) + { + gen_cil_node (file, TREE_VALUE (args)); + args = TREE_CHAIN (args); + } + + /* Print function pointer, in case of indirect call */ + if (!direct_call) + gen_cil_node (file, fun_expr); + +#if 0 + op1 = TREE_OPERAND (node, 2); + if (op1) + { + gcc_assert (0); + fprintf (file, " [static-chain: "); + gen_cil_node (file, op1); + fprintf (file, "%c", ']'); + } + + if (0 && CALL_EXPR_RETURN_SLOT_OPT (node)) + fprintf (file, " [return slot optimization]"); +#endif + + fprintf (file, "\n\t"); +#if 0 + if (CALL_EXPR_TAILCALL (node)) + fprintf (file, "tail."); +#endif + if (direct_call) + fputs ("call\t", file); + else + fputs ("calli\t", file); + + args_type = TYPE_ARG_TYPES (fun_type); + last_arg_type = 0; + varargs = FALSE; + + if (args_type == NULL) + { + if (direct_call) + warning (0, + "Missing function %s prototype, guessing it, " + "you should fix the code", + IDENTIFIER_POINTER (DECL_NAME (dfun))); + else + warning (0, + "Missing indirect function prototype, guessing it, " + "you should fix the code"); + } + + else + { + last_arg_type = args_type; + + while (TREE_CHAIN (last_arg_type)) + last_arg_type = TREE_CHAIN (last_arg_type); + + if (TREE_VALUE (last_arg_type) != void_type_node) + { + last_arg_type = 0; + varargs = TRUE; + } + } + + if (varargs) + fputs ("vararg ", file); + + dump_type (file, TREE_TYPE (fun_type), true); + + if (direct_call) + { + struct fnct_attr attrs; + decode_function_attrs (dfun, &attrs); + + fputs (" ", file); + + if (attrs.assembly_name) + fprintf (file, "[%s]", attrs.assembly_name); + else if (TARGET_EMIT_EXTERNAL_ASSEMBLY && DECL_EXTERNAL (dfun)) + fputs ("[ExternalAssembly]ExternalAssembly::", file); + + if (attrs.cil_name) + fprintf (file, "%s", attrs.cil_name); + else + dump_decl_name (file, dfun); + } + + fputs (" (", file); + args = TREE_OPERAND (node, 1); + + while (args_type != last_arg_type) + { + ++nargs; + dump_type (file, TREE_VALUE (args_type), true); + args = TREE_CHAIN (args); + args_type = TREE_CHAIN (args_type); + if (args_type != last_arg_type) + fputs (", ", file); + } + + if (varargs && args) + fputs (", ..., ", file); + + while (args) + { + ++nargs; + dump_type_promotion (file, TREE_TYPE (TREE_VALUE (args)), true); + args = TREE_CHAIN (args); + if (args) + fputs (", ", file); + } + + fputs (")", file); + + if (direct_call) + stack_pop (nargs); + else + stack_pop (nargs + 1); + + if (TREE_CODE (TREE_TYPE (fun_type)) != VOID_TYPE) + stack_push (1); + + break; + } + + case MULT_EXPR: + case PLUS_EXPR: + case MINUS_EXPR: + case RDIV_EXPR: + case LSHIFT_EXPR: + case BIT_XOR_EXPR: + op0 = TREE_OPERAND (node, 0); + op1 = TREE_OPERAND (node, 1); + + gen_cil_node (file, op0); + gen_cil_node (file, op1); + + switch (TREE_CODE (node)) + { + case MULT_EXPR: fputs ("\n\tmul", file); break; + case PLUS_EXPR: fputs ("\n\tadd", file); break; + case MINUS_EXPR: fputs ("\n\tsub", file); break; + case RDIV_EXPR: fputs ("\n\tdiv", file); break; + case LSHIFT_EXPR: fputs ("\n\tshl", file); break; + case BIT_XOR_EXPR: fputs ("\n\txor", file); break; + default: + gcc_unreachable (); + } + + /* Values smaller than 32-bits are represented as 32-bit + on the evaluation stack, therefore an explicit conversion + is required. */ + if (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (node))) < 32) + { + fputs ("\n\tconv.", file); + print_type_suffix (file, TREE_TYPE (node), NULL_TREE, true); + } + + stack_pop (1); + break; + + case BIT_IOR_EXPR: + case BIT_AND_EXPR: + op0 = TREE_OPERAND (node, 0); + op1 = TREE_OPERAND (node, 1); + + gen_cil_node (file, op0); + gen_cil_node (file, op1); + + switch (TREE_CODE (node)) + { + case BIT_IOR_EXPR: fputs ("\n\tor", file); break; + case BIT_AND_EXPR: fputs ("\n\tand", file); break; + default: + gcc_unreachable (); + } + + /* No need for conversions even in case of values smaller + than 32-bits, since for these operations the output is + always less or equal than both operands. */ + + stack_pop (1); + break; + + case LT_EXPR: + case GT_EXPR: + case EQ_EXPR: + case NE_EXPR: + op0 = TREE_OPERAND (node, 0); + op1 = TREE_OPERAND (node, 1); + + gen_cil_node (file, op0); + gen_cil_node (file, op1); + + switch (TREE_CODE (node)) + { + case LT_EXPR: + fputs (TYPE_UNSIGNED (TREE_TYPE (op0))?"\n\tclt.un":"\n\tclt", file); + break; + + case GT_EXPR: + fputs (TYPE_UNSIGNED (TREE_TYPE (op0))?"\n\tcgt.un":"\n\tcgt", file); + break; + + case EQ_EXPR: fputs ("\n\tceq", file); break; + case NE_EXPR: fputs ("\n\tceq" + "\n\tldc.i4.1" + "\n\txor", file); break; + + default: + gcc_unreachable (); + } + + if (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (node))) == 64) + fputs ("\n\tconv.i8", file); + + stack_pop (1); + break; + + + case LE_EXPR: + case GE_EXPR: + op0 = TREE_OPERAND (node, 0); + op1 = TREE_OPERAND (node, 1); + + gen_cil_node (file, op0); + gen_cil_node (file, op1); + + switch (TREE_CODE (node)) + { + case LE_EXPR: + fputs (TYPE_UNSIGNED (TREE_TYPE (op0))?"\n\tcgt.un":"\n\tcgt", file); + break; + + case GE_EXPR: + fputs (TYPE_UNSIGNED (TREE_TYPE (op0))?"\n\tclt.un":"\n\tclt", file); + break; + + default: + gcc_unreachable (); + } + + fputs ("\n\tldc.i4.1" + "\n\txor", file); + + if (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (node))) == 64) + fputs ("\n\tconv.i8", file); + + stack_pop (1); + break; + + case EXACT_DIV_EXPR: + case TRUNC_DIV_EXPR: + case TRUNC_MOD_EXPR: + case RSHIFT_EXPR: + op0 = TREE_OPERAND (node, 0); + op1 = TREE_OPERAND (node, 1); + + gen_cil_node (file, op0); + gen_cil_node (file, op1); + + switch (TREE_CODE (node)) + { + case EXACT_DIV_EXPR: + case TRUNC_DIV_EXPR: fputs ("\n\tdiv", file); break; + case TRUNC_MOD_EXPR: fputs ("\n\trem", file); break; + case RSHIFT_EXPR: fputs ("\n\tshr", file); break; + default: + gcc_unreachable (); + } + + if (TYPE_UNSIGNED (TREE_TYPE (node))) + fputs (".un", file); + + /* No need for conversions even in case of values smaller + than 32-bits, since for these operations the output is + always less or equal than both operands. */ + + stack_pop (1); + break; + + case FLOOR_DIV_EXPR: + { + bool is_signed0, is_signed1; + + op0 = TREE_OPERAND (node, 0); + op1 = TREE_OPERAND (node, 1); + + gen_cil_node (file, op0); + gen_cil_node (file, op1); + + is_signed0 = TYPE_UNSIGNED (TREE_TYPE (op0)); + is_signed1 = TYPE_UNSIGNED (TREE_TYPE (op1)); + /* If both operands are unsigned, the result is positive and thus + rounding towards zero is identical to towards -infinity. */ + if (is_signed0 && is_signed1) + { + fputs ("\n\tdiv.un", file); + } + else + { + fputs ("\n\tcall\t int32 [gcc4net]gcc4net.Crt::floordiv(", file); + dump_type_for_builtin (file, TREE_TYPE (op0), true); + fputs (", ", file); + dump_type_for_builtin (file, TREE_TYPE (op1), true); + fputs (")", file); + } + + /* No need for conversions even in case of values smaller + than 32-bits, since for this operation the output is + always less or equal than both operands. */ + + stack_pop (1); + break; + } + + case NEGATE_EXPR: + case BIT_NOT_EXPR: + gen_cil_node (file, TREE_OPERAND (node, 0)); + + switch (TREE_CODE (node)) + { + case NEGATE_EXPR: fputs ("\n\tneg", file); break; + case BIT_NOT_EXPR: fputs ("\n\tnot", file); break; + default: + gcc_unreachable (); + } + + /* Values smaller than 32-bits are represented as 32-bit + on the evaluation stack, therefore an explicit conversion + is required. + Unfortunately this is true for the negation as well just + for the case in which the operand is the smallest negative value. + Example: 8-bit negation of -128 gives 0 and not 128. */ + if (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (node))) < 32) + { + fputs ("\n\tconv.", file); + print_type_suffix (file, TREE_TYPE (node), NULL_TREE, true); + } + + break; + + case INDIRECT_REF: + case BIT_FIELD_REF: + compute_addr_expr (file, node); + fputs ("\n\tldind.", file); + print_type_suffix (file, TREE_TYPE (node), NULL_TREE, true); + break; + + case CONVERT_EXPR: + /* ER: if flag_trapv is set, we could generate the .ovf version? */ + /* TODO: */ + gen_cil_node (file, TREE_OPERAND (node, 0)); + fputs ("\n\tconv.", file); + print_type_suffix (file, TREE_TYPE (node), TREE_TYPE (TREE_OPERAND (node, 0)), true); + break; + + case NOP_EXPR: + { + enum tree_code out_type_code = TREE_CODE (TREE_TYPE (node)); + + gen_cil_node (file, TREE_OPERAND (node, 0)); + + if (out_type_code == INTEGER_TYPE + || out_type_code == ENUMERAL_TYPE + || out_type_code == REAL_TYPE + || out_type_code == POINTER_TYPE) + { + fputs ("\n\tconv.", file); + print_type_suffix (file, TREE_TYPE (node), + TREE_TYPE (TREE_OPERAND (node, 0)), true); + } + } + break; + + case LABEL_EXPR: + op0 = TREE_OPERAND (node, 0); + /* If this is for break or continue, don't bother printing it. */ + if (DECL_NAME (op0)) + { + const char *name = IDENTIFIER_POINTER (DECL_NAME (op0)); + if (strcmp (name, "break") == 0 + || strcmp (name, "continue") == 0) + break; + } + fprintf (file, "\n"); + dump_label_name (file, op0); + fprintf (file, ":"); + + if (DECL_NONLOCAL (op0)) + fprintf (file, " [non-local]"); + + break; + + case RETURN_EXPR: + op0 = TREE_OPERAND (node, 0); + if (op0) + { +/* dump_generic_node (file, op0); */ + if (TREE_CODE (op0) == MODIFY_EXPR) + gen_cil_node (file, TREE_OPERAND (op0, 1)); + else + gen_cil_node (file, op0); + } + + /* Pre-C99 code may contain void-returns for non-void functions, + but the simplification pass should already have avoided this. */ + gcc_assert (op0 + || TREE_CODE (TREE_TYPE (DECL_RESULT (current_function_decl))) + == VOID_TYPE); + + fputs ("\n\tret", file); + stack_reset (); + break; + + case ASM_EXPR: + { + /* support just a simple string, no input/output/clober */ + tree asm_string = ASM_STRING (node); + const char *str = TREE_STRING_POINTER (asm_string); + + fputs ("\n\t", file); + fputs (str, file); + break; + } + + case MIN_EXPR: + case MAX_EXPR: + { + tree node_type = TREE_TYPE (node); + + gcc_assert (!TARGET_EXPAND_MINMAX); + + op0 = TREE_OPERAND (node, 0); + op1 = TREE_OPERAND (node, 1); + + gen_cil_node (file, op0); + gen_cil_node (file, op1); + + /* emit a call */ + fputs ("\n\tcall\t", file); + dump_type_for_builtin (file, node_type, false); + fputs (" [gcc4net]gcc4net.Crt::__", file); + + if (TYPE_UNSIGNED (node_type)) + fputs ("u", file); + + if (TREE_CODE (node) == MIN_EXPR) + fputs ("min", file); + else + fputs ("max", file); + + dump_type_eval_mode (file, node_type, false); + fputs ("3(", file); + dump_type_for_builtin (file, TREE_TYPE (op0), false); + fputs (", ", file); + dump_type_for_builtin (file, TREE_TYPE (op1), false); + fputs (")", file); + stack_pop (1); + } + break; + + case ABS_EXPR: + { + tree node_type = TREE_TYPE (node); + + gcc_assert (!TARGET_EXPAND_ABS); + + op0 = TREE_OPERAND (node, 0); + gen_cil_node (file, op0); + + /* emit a call */ + fputs ("\n\tcall\t", file); + dump_type_for_builtin (file, node_type, false); + fputs (" [gcc4net]gcc4net.Crt::__abs", file); + dump_type_eval_mode (file, node_type, false); + fputs ("2(", file); + dump_type_for_builtin (file, TREE_TYPE (op0), false); + fputs (")", file); + } + break; + + case SSA_NAME: + gcc_assert (0); + break; + + case VAR_DECL: + mark_referenced_type (TREE_TYPE (node)); + + if (!DECL_FILE_SCOPE_P (node)) + fputs ("\n\tldloc\t", file); + else + { + fputs ("\n\tldsfld\t", file); + dump_type (file, TREE_TYPE (node), true); + fputs (" ", file); + if (TARGET_EMIT_EXTERNAL_ASSEMBLY && DECL_EXTERNAL (node)) + fputs ("[ExternalAssembly]ExternalAssembly::", file); + } + + dump_decl_name (file, node); + stack_push (1); + break; + + case PARM_DECL: + mark_referenced_type (TREE_TYPE (node)); + fputs ("\n\tldarg\t", file); + dump_decl_name (file, node); + stack_push (1); + break; + + case FIELD_DECL: + case NAMESPACE_DECL: + fprintf (stderr, "CIL: Cannot handle FIELD_DECL or NAMESPACE_DECL: "); + dump_decl_name (stderr, node); + gcc_assert (0); + break; + + case TREE_LIST: + gcc_assert (0); + break; + + case FUNCTION_DECL: + case CONST_DECL: + gcc_assert (0); + break; + + case ADDR_EXPR: + compute_addr_expr (file, TREE_OPERAND (node, 0)); + break; + + case COMPONENT_REF: + { + tree obj = TREE_OPERAND (node, 0); + tree fld = TREE_OPERAND (node, 1); + tree obj_type = TYPE_MAIN_VARIANT (TREE_TYPE (obj)); + tree fld_type = TREE_TYPE (fld); + + gcc_assert (TREE_CODE (fld) == FIELD_DECL); + gcc_assert (! DECL_BIT_FIELD (fld)); + + compute_addr_expr (file, obj); + fputs ("\n\tldfld\t", file); + dump_type (file, fld_type, true); + fputs (" ", file); + mark_referenced_type (obj_type); + dump_valuetype_name (file, obj_type); + fputs ("::", file); + dump_decl_name (file, fld); + } + break; + + case FLOAT_EXPR: + case FIX_TRUNC_EXPR: + gen_cil_node (file, TREE_OPERAND (node, 0)); + fputs ("\n\tconv.", file); + print_type_suffix (file, TREE_TYPE (node), NULL_TREE, true); + break; + + case TRUTH_NOT_EXPR: + gen_cil_node (file, TREE_OPERAND (node, 0)); + fputs ("\n\tldc.i4.0" + "\n\tceq", file); + stack_push (1); + stack_pop (1); + break; + + case TRUTH_AND_EXPR: + case TRUTH_OR_EXPR: + case TRUTH_XOR_EXPR: + op0 = TREE_OPERAND (node, 0); + op1 = TREE_OPERAND (node, 1); + + gen_cil_node (file, op0); + if (TREE_CODE (TREE_TYPE (op0)) == INTEGER_TYPE) + { + fputs ("\n\tldc.i4.0" + "\n\tceq" + "\n\tldc.i4.1" + "\n\txor", file); + stack_push (1); + stack_pop (1); + } + + gen_cil_node (file, op1); + if (TREE_CODE (TREE_TYPE (op1)) == INTEGER_TYPE) + { + fputs ("\n\tldc.i4.0" + "\n\tceq" + "\n\tldc.i4.1" + "\n\txor", file); + stack_push (1); + stack_pop (1); + } + + if (TREE_CODE (node) == TRUTH_AND_EXPR) + fputs ("\n\tand", file); + else if (TREE_CODE (node) == TRUTH_OR_EXPR) + fputs ("\n\tor", file); + else + fputs ("\n\txor" + "\n\tldc.i4.1" + "\n\tand", file); + stack_pop (1); + break; + + case ENUMERAL_TYPE: + case ARRAY_TYPE: + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + case VOID_TYPE: + case INTEGER_TYPE: + case REAL_TYPE: + case COMPLEX_TYPE: + case VECTOR_TYPE: + case BOOLEAN_TYPE: + case POINTER_TYPE: + case REFERENCE_TYPE: + internal_error ("gen_cil_node does not support TYPE nodes," + " to dump Type name use dump_type.\n"); + break; + + default: + internal_error ("\n\nUnsupported tree in CIL generation: '%s'\n", + tree_code_name[TREE_CODE (node)]); + break; + } +} + +static void +gen_cil_modify_expr (FILE *file, tree node) +{ + tree lhs = TREE_OPERAND (node, 0); + tree rhs = TREE_OPERAND (node, 1); + + if (AGGREGATE_TYPE_P (TREE_TYPE (rhs)) + && (TREE_CODE (lhs) == INDIRECT_REF || TREE_CODE (rhs) == INDIRECT_REF)) + { + gcc_assert (AGGREGATE_TYPE_P (TREE_TYPE (lhs))); + compute_addr_expr (file, lhs); + compute_addr_expr (file, rhs); + fprintf (file, "\n\tldc.i4\t%lu", + TREE_INT_CST_LOW (TYPE_SIZE_UNIT (TREE_TYPE (rhs)))); + fputs ("\n\tcall\tvoid [gcc4net]gcc4net.Crt::memcpy(void*, void*, int32)",file); + stack_push (1); + stack_pop (3); + return; + } + + switch (TREE_CODE (lhs)) + { + case SSA_NAME: + gcc_assert (0); + break; + + case INDIRECT_REF: + compute_addr_expr (file, lhs); + gen_cil_node (file, rhs); + fputs ("\n\tstind.", file); + print_type_suffix (file, TREE_TYPE (lhs), NULL_TREE, false); + stack_pop (2); + break; + + case VAR_DECL: + mark_referenced_type (TREE_TYPE (lhs)); + gen_cil_node (file, rhs); + + if (!DECL_FILE_SCOPE_P (lhs)) + fputs ("\n\tstloc\t", file); + else + { + fputs ("\n\tstsfld\t", file); + dump_type (file, TREE_TYPE (lhs), true); + fputs (" ", file); + if (TARGET_EMIT_EXTERNAL_ASSEMBLY && DECL_EXTERNAL (lhs)) + fputs ("[ExternalAssembly]ExternalAssembly::", file); + } + dump_decl_name (file, lhs); + + stack_pop (1); + break; + + case PARM_DECL: + gen_cil_node (file, rhs); + fputs ("\n\tstarg\t", file); + dump_decl_name (file, lhs); + stack_pop (1); + break; + + case COMPONENT_REF: + { + tree obj = TREE_OPERAND (lhs, 0); + tree fld = TREE_OPERAND (lhs, 1); + tree obj_type = TYPE_MAIN_VARIANT (TREE_TYPE (obj)); + tree fld_type = TREE_TYPE (fld); + + gcc_assert (TREE_CODE (fld) == FIELD_DECL); + gcc_assert (! DECL_BIT_FIELD (fld)); + + compute_addr_expr (file, obj); + gen_cil_node (file, rhs); + mark_referenced_type (obj_type); + fputs ("\n\tstfld\t", file); + dump_type (file, fld_type, true); + fputs (" ", file); + dump_valuetype_name (file, obj_type); + fputs ("::", file); + dump_decl_name (file, fld); + stack_pop (2); + } + break; + + default: +/* { */ +/* fprintf (stderr, "  LHS  "); */ +/* gen_cil_node (file, lhs); */ +/* break; */ +/* } */ + fprintf (stderr, "CIL: Cannot handle lhs: "); + debug_generic_expr (lhs); + gcc_assert (0); + } +} + +/* Warning: these strings are not null-terminated */ +static char * +append_string (char *str, const char *to_append, + unsigned int *len, unsigned int *max_len) +{ + size_t i, orig_len = *len; + size_t append_len = strlen (to_append); + + *len += append_len; + + if (*len > *max_len) + { + while (*len > *max_len) + *max_len *= 2; + str = (char *)xrealloc (str, *max_len); + } + + for (i=0; i < append_len; ++i) + str[orig_len + i] = to_append[i]; + + return str; +} + +/* Warning: these strings are not null-terminated */ +static char * +append_coded_type (char *str, tree type, + unsigned int *len, unsigned int *max_len) +{ + encode_type: + type = TYPE_MAIN_VARIANT (type); + + switch (TREE_CODE (type)) + { + case INTEGER_TYPE: + { + int type_size = GET_MODE_BITSIZE (TYPE_MODE (type)); + char tmp_str[8] = "UI"; + char *tmp_str_ptr = tmp_str; + + snprintf (tmp_str_ptr + 2, 6, "%d", type_size); + + if (!TYPE_UNSIGNED (type)) + ++tmp_str_ptr; + + str = append_string (str, tmp_str_ptr, len, max_len); + } + break; + + case REAL_TYPE: + { + int type_size = GET_MODE_BITSIZE (TYPE_MODE (type)); + char tmp_str[4] = "F"; + + snprintf (tmp_str + 1, 3, "%d", type_size); + + str = append_string (str, tmp_str, len, max_len); + } + break; + + case POINTER_TYPE: + str = append_string (str, "*", len, max_len); + type = TREE_TYPE (type); + goto encode_type; + + case ARRAY_TYPE: + str = append_string (str, "[", len, max_len); + if (TYPE_DOMAIN (type)) + { + tree domain = TYPE_DOMAIN (type); + tree min = TYPE_MIN_VALUE (domain); + tree max = TYPE_MAX_VALUE (domain); + + if (min && max + && integer_zerop (min) + && host_integerp (max, 0)) + { + unsigned int size = TREE_INT_CST_LOW (max) + 1; + char tmp_str[32]; + + snprintf (tmp_str, 32, "%d", size); + str = append_string (str, tmp_str, len, max_len); + } + else + str = append_string (str, "unk", len, max_len); + } + str = append_string (str, "]", len, max_len); + type = TREE_TYPE (type); + goto encode_type; + + case ENUMERAL_TYPE: + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + { + const char *prefix; + const char *type_str; + tree type_name; + + /* Give the aggregate a name unless it has it already */ + if (TYPE_NAME (type) == 0) + { + tree type_decl = build0 (TYPE_DECL, type); + DECL_NAME (type_decl) = make_valuetype_identifier (type); + TYPE_NAME (type) = type_decl; + } + + type_name = TYPE_NAME (type); + + if (TREE_CODE (type_name) == IDENTIFIER_NODE) + type_str = IDENTIFIER_POINTER (type_name); + else + type_str = IDENTIFIER_POINTER (DECL_NAME (type_name)); + + switch (TREE_CODE (type)) + { + case ENUMERAL_TYPE: + prefix = "E"; + break; + + case RECORD_TYPE: + prefix = "S"; + break; + + case UNION_TYPE: + case QUAL_UNION_TYPE: + prefix = "UN"; + break; + + default: + gcc_assert (0); + prefix = "error"; + } + + str = append_string (str, prefix, len, max_len); + str = append_string (str, type_str, len, max_len); + } + break; + + default: + str = append_string (str, "unknown", len, max_len); + } + + return str; +} + +static char * +get_md5 (const char *str, size_t len, size_t *md5_len) +{ + char *md5_str; + + /* TODO: unimplemented */ + *md5_len = len; + md5_str = (char *)xmalloc (len); + memcpy (md5_str, str, len); + + return md5_str; +} + +static tree +make_valuetype_identifier (tree t) +{ + size_t tmp_name_max_len = 256; + size_t tmp_name_len = 0; + char *tmp_name; + size_t vt_name_len = 0; + char *vt_name; + tree ident; + + tmp_name = (char *)xmalloc (tmp_name_max_len); + + if (TREE_CODE (t) == ENUMERAL_TYPE) + { + tree tmp; + + tmp_name = append_string (tmp_name, "enum?", + &tmp_name_len, &tmp_name_max_len); + + tmp = TYPE_VALUES (t); + + while (tmp) + { + tmp_name = append_string (tmp_name, + IDENTIFIER_POINTER (TREE_PURPOSE (tmp)), + &tmp_name_len, &tmp_name_max_len); + tmp_name = append_string (tmp_name, "?", + &tmp_name_len, &tmp_name_max_len); + tmp = TREE_CHAIN (tmp); + } + } + else if (TREE_CODE (t) == ARRAY_TYPE) + { + tmp_name = append_string (tmp_name, "array?", + &tmp_name_len, &tmp_name_max_len); + tmp_name = append_coded_type (tmp_name, t, + &tmp_name_len, &tmp_name_max_len); + } + else + { + tree tmp; + + if (TREE_CODE (t) == RECORD_TYPE) + tmp_name = append_string (tmp_name, "struct?", + &tmp_name_len, &tmp_name_max_len); + else + tmp_name = append_string (tmp_name, "union?", + &tmp_name_len, &tmp_name_max_len); + + tmp = TYPE_FIELDS (t); + while (tmp) + { + tree ttype = TREE_TYPE (tmp); + + tmp_name = append_coded_type (tmp_name, ttype, + &tmp_name_len, &tmp_name_max_len); + tmp_name = append_string (tmp_name, "?", + &tmp_name_len, &tmp_name_max_len); + if (DECL_NAME (tmp) != 0) + tmp_name = append_string (tmp_name, + IDENTIFIER_POINTER (DECL_NAME (tmp)), + &tmp_name_len, &tmp_name_max_len); + else + /* Unnamed bitfields or unions */ + tmp_name = append_string (tmp_name, "?unnamed", + &tmp_name_len, &tmp_name_max_len); + tmp_name = append_string (tmp_name, "?", + &tmp_name_len, &tmp_name_max_len); + tmp = TREE_CHAIN (tmp); + } + } + + vt_name = get_md5 (tmp_name, tmp_name_len, &vt_name_len); + free (tmp_name); + + ident = get_identifier_with_length (vt_name, vt_name_len); + free (vt_name); + + return ident; +} + +static void +print_valuetype_decl (FILE *file, tree t) +{ + bool is_enum; + + if (!AGGREGATE_TYPE_P (t) && TREE_CODE (t) != ENUMERAL_TYPE) + return; + + if (file == 0) + return; + + gcc_assert (TYPE_MAIN_VARIANT (t) == t); + gcc_assert (TYPE_NAME (t)); + + is_enum = (TREE_CODE (t) == ENUMERAL_TYPE); + + /* Print the name of the valuetype. */ + fputs ("\n.class ", file); + + if (TYPE_FILE_SCOPE_P (t)) + fputs ("public ", file); + else + fputs ("private ", file); + + if (!is_enum) + fputs ("explicit ", file); + + fputs ("sealed serializable ansi ", file); + dump_valuetype_name (file, t); + fputs (" extends ['mscorlib']System.", file); + + if (is_enum) + fputs ("Enum\n", file); + else + fputs ("ValueType\n", file); + + /* Print the contents of the valuetype. */ + fputs ("{\n", file); + + if (is_enum) + { + int type_size = GET_MODE_BITSIZE (TYPE_MODE (t)); + char tmp_str[8] = "int"; + char *base_type_str = tmp_str; + tree tmp; + + snprintf (base_type_str + 3, 5, "%d", type_size); + + fputs ("\t.field public specialname rtspecialname ", file); + + if (!TYPE_UNSIGNED (t)) + fputs ("unsigned ", file); + + fprintf (file, "%s 'value__'\n", base_type_str); + + tmp = TYPE_VALUES (t); + while (tmp) + { + fputs ("\t.field public static literal ", file); + dump_type (file, t, false); + fputs (" ", file); + dump_decl_name (file, TREE_PURPOSE (tmp)); + fprintf (file, " = %s(%ld)\n", + base_type_str, TREE_INT_CST_LOW (TREE_VALUE (tmp))); + tmp = TREE_CHAIN (tmp); + } + } + else if (TREE_CODE (t) == ARRAY_TYPE) + { + /* array */ + fprintf (file, "\t.size %ld\n", TREE_INT_CST_LOW (TYPE_SIZE_UNIT (t))); + fprintf (file, "\t.pack %u\n", TYPE_ALIGN_UNIT (t)); + fputs ("\t.field [0] public specialname ", file); + dump_type (file, TREE_TYPE (t), false); + fputs (" 'elem__'\n", file); + } + else + { + /* struct and union */ + tree tmp; + + fprintf (file, "\t.size %ld\n", TREE_INT_CST_LOW (TYPE_SIZE_UNIT (t))); + fprintf (file, "\t.pack %u\n", TYPE_ALIGN_UNIT (t)); + + tmp = TYPE_FIELDS (t); + while (tmp) + { + tree type; + unsigned int bit_offset = + TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (tmp)); + unsigned int byte_offset = + TREE_INT_CST_LOW (DECL_FIELD_OFFSET (tmp)); + unsigned int offset; + + /* Skip unnamed bitfields */ + if (DECL_NAME (tmp) == 0 && DECL_BIT_FIELD (tmp)) + goto next; + + if (DECL_BIT_FIELD (tmp)) + { + unsigned int type_size; + + type = DECL_BIT_FIELD_TYPE (tmp); + + gcc_assert (TREE_CODE (TREE_TYPE (tmp)) == INTEGER_TYPE + || TREE_CODE (TREE_TYPE (tmp)) == BOOLEAN_TYPE); + gcc_assert (TREE_CODE (type) == INTEGER_TYPE + || TREE_CODE (type) == BOOLEAN_TYPE + || TREE_CODE (type) == ENUMERAL_TYPE); + + type_size = TREE_INT_CST_LOW (TYPE_SIZE (type)); + offset = byte_offset + (bit_offset & ~(type_size - 1)) / 8; + } + else + { + type = TREE_TYPE (tmp); + gcc_assert (bit_offset % 8 == 0); + offset = byte_offset + bit_offset / 8; + } + + fprintf (file, "\t.field [%d] public ", offset); + dump_type (file, type, false); + fputs (" ", file ); + dump_decl_name (file, tmp); + fputs ("\n", file); + next: + tmp = TREE_CHAIN (tmp); + } + } + + fputs ("}\n", file); +} + +static void +print_string_decl (FILE *file, tree t) +{ + const char *str; + int len, i; + + gcc_assert (TREE_CODE (t) == STRING_CST); + str = TREE_STRING_POINTER (t); + len = TREE_STRING_LENGTH (t); + + mark_referenced_type (TREE_TYPE (t)); + + /* Emit the string in readable form as a comment. */ + fputs ("// string: \"", file); + for (i=0; i < len-1; ++i) + { + switch (str[i]) { + case '\n': fputs ("\\n", file); break; + case '"': fputs ("\"", file); break; + default: fputc (str[i], file); break; + } + } + fputs ("\"\n", file); + + fprintf (file, ".data 'DataStr%lu' = bytearray(", + (unsigned long)TREE_STRING_POINTER (t)); + for (i=0; i < len; ++i) + fprintf (file, "%02x ", str[i]); + fputs (")\n", file); + + fputs (".field private static ", file); + dump_type (file, TREE_TYPE (t), true); + fputs (" ", file); + dump_string_name (file, t); + fprintf (file, " at 'DataStr%lu'\n", + (unsigned long)TREE_STRING_POINTER (t)); +} + +static void +gen_start_function (FILE *stream) +{ + int nargs; + tree args; + for (nargs=0,args = DECL_ARGUMENTS (current_function_decl); + args; + args = TREE_CHAIN (args),++nargs) + { + } + + fputs ("\n.method public static void '.start'(class [mscorlib]System.String[] 'args') cil managed", + stream); + fputs ("\n{" + "\n\t.entrypoint" + "\n\t.maxstack 2" + "\n\t.locals (int32 'argc', int8** 'argv')", stream); + /* TODO: add startup code*/ + switch (nargs) + { + case 0: + fputs ("\n\tcall\tvoid [gcc4net]gcc4net.StartupHelper::Startup()" + "\n\tcall\tint32 main()", stream); + break; + + case 1: + fputs ("\n\tldloca\t'argc'" + "\n\tcall\tnative int [gcc4net]gcc4net.StartupHelper::GetArgv(int32&)" + "\n\tpop" + "\n\tcall\tvoid [gcc4net]gcc4net.StartupHelper::Startup()" + "\n\tldloc\t'argc'" + "\n\tcall\tint32 main(int32)", stream); + break; + + case 2: + fputs ("\n\tldloca\t'argc'" + "\n\tcall\tnative int [gcc4net]gcc4net.StartupHelper::GetArgv(int32&)" + "\n\tstloc\t'argv'" + "\n\tcall\tvoid [gcc4net]gcc4net.StartupHelper::Startup()" + "\n\tldloc\t'argc'" + "\n\tldloc\t'argv'" + "\n\tcall\tint32 main(int32, int8**)", stream); + break; + + default: + gcc_assert (0); + } + + /* TODO: add exit code*/ + fputs ("\n\tcall\tvoid [gcc4net]gcc4net.StartupHelper::Shutdown(int32)" + "\n\tret" + "\n} // .start" + "\n\n", stream); +} + +static void +gen_cil_1 (FILE *stream) +{ + basic_block bb; + block_stmt_iterator bsi; + bool varargs = FALSE; + tree args; + + /* Mark defs and uses of local non-static variables */ + defd_vars = pointer_set_create (); + defd_more_than_once_vars = pointer_set_create (); + used_vars = pointer_set_create (); + used_more_than_once_vars = pointer_set_create (); + FOR_EACH_BB (bb) + { + for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi)) + { + mark_var_defs_uses (bsi_stmt (bsi)); + } + } + pointer_set_destroy (defd_vars); + + /* Remove useless pairs of stloc - ldloc */ + useless_vars = pointer_set_create (); + FOR_EACH_BB (bb) + { + /* Start from the second statement, if any */ + bsi = bsi_start (bb); + if (! bsi_end_p (bsi)) + bsi_next (&bsi); + + for (; !bsi_end_p (bsi); bsi_next (&bsi)) + { + bool changed; + + /* Remove stloc - ldloc pairs until no change is done + to the current statement */ + do { + changed = false; + remove_stloc_ldloc (bsi, bsi_stmt_ptr (bsi), &changed); + } while (changed); + } + } + pointer_set_destroy (defd_more_than_once_vars); + pointer_set_destroy (used_vars); + pointer_set_destroy (used_more_than_once_vars); + + /* Remove useless vars (only used in removed stloc - ldloc pairs) */ + { + tree cell, prev_cell = NULL_TREE; + + for (cell = cfun->unexpanded_var_list; + cell; + cell = TREE_CHAIN (cell)) + { + tree var = TREE_VALUE (cell); + + if (pointer_set_contains (useless_vars, var)) + { + if (prev_cell == NULL_TREE) + cfun->unexpanded_var_list = TREE_CHAIN (cell); + else + TREE_CHAIN (prev_cell) = TREE_CHAIN (cell); + } + else + prev_cell = cell; + } + } + pointer_set_destroy (useless_vars); + + stack_reset (); + max_stack = 0; + + if (strcmp ("main", + lang_hooks.decl_printable_name (current_function_decl, 1)) == 0) + gen_start_function (stream); + + { + tree var, cell; + + for (cell = cfun->unexpanded_var_list; + cell; + cell = TREE_CHAIN (cell)) + { + var = TREE_VALUE (cell); + + if (TREE_STATIC (var) && TREE_ASM_WRITTEN (var) == 0) + make_decl_cil (stream, var); + } + } + + { + tree args_type = TYPE_ARG_TYPES (TREE_TYPE (current_function_decl)); + + if (args_type != NULL) + { + while (TREE_CHAIN (args_type)) + args_type = TREE_CHAIN (args_type); + + if (TREE_VALUE (args_type) != void_type_node) + varargs = TRUE; + } + } + + fputs ("\n.method ", stream); + + if (TREE_PUBLIC (current_function_decl)) + fputs ("public ", stream); + else + fputs ("private ", stream); + + fputs ("static ", stream); + if (varargs) + fputs ("vararg ", stream); + dump_type (stream, TREE_TYPE (TREE_TYPE (current_function_decl)), true); + fprintf (stream, " '%s' (", + lang_hooks.decl_printable_name (current_function_decl, 1)); + + args = DECL_ARGUMENTS (current_function_decl); + + while (args) + { + dump_type (stream, DECL_ARG_TYPE (args), true); + fputs (" ", stream); + dump_decl_name (stream, args); + args = TREE_CHAIN (args); + + if (args) + fputs (", ", stream); + else if (varargs) + fputs (", ...", stream); + } + + fputs (") cil managed" + "\n{" + "\n\t.locals init (", stream); + { + tree var, cell; + bool first = true; + + for (cell = cfun->unexpanded_var_list; + cell; + cell = TREE_CHAIN (cell)) + { + var = TREE_VALUE (cell); + if (!TREE_STATIC (var)) + { + if (!first) + fputs (", ", stream); + first = false; + dump_type (stream, TREE_TYPE (var), true); + fputs (" ", stream); + dump_decl_name (stream, var); + } + } + } + fputs (")\n", stream); + + if (DECL_STATIC_CONSTRUCTOR (current_function_decl)) + { + if (TARGET_OPENSYSTEMC) + fputs ("\n\t.custom instance " + "void ['OpenSystem.C']'OpenSystem.C'.InitializerAttribute::.ctor() " + "= (01 00 00 00)", stream); + } + + FOR_EACH_BB (bb) + { + tree stmt = NULL_TREE; + for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi)) + { + stmt = bsi_stmt (bsi); + if (TARGET_EMIT_GIMPLE_COMMENTS) + { + fprintf (stream, "\n\t/* "); + print_generic_expr (stream, stmt, 0); + fprintf (stream, " */"); + } + gcc_assert (stack == 0); + + if (TREE_CODE (stmt) != NOP_EXPR + || TREE_CODE (TREE_OPERAND (stmt, 0)) != INTEGER_CST) + gen_cil_node (stream, stmt); + + if (TREE_CODE (stmt) == CALL_EXPR) + { + tree fun_expr = TREE_OPERAND (stmt, 0); + tree fun_type = TREE_TYPE (TREE_TYPE (fun_expr)); + + if (TREE_CODE (TREE_TYPE (fun_type)) != VOID_TYPE) + { + fputs ("\n\tpop", stream); + stack_pop (1); + } + } + } + + if ((!stmt || (TREE_CODE (stmt) != COND_EXPR)) && single_succ_p (bb)) + { + basic_block succ = single_succ (bb); + + /* The last part of the test (succ != bb->next_bb) is a HACK. It + avoids generating a branch to the successor in case of a + fallthrough. To be fixed when we have a proper layout of basic + blocks. Note that branches from COND_EXPR are still generated, + even to a fallthrough. */ + if ((succ->index != EXIT_BLOCK) && (succ != bb->next_bb)) + { + tree label = tree_block_label (succ); + fputs ("\n\tbr ", stream); + dump_label_name (stream, label); + gcc_assert (stack == 0); + } + } + } + + fprintf (stream, "\n\t.maxstack %d\n", max_stack); + fprintf (stream, "\n} // %s\n", + lang_hooks.decl_printable_name (current_function_decl, 1)); + TREE_ASM_WRITTEN (current_function_decl) = 1; +} + +static void +create_init_method (tree decl) +{ + static int init_counter = 0; + tree init; + char name[30]; + tree fun_type; + tree fun_decl; + tree init_expr = NULL; + tree result; + + ++init_counter; + sprintf (name, "?init-%d", init_counter); + + fun_type = build_function_type (void_type_node, void_list_node); + fun_decl = build_decl (FUNCTION_DECL, get_identifier (name), fun_type); + + result = build_decl (RESULT_DECL, NULL_TREE, void_type_node); + DECL_ARTIFICIAL (result) = 1; + DECL_IGNORED_P (result) = 1; + DECL_RESULT (fun_decl) = result; + + allocate_struct_function (fun_decl); + + + TREE_STATIC (fun_decl) = 1; + TREE_USED (fun_decl) = 1; + DECL_ARTIFICIAL (fun_decl) = 1; + DECL_IGNORED_P (fun_decl) = 0; + TREE_PUBLIC (fun_decl) = 0; + DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (fun_decl) = 1; + DECL_UNINLINABLE (fun_decl) = 1; + DECL_EXTERNAL (fun_decl) = 0; + DECL_STATIC_CONSTRUCTOR (fun_decl) = 1; + DECL_CONTEXT (fun_decl) = NULL_TREE; + DECL_INITIAL (fun_decl) = make_node (BLOCK); + + init = DECL_INITIAL (decl); + DECL_INITIAL (decl) = NULL_TREE; + + DECL_SOURCE_LOCATION (fun_decl) = EXPR_LOCATION (init); + + expand_init_to_stmt_list (decl, init, &init_expr, false); + + DECL_SAVED_TREE (fun_decl) = init_expr; + + gimplify_function_tree (fun_decl); + tree_lowering_passes (fun_decl); + tree_rest_of_compilation (fun_decl); +} + +void +make_decl_cil (FILE *stream, tree decl) +{ + if (TREE_CODE (decl) == VAR_DECL && (TREE_STATIC (decl) || TREE_PUBLIC (decl))) + { + tree init = DECL_INITIAL (decl); + + fputs ("\n.field ", stream); + + if (TREE_PUBLIC (decl)) + fputs ("public ", stream); + else + fputs ("private ", stream); + + fputs ("static ", stream); + dump_type (stream, TREE_TYPE (decl), true); + fputs (" ", stream); + dump_decl_name (stream, decl); + fputs ("\n", stream); + + if (init && init != error_mark_node) + VARRAY_PUSH_TREE (pending_ctors, decl); + + TREE_ASM_WRITTEN (decl) = 1; + } +} + +void +gen_cil_init (void) +{ + FILE *stream = asm_out_file; + + fputs (".assembly extern gcc4net {}", stream); + if (TARGET_EMIT_EXTERNAL_ASSEMBLY) + fputs ("\n.assembly '_C_MONO_ASSEMBLY' {}", stream); + fputs ("\n.module ''", stream); + + if (TARGET_EMIT_EXTERNAL_ASSEMBLY) + fputs ("\n.class public '_C_MONO_MODULE'" + "\n{", stream); + + if (TARGET_OPENSYSTEMC) + fputs ("\n.custom instance " + "void ['OpenSystem.C']'OpenSystem.C'.ModuleAttribute::.ctor() " + "= (01 00 00 00)" + "\n", stream); + + referenced_types = pointer_set_create (); + VARRAY_TREE_INIT (referenced_strings, 32, "strings used in current unit"); + referenced_string_ptrs = pointer_set_create (); + referenced_pinvoke = pointer_set_create (); + VARRAY_TREE_INIT (pending_ctors, 32, "pending ctors"); +} + +void +gen_cil_fini (void) +{ + FILE *stream = asm_out_file; + struct pointer_set_iter_t it; + int i, n; + + + i = 0; + n = VARRAY_ACTIVE_SIZE (pending_ctors); + while (i */ + +#ifndef GEN_CIL_H +#define GEN_CIL_H + +#include "tree.h" + +void +make_decl_cil (FILE *, tree); + +void +cil_add_pinvoke(tree); + +void +gen_cil_init (void); +void +gen_cil_fini (void); + +#endif /* GEN_CIL_H */ diff --git a/gcc/config/cil32/t-cil32 b/gcc/config/cil32/t-cil32 new file mode 100644 index 00000000000..78bb2c5c78c --- /dev/null +++ b/gcc/config/cil32/t-cil32 @@ -0,0 +1,60 @@ +# Hi emacs, use Makefile syntax mode. -*-mode: Makefile; -*- + +# Copyright (C) 2006 Free Software Foundation, Inc. +# +# This file is part of GCC. +# +# 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 2, 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 COPYING. If not, write to the Free +# Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# Authors: +# Andrea Bona +# Andrea Ornstein +# Erven Rohou +# Roberto Costa +# +# Contact information at STMicroelectronics: +# Roberto Costa + + +gen-cil.o: $(srcdir)/config/cil32/gen-cil.c $(srcdir)/config/cil32/gen-cil.h \ + $(srcdir)/config/cil32/tree-simp-cil.h gt-gen-cil.h\ + $(CONFIG_H) $(SYSTEM_H) \ + $(TREE_H) $(DIAGNOSTIC_H) real.h $(HASHTAB_H) $(TREE_FLOW_H) \ + $(TM_H) coretypes.h tree-iterator.h tree-chrec.h langhooks.h tree-pass.h + $(CC) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $< $(OUTPUT_OPTION) + +tree-simp-cil.o: $(srcdir)/config/cil32/tree-simp-cil.c \ + $(srcdir)/config/cil32/tree-simp-cil.h \ + $(CONFIG_H) $(SYSTEM_H) \ + $(TREE_H) $(DIAGNOSTIC_H) real.h $(HASHTAB_H) $(TREE_FLOW_H) \ + $(TM_H) coretypes.h tree-iterator.h tree-chrec.h langhooks.h tree-pass.h + $(CC) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $< $(OUTPUT_OPTION) + +$(out_object_file): gt-gen-cil.h +gt-gen-cil.h : s-gtype ; @true + + +LIBGCC = +INSTALL_LIBGCC = install-gcc4net + +install-gcc4net: gcc4net.dll installdirs + $(INSTALL_DATA) gcc4net.dll $(DESTDIR)$(libdir)/ + $(INSTALL_DATA) gcc4net.dll $(DESTDIR)$(libsubdir)/ + +MCS=mcs + +gcc4net.dll: $(srcdir)/config/cil32/gcc4net.cs + $(MCS) $< -unsafe -target:library -out:$@ diff --git a/gcc/config/cil32/tree-simp-cil.c b/gcc/config/cil32/tree-simp-cil.c new file mode 100644 index 00000000000..8e43c2fbee9 --- /dev/null +++ b/gcc/config/cil32/tree-simp-cil.c @@ -0,0 +1,2166 @@ +/* Simplify GENERIC trees before CIL emission. + + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GCC. + +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 2, 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 COPYING. If not, write to the Free +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. + +Authors: + Andrea Bona + Andrea Ornstein + Erven Rohou + Roberto Costa + +Contact information at STMicroelectronics: +Roberto Costa */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "diagnostic.h" +#include "real.h" +#include "hashtab.h" +#include "tree-flow.h" +#include "langhooks.h" +#include "tree-iterator.h" +#include "tree-chrec.h" +#include "tree-pass.h" +#include "timevar.h" +#include "assert.h" +#include "toplev.h" +#include "output.h" +#include "tree-simp-cil.h" + +/* The purpose of this pass is to simplify GIMPLE trees in order + to make CIL emission easier. + As a matter of fact, there are some transformations that are + difficult at emission time (pass gen_cil), i.e. those that + involve generating new local temporary variables, modifications + in the control-flow graph or in types... + On the other hand, these transformations can be well performed + in GIMPLE representation. + The choice that is taken is to add restrictions to the GIMPLE trees + gen_cil pass can handle and to make simp_cil pass enforce them. + + Currently, these are the transformations performed by cil_simp pass: + + *) Removal of RESULT_DECL nodes. CIL doesn't treat the value + returned by a function in any special way: if it has to be + temporarily stored, this must be in a local. + A new local variable is generated and each RESULT_DECL node is + transformed into a VAR_DECL of that variable. + + *) Expansion of ABS_EXPR nodes (in case of -mexpand-abs option). + The expansion requires changes to the control-flow graph. + + *) Expansion of MAX_EXPR and MIN_EXPR nodes (in case of + -mexpand-minmax option). + The expansion requires changes to the control-flow graph. + + *) Expansion of SWITCH_EXPR, when it is not profitable to have + a switch table (heuristic decision is based on case density). + CIL emission pass (gen_cil) always emits a SWITCH_EXPR to a + CIL switch opcode. When a low case density makes compare trees + preferable, the SWITCH_EXPR is expanded; otherwise the + SWITCH_EXPR is not modified. + The expansion requires changes to the control-flow graph. + + *) Expansion of COMPONENT_REF nodes operating on bit-fields. + CIL has no direct support for bit-field access; hence, + equivalent code that extracts the bit pattern and applies the + appropriate bit mask is generated. + Memory access is performed by using INDIRECT_REF nodes. + Beware that such a COMPONENT_REF on the left-hand side of an + assignment also requires a load from memory; from the memory + access point of view, the operation cannot be made atomic. + + *) Expansion of TARGET_MEM_REF nodes. + Emission of such nodes is not difficult in gen_cil pass; + however, a previous expansion may trigger further optimizations + (since there is no similar construct in CIL bytecodes). + + *) Expansion of ARRAY_REF nodes. + Emission of such nodes is not difficult in gen_cil pass; + however, a previous expansion may generate better code (i.e.: + it may fold constants) or trigger further optimizations + (CIL arrays cannot be used for C-style arrays). + + *) Expansion of CONSTRUCTOR nodes used as right-hand sides of + INIT_EXPR and MODIFY_EXPR nodes. + Such CONSTRUCTOR nodes must be implemented in CIL bytecode through + a sequence of finer grain initializations. + Hence, initializer statements containing CONSTRUCTOR nodes + are expanded into an equivalent list of initializer statements, + with no more CONSTRUCTOR nodes. + Since the same expansion must occur for global variables (which + is performed by other passes), function + expand_init_to_stmt_list (...) is exported. + + *) Expansion of LROTATE_EXPR and RROTATE_EXPR nodes. + In CIL there no are opcodes for rotation and they have + to be emulated through shifts and bit operations. + A previous expansion may generate better code (i.e.: + it may fold constants) or trigger further optimizations. + + *) The second operand of LSHIFT_EXPR and RSHIFT_EXPR + is converted to a 32-bit size in the very rare + cases it isn't already. This is always safe, because shifts with + shift amounts bigger than the size of the operand to be shifted + produce undefined results. + The reason is that CIL shift operations require a shift operand + of type int32. + + *) Rename of inlined variables to unique names. + Emitted variables by gen_cil pass keep the original name. + In case of variables declared within inlined functions, + renaming them is needed clashes. + + *) Globalization of function static variables. + CIL locals can be used for function non-static variables; + there is no CIL feature to do the same with function static + variables. Therefore, those variables have their scope changed + (they become global), and their name as well, to avoid clashes. + + *) Expansion of initializers of local variables. + In order to simplify gen_cil, the initialization of local + variables (for those that have it) is expanded into the body + of the entry basic block of the function. +*/ + +/* Local functions, macros and variables. */ +static tree get_unsigned_integer_type (int); +static bool is_copy_required (tree); +static bool mostly_zeros_p (tree); +static bool all_zeros_p (tree); +static void simp_switch (block_stmt_iterator *, tree *); +static void simp_trivial_switch (block_stmt_iterator *, tree *); +static void simp_abs (block_stmt_iterator *, tree *); +static void simp_min_max (block_stmt_iterator *, tree *); +static void simp_rotate (block_stmt_iterator *, tree *); +static void simp_shift (block_stmt_iterator *, tree); +static void simp_target_mem_ref (block_stmt_iterator *, tree *); +static void simp_array_ref (block_stmt_iterator *, tree *); +static void simp_rhs_bitfield_component_ref (block_stmt_iterator *, tree *); +static void simp_lhs_bitfield_component_ref (block_stmt_iterator *, tree *); +static void pre_simp_init (block_stmt_iterator *, tree); +static void simp_cil_node (block_stmt_iterator *, tree *); +static void split_use (block_stmt_iterator, tree *); +static void rename_var (tree, const char*); +static void simp_vars (void); +static unsigned int simp_cil (void); +static bool simp_cil_gate (void); + +static tree res_var; +static tree uint32_type; + +/* Return the unsigned integer type with size BITS bits */ + +static tree +get_unsigned_integer_type (int bits) +{ + if (GET_MODE_BITSIZE (TYPE_MODE (unsigned_type_node)) == bits) + return unsigned_type_node; + else if (GET_MODE_BITSIZE (TYPE_MODE (long_unsigned_type_node)) == bits) + return long_unsigned_type_node; + else if (GET_MODE_BITSIZE (TYPE_MODE (short_unsigned_type_node)) == bits) + return short_unsigned_type_node; + else if (GET_MODE_BITSIZE (TYPE_MODE (long_long_unsigned_type_node)) + == bits) + return long_long_unsigned_type_node; + else if (GET_MODE_BITSIZE (TYPE_MODE (unsigned_char_type_node)) == bits) + return unsigned_char_type_node; + else + { + gcc_assert (0); + return NULL_TREE; + } +} + +/* In the case of multiple uses of tree NODE, return whether + it is required to compute NODE only once or not. + If NODE has side effects, TRUE is obviously always returned. + If NODE has no side effects, TRUE is still returned if + it looks more profitable to compute NODE only once, + FALSE otherwise (this is a heuristic decision). */ + +static bool +is_copy_required (tree node) +{ + if (TREE_SIDE_EFFECTS (node)) + return TRUE; + + switch (TREE_CODE (node)) + { + case INTEGER_CST: + case REAL_CST: + case VAR_DECL: + case PARM_DECL: + return FALSE; + default: + return TRUE; + } +} + +/* Simplify the node pointed by NODE_PTR in order to make CIL emission easier. + BSI points to the iterator of the statement that contains *NODE_PTR + (in order to allow insertion of new statements). + BSI is passed by reference because instructions may be inserted, + new basic blocks created... + NODE is passed by reference because simplification may require + replacing the node. */ + +static void +simp_cil_node (block_stmt_iterator *bsi, tree *node_ptr) +{ + tree node = *node_ptr; + + if (node == NULL_TREE) + return; /* ER: was spc */ + + switch (TREE_CODE (node)) + { + case COND_EXPR: + simp_cil_node (bsi, &COND_EXPR_COND (node)); + break; + + case SWITCH_EXPR: + simp_cil_node (bsi, &SWITCH_COND (node)); + simp_switch (bsi, node_ptr); + break; + + case CALL_EXPR: + { + tree args = TREE_OPERAND (node, 1); + + simp_cil_node (bsi, &TREE_OPERAND (node, 0)); + + while (args) + { + simp_cil_node (bsi, &TREE_VALUE (args)); + args = TREE_CHAIN (args); + } + } + break; + + case MULT_EXPR: + case PLUS_EXPR: + case MINUS_EXPR: + case RDIV_EXPR: + case BIT_IOR_EXPR: + case BIT_XOR_EXPR: + case BIT_AND_EXPR: + case TRUTH_AND_EXPR: + case TRUTH_OR_EXPR: + case TRUTH_XOR_EXPR: + case LT_EXPR: + case GT_EXPR: + case EQ_EXPR: + case NE_EXPR: + case LE_EXPR: + case GE_EXPR: + case EXACT_DIV_EXPR: + case TRUNC_DIV_EXPR: + case TRUNC_MOD_EXPR: + simp_cil_node (bsi, &TREE_OPERAND (node, 0)); + simp_cil_node (bsi, &TREE_OPERAND (node, 1)); + break; + + case LSHIFT_EXPR: + case RSHIFT_EXPR: + simp_cil_node (bsi, &TREE_OPERAND (node, 0)); + simp_cil_node (bsi, &TREE_OPERAND (node, 1)); + if (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (TREE_OPERAND (node, 1)))) + > 32) + simp_shift (bsi, node); + break; + + case LROTATE_EXPR: + case RROTATE_EXPR: + simp_cil_node (bsi, &TREE_OPERAND (node, 0)); + simp_cil_node (bsi, &TREE_OPERAND (node, 1)); + simp_rotate (bsi, node_ptr); + break; + + case INIT_EXPR: + case MODIFY_EXPR: + simp_cil_node (bsi, &TREE_OPERAND (node, 0)); + simp_cil_node (bsi, &TREE_OPERAND (node, 1)); + gcc_assert (TREE_CODE (TREE_OPERAND (node, 1)) != CONSTRUCTOR + && TREE_CODE (TREE_OPERAND (node, 1)) != STRING_CST); + if (AGGREGATE_TYPE_P (TREE_TYPE (TREE_OPERAND (node, 1))) + && TREE_CODE (TREE_OPERAND (node, 0)) == INDIRECT_REF + && TREE_CODE (TREE_OPERAND (node, 1)) == CALL_EXPR) + split_use (*bsi, &TREE_OPERAND (node, 1)); + break; + + case NEGATE_EXPR: + case BIT_NOT_EXPR: + case TRUTH_NOT_EXPR: + case CONVERT_EXPR: + case NOP_EXPR: + case FLOAT_EXPR: + case FIX_TRUNC_EXPR: + case BIT_FIELD_REF: + simp_cil_node (bsi, &TREE_OPERAND (node, 0)); + break; + + case ADDR_EXPR: + simp_cil_node (bsi, &TREE_OPERAND (node, 0)); + if (AGGREGATE_TYPE_P (TREE_TYPE (TREE_OPERAND (node, 0))) + && TREE_CODE (TREE_OPERAND (node, 0)) == CALL_EXPR) + split_use (*bsi, &TREE_OPERAND (node, 0)); + break; + + case INDIRECT_REF: + simp_cil_node (bsi, &TREE_OPERAND (node, 0)); + if (AGGREGATE_TYPE_P (TREE_TYPE (node)) + && TREE_CODE (TREE_OPERAND (node, 0)) == CALL_EXPR) + split_use (*bsi, &TREE_OPERAND (node, 0)); + break; + + case COMPONENT_REF: + gcc_assert (TREE_CODE (TREE_OPERAND (node, 1)) == FIELD_DECL); + simp_cil_node (bsi, &TREE_OPERAND (node, 0)); + if (AGGREGATE_TYPE_P (TREE_TYPE (TREE_OPERAND (node, 0))) + && TREE_CODE (TREE_OPERAND (node, 0)) == CALL_EXPR) + split_use (*bsi, &TREE_OPERAND (node, 0)); + if (DECL_BIT_FIELD (TREE_OPERAND (node, 1))) + { + tree stmt = bsi_stmt (*bsi); + + if (TREE_CODE (stmt) == MODIFY_EXPR + && TREE_OPERAND (stmt, 0) == node) + simp_lhs_bitfield_component_ref (bsi, node_ptr); + else + simp_rhs_bitfield_component_ref (bsi, node_ptr); + } + break; + + case TARGET_MEM_REF: + simp_cil_node (bsi, &TMR_SYMBOL (node)); + simp_cil_node (bsi, &TMR_BASE (node)); + simp_cil_node (bsi, &TMR_INDEX (node)); + simp_target_mem_ref (bsi, node_ptr); + node = *node_ptr; + gcc_assert (TREE_CODE (node) == INDIRECT_REF); + if (AGGREGATE_TYPE_P (TREE_TYPE (node)) + && TREE_CODE (TREE_OPERAND (node, 0)) == CALL_EXPR) + split_use (*bsi, &TREE_OPERAND (node, 0)); + break; + + case ARRAY_REF: + simp_cil_node (bsi, &TREE_OPERAND (node, 0)); + simp_cil_node (bsi, &TREE_OPERAND (node, 1)); + simp_array_ref (bsi, node_ptr); + node = *node_ptr; + gcc_assert (TREE_CODE (node) == INDIRECT_REF); + if (AGGREGATE_TYPE_P (TREE_TYPE (node)) + && TREE_CODE (TREE_OPERAND (node, 0)) == CALL_EXPR) + split_use (*bsi, &TREE_OPERAND (node, 0)); + break; + + case RETURN_EXPR: + if (!TREE_OPERAND (node, 0) + && TREE_CODE (TREE_TYPE (DECL_RESULT (current_function_decl))) + != VOID_TYPE) + { + /* Pre-C99 code may contain void-returns for non-void functions. + In this case, return the result variable. */ + + if (res_var == NULL_TREE) + res_var = create_tmp_var (TREE_TYPE (DECL_RESULT (current_function_decl)), + "cilsimp"); + + TREE_OPERAND (node, 0) = res_var; + } + simp_cil_node (bsi, &TREE_OPERAND (node, 0)); + break; + + case RESULT_DECL: + if (res_var == NULL_TREE) + res_var = create_tmp_var (TREE_TYPE (node), "cilsimp"); + + *node_ptr = res_var; + break; + + case ABS_EXPR: + simp_cil_node (bsi, &TREE_OPERAND (node, 0)); + if (TARGET_EXPAND_ABS) + simp_abs (bsi, node_ptr); + break; + + case MAX_EXPR: + simp_cil_node (bsi, &TREE_OPERAND (node, 0)); + simp_cil_node (bsi, &TREE_OPERAND (node, 1)); + if (TARGET_EXPAND_MINMAX) + simp_min_max (bsi, node_ptr); + break; + + case MIN_EXPR: + simp_cil_node (bsi, &TREE_OPERAND (node, 0)); + simp_cil_node (bsi, &TREE_OPERAND (node, 1)); + if (TARGET_EXPAND_MINMAX) + simp_min_max (bsi, node_ptr); + break; + + default: + ; + } +} + +/* Expand the SWITCH_EXPR pointed by NODE_PTR by inserting + compare trees. The expansion occurs only if heuristics say + it is profitable; the current heuristic is based on case label + density. + BSI points to the iterator of the statement that contains *NODE_PTR + (in order to allow insertion of new statements). + BSI is passed by reference because instructions are inserted, + new basic blocks created... + NODE is passed by reference because simplification requires + replacing the node. */ + +static void +simp_switch (block_stmt_iterator *bsi, tree *node_ptr) +{ + tree node = *node_ptr; + location_t locus = EXPR_LOCATION (bsi_stmt (*bsi)); + tree vec = SWITCH_LABELS (node), vec1, vec2; + unsigned int vec_len, i; + bool first_case = true; + tree min_val = 0, max_val = 0; + double_int range, n_elems = double_int_zero, density1000, tmp_elems; + unsigned int sw1_last; + tree sw1_last_int_cst; + tree sw1_stmt, sw2_stmt; + block_stmt_iterator tmp_bsi; + basic_block bb_orig_sw, bb1, bb2; + tree cmp_stmt; + tree label1_decl, label2_decl, label1, label2; + edge_iterator ei; + edge e1, e2, tmp_edge; + + /* The switch body is lowered in gimplify.c, we should never have + switches with a non-NULL SWITCH_BODY here. */ + gcc_assert (TREE_CODE (node) == SWITCH_EXPR && vec && !SWITCH_BODY (node)); + + vec_len = TREE_VEC_LENGTH (vec); + + /* Switches made of one case are always separately (they are always + transformed into if ... then ... else ... */ + if (vec_len == 2) + simp_trivial_switch (bsi, node_ptr); + + /* Compute range of cases */ + for (i = 0; i < vec_len - 1; ++i) + { + tree elt = TREE_VEC_ELT (vec, i); + tree low = CASE_LOW (elt); + tree high = CASE_HIGH (elt); + + if (!high) + high = low; + + gcc_assert (low && high); + + /* Discard empty ranges. */ + if (INT_CST_LT (high, low)) + continue; + + /* Increment number of elements seen so far */ + n_elems = double_int_add (n_elems, TREE_INT_CST (high)); + n_elems = double_int_add (n_elems, double_int_one); + n_elems = double_int_add (n_elems, double_int_neg (TREE_INT_CST (low))); + + if (first_case) + { + min_val = low; + max_val = high; + first_case = false; + } + else + { + if (INT_CST_LT (low, min_val)) + min_val = low; + + if (INT_CST_LT (max_val, high)) + max_val = high; + } + } + gcc_assert (!INT_CST_LT (max_val, min_val)); + range = double_int_add (TREE_INT_CST (max_val), double_int_one); + range = double_int_add (range, double_int_neg (TREE_INT_CST (min_val))); + + /* If the range density is not high, keep the switch statement */ + density1000 = double_int_udiv (double_int_mul (n_elems, + shwi_to_double_int (1000)), + range, + TRUNC_DIV_EXPR); + if (! double_int_negative_p (double_int_add (density1000, + shwi_to_double_int (-333)))) + return; + + /* Insert a copy of the switch condition, if required */ + if (is_copy_required (SWITCH_COND (node))) + { + tree cond = SWITCH_COND (node); + tree var = create_tmp_var (TREE_TYPE (cond), "cilsimp"); + tree stmt = build2 (MODIFY_EXPR, TREE_TYPE (cond), var, cond); + + SET_EXPR_LOCATION (stmt, locus); + bsi_insert_before (bsi, stmt, BSI_SAME_STMT); + SWITCH_COND (node) = var; + } + + /* Compute the last CASE_LABEL_EXPR that will go to the 1st switch. + To do that, count number of elements until tmp_elems >= 0 */ + tmp_elems = double_int_neg (double_int_udiv (n_elems, + double_int_two, + TRUNC_DIV_EXPR)); + for (i = 0; i < vec_len - 1; ++i) + { + tree elt = TREE_VEC_ELT (vec, i); + tree low = CASE_LOW (elt); + tree high = CASE_HIGH (elt); + + if (!high) + high = low; + + gcc_assert (low && high); + + /* Discard empty ranges. */ + if (INT_CST_LT (high, low)) + continue; + + /* Increment number of elements seen so far */ + tmp_elems = double_int_add (tmp_elems, TREE_INT_CST (high)); + tmp_elems = double_int_add (tmp_elems, double_int_one); + tmp_elems = double_int_add (tmp_elems, + double_int_neg (TREE_INT_CST (low))); + + if (! double_int_negative_p (tmp_elems)) + break; + } + gcc_assert (! double_int_negative_p (tmp_elems)); + gcc_assert (i <= vec_len - 2); + if (i == vec_len - 2) + /* It's the last case! Then, take the previous case */ + i = vec_len - 3; + sw1_last = i; + sw1_last_int_cst = CASE_HIGH (TREE_VEC_ELT (vec, sw1_last)); + if (! sw1_last_int_cst) + sw1_last_int_cst = CASE_LOW (TREE_VEC_ELT (vec, sw1_last)); + + /* Build a COND_EXPR, replace the switch with the COND_EXPR */ + bb_orig_sw = bb_for_stmt (bsi_stmt (*bsi)); + label1_decl = create_artificial_label (); + label2_decl = create_artificial_label (); + cmp_stmt = build3 (COND_EXPR, void_type_node, + build2 (GT_EXPR, boolean_type_node, + SWITCH_COND (node), sw1_last_int_cst), + build1 (GOTO_EXPR, void_type_node, label2_decl), + build1 (GOTO_EXPR, void_type_node, label1_decl)); + SET_EXPR_LOCATION (cmp_stmt, locus); + gcc_assert (stmt_ends_bb_p (node) && bb_for_stmt (node) == bb_orig_sw); + + /* Replace the original switch with the COND_EXPR */ + *node_ptr = cmp_stmt; + set_bb_for_stmt (cmp_stmt, bb_orig_sw); + + /* Update the basic block statement iterator */ + *bsi = bsi_last (bb_orig_sw); + + /* Generate a basic block with the first switch */ + bb1 = create_empty_bb (bb_orig_sw); + bb1->count = bb_orig_sw->count / 2; + label1 = build1 (LABEL_EXPR, void_type_node, label1_decl); + vec1 = make_tree_vec (sw1_last + 2); + sw1_stmt = build3 (SWITCH_EXPR, TREE_TYPE (node), + SWITCH_COND (node), NULL, vec1); + SET_EXPR_LOCATION (sw1_stmt, locus); + tmp_bsi = bsi_start (bb1); + bsi_insert_after (&tmp_bsi, label1, BSI_NEW_STMT); + bsi_insert_after (&tmp_bsi, sw1_stmt, BSI_SAME_STMT); + + /* Generate a basic block with the second switch */ + bb2 = create_empty_bb (bb1); + bb2->count = bb_orig_sw->count - bb1->count; + label2 = build1 (LABEL_EXPR, void_type_node, label2_decl); + vec2 = make_tree_vec (vec_len - sw1_last - 1); + sw2_stmt = build3 (SWITCH_EXPR, TREE_TYPE (node), + SWITCH_COND (node), NULL, vec2); + SET_EXPR_LOCATION (sw2_stmt, locus); + tmp_bsi = bsi_start (bb2); + bsi_insert_after (&tmp_bsi, label2, BSI_NEW_STMT); + bsi_insert_after (&tmp_bsi, sw2_stmt, BSI_SAME_STMT); + + /* Build the case labels for the 1st new switch and the out-edges + of its basic block. */ + for (i = 0; i < sw1_last + 1; ++i) + { + tree elt = TREE_VEC_ELT (vec, i); + basic_block target_bb = label_to_block (CASE_LABEL (elt)); + edge e = make_edge (bb1, target_bb, 0); + + if (!e) + e = find_edge (bb1, target_bb); + + e->count = 0; + e->probability = 0; + + TREE_VEC_ELT (vec1, i) = elt; + } + { + tree elt = TREE_VEC_ELT (vec, vec_len - 1); + basic_block target_bb = label_to_block (CASE_LABEL (elt)); + edge e = make_edge (bb1, target_bb, 0); + + if (!e) + e = find_edge (bb1, target_bb); + + e->count = bb1->count; + e->probability = REG_BR_PROB_BASE; + + TREE_VEC_ELT (vec1, sw1_last + 1) = elt; + } + + /* Build the case labels for the 2nd new switch and the out-edges + of its basic block. */ + for (; i < vec_len - 1; ++i) + { + tree elt = TREE_VEC_ELT (vec, i); + basic_block target_bb = label_to_block (CASE_LABEL (elt)); + edge e = make_edge (bb2, target_bb, 0); + + if (!e) + e = find_edge (bb2, target_bb); + + e->count = 0; + e->probability = 0; + + TREE_VEC_ELT (vec2, i - sw1_last - 1) = elt; + } + { + tree elt = TREE_VEC_ELT (vec, vec_len - 1); + basic_block target_bb = label_to_block (CASE_LABEL (elt)); + edge e = make_edge (bb2, target_bb, 0); + + if (!e) + e = find_edge (bb2, target_bb); + + e->count = bb2->count; + e->probability = REG_BR_PROB_BASE; + + TREE_VEC_ELT (vec2, vec_len - sw1_last - 2) = elt; + } + + /* Update out-edges of original switch basic block */ + for (ei = ei_start (bb_orig_sw->succs); (tmp_edge = ei_safe_edge (ei)); ) + { + remove_edge (tmp_edge); + } + e1 = unchecked_make_edge (bb_orig_sw, bb1, EDGE_FALSE_VALUE); + e2 = unchecked_make_edge (bb_orig_sw, bb2, EDGE_TRUE_VALUE); + e1->probability = REG_BR_PROB_BASE / 2; + e1->count = bb1->count; + e2->probability = REG_BR_PROB_BASE - e1->probability; + e2->count = bb2->count; + + /* TODO: probabilities of the out-edges of the new basic blocks + currently do not reflect those of the out-edges of the + original switch basic block. + In order to "preserve" them, the new edges should be given + probabilities based on the original edges, but normalized. */ + + /* TODO: basic block frequencies are not updated, this makes + the profile information sanity check to fail. */ +} + +/* Expand the SWITCH_EXPR pointed by NODE_PTR, composed of just + one case, into a COND_EXPR (or GOTO_EXPR). + The expansion always occurs, since generally profitable. + BSI points to the iterator of the statement that contains *NODE_PTR + (in order to allow insertion of new statements). + BSI is passed by reference because instructions are inserted, + new basic blocks may be created... + NODE is passed by reference because simplification requires + replacing the node. */ + +static void +simp_trivial_switch (block_stmt_iterator *bsi, tree *node_ptr) +{ + tree node = *node_ptr; + location_t locus = EXPR_LOCATION (bsi_stmt (*bsi)); + tree one_case = TREE_VEC_ELT (SWITCH_LABELS (node), 0); + basic_block bb_sw = bb_for_stmt (bsi_stmt (*bsi)); + basic_block bb_case = label_to_block (CASE_LABEL (one_case)); + + /* The switch body is lowered in gimplify.c, we should never have + switches with a non-NULL SWITCH_BODY here. */ + gcc_assert (TREE_CODE (node) == SWITCH_EXPR + && !SWITCH_BODY (node) + && SWITCH_LABELS (node) + && TREE_VEC_LENGTH (SWITCH_LABELS (node)) == 2); + + gcc_assert (stmt_ends_bb_p (node) && bb_for_stmt (node) == bb_sw); + + /* Check for the easiest situation: the one case and default go + to the same basic block. + If so, SWITCH_EXPR is replaced by a GOTO_EXPR. */ + if (single_succ_p (bb_sw)) + { + tree goto_stmt; + edge e; + + gcc_assert (bb_case + == label_to_block (CASE_LABEL (TREE_VEC_ELT (SWITCH_LABELS (node), 1)))); + + /* Build the GOTO_EXPR */ + goto_stmt = build1 (GOTO_EXPR, void_type_node, CASE_LABEL (one_case)); + SET_EXPR_LOCATION (goto_stmt, locus); + + /* Update CFG */ + e = find_edge (bb_sw, bb_case); + e->flags |= EDGE_FALLTHRU; + + /* Replace the original switch with the GOTO_EXPR */ + *node_ptr = goto_stmt; + set_bb_for_stmt (goto_stmt, bb_sw); + + /* Update the basic block statement iterator */ + *bsi = bsi_last (bb_sw); + } + + /* Check whether the one case is not a range. + If it is not, SWITCH_EXPR is replaced by a single COND_EXPR. */ + else if (! CASE_HIGH (one_case)) + { + tree deft = TREE_VEC_ELT (SWITCH_LABELS (node), 1); + tree cmp_stmt; + basic_block bb_deft = label_to_block (CASE_LABEL (deft)); + edge e1, e2; + + /* Build the COND_EXPR */ + cmp_stmt = build3 (COND_EXPR, void_type_node, + build2 (EQ_EXPR, boolean_type_node, + SWITCH_COND (node), + CASE_LOW (one_case)), + build1 (GOTO_EXPR, void_type_node, + CASE_LABEL (one_case)), + build1 (GOTO_EXPR, void_type_node, + CASE_LABEL (deft))); + SET_EXPR_LOCATION (cmp_stmt, locus); + + /* Update CFG */ + e1 = find_edge (bb_sw, bb_case); + e2 = find_edge (bb_sw, bb_deft); + gcc_assert (e1 && e2 && e1 != e2); + gcc_assert ((e1->flags & EDGE_FALSE_VALUE) == 0); + gcc_assert ((e2->flags & EDGE_TRUE_VALUE) == 0); + e1->flags |= EDGE_TRUE_VALUE; + e2->flags |= EDGE_FALSE_VALUE; + + /* Replace the original switch with the COND_EXPR */ + *node_ptr = cmp_stmt; + set_bb_for_stmt (cmp_stmt, bb_sw); + + /* Update the basic block statement iterator */ + *bsi = bsi_last (bb_sw); + } + + /* The one case is a range. + Therefore, SWITCH_EXPR is replaced by a couple of COND_EXPRs. */ + else + { + tree deft = TREE_VEC_ELT (SWITCH_LABELS (node), 1); + tree label_decl = create_artificial_label (); + tree cmp1_stmt, cmp2_stmt, label; + basic_block bb_deft = label_to_block (CASE_LABEL (deft)); + basic_block bb_new; + block_stmt_iterator tmp_bsi; + edge e1, e2, e3, e4; + int e1_orig_prob, e2_orig_prob; + + /* Build the 1st COND_EXPR */ + cmp1_stmt = build3 (COND_EXPR, void_type_node, + build2 (GE_EXPR, boolean_type_node, + SWITCH_COND (node), + CASE_LOW (one_case)), + build1 (GOTO_EXPR, void_type_node, label_decl), + build1 (GOTO_EXPR, void_type_node, + CASE_LABEL (deft))); + SET_EXPR_LOCATION (cmp1_stmt, locus); + + /* Replace the original switch with the 1st COND_EXPR */ + *node_ptr = cmp1_stmt; + set_bb_for_stmt (cmp1_stmt, bb_sw); + + /* Build the 2nd COND_EXPR */ + cmp2_stmt = build3 (COND_EXPR, void_type_node, + build2 (LE_EXPR, boolean_type_node, + SWITCH_COND (node), + CASE_HIGH (one_case)), + build1 (GOTO_EXPR, void_type_node, + CASE_LABEL (one_case)), + build1 (GOTO_EXPR, void_type_node, + CASE_LABEL (deft))); + SET_EXPR_LOCATION (cmp2_stmt, locus); + + /* Create new basic block and insert the 2nd COND_EXPR */ + bb_new = create_empty_bb (bb_sw); + label = build1 (LABEL_EXPR, void_type_node, label_decl); + tmp_bsi = bsi_start (bb_new); + bsi_insert_after (&tmp_bsi, label, BSI_NEW_STMT); + bsi_insert_after (&tmp_bsi, cmp2_stmt, BSI_SAME_STMT); + + /* Update CFG */ + e1 = find_edge (bb_sw, bb_case); + e2 = find_edge (bb_sw, bb_deft); + gcc_assert (e1 && e2 && e1 != e2); + gcc_assert ((e1->flags & EDGE_FALSE_VALUE) == 0); + gcc_assert ((e2->flags & EDGE_TRUE_VALUE) == 0); + redirect_edge_succ (e1, bb_new); + e2->flags |= EDGE_FALSE_VALUE; + e3 = unchecked_make_edge (bb_new, bb_case, EDGE_TRUE_VALUE); + e3->flags = e1->flags | EDGE_TRUE_VALUE;; + e4 = unchecked_make_edge (bb_new, bb_deft, EDGE_FALSE_VALUE); + e4->flags = e2->flags; + e1->flags = EDGE_TRUE_VALUE; + + /* Update edge and basic block counts */ + e3->count = e1->count; + e4->count = e2->count / 2; + e2->count = e2->count - e4->count; + e1->count = e3->count + e4->count; + bb_new->count = e1->count; + + /* Update edge probabilities */ + e1_orig_prob = e1->probability; + e2_orig_prob = e2->probability; + e2->probability = e2_orig_prob / 2; + e1->probability = REG_BR_PROB_BASE - e2->probability; + e3->probability = (e1_orig_prob * REG_BR_PROB_BASE) / e1->probability; + e4->probability = REG_BR_PROB_BASE - e3->probability; + + /* Update the basic block statement iterator */ + *bsi = tmp_bsi; + } +} + +/* Remove the ABS_EXPR pointed by NODE_PTR by inserting + explicit control flow. + BSI points to the iterator of the statement that contains *NODE_PTR + (in order to allow insertion of new statements). + BSI is passed by reference because instructions are inserted, + new basic blocks created... + NODE is passed by reference because simplification requires + replacing the node. */ + +static void +simp_abs (block_stmt_iterator *bsi, tree *node_ptr) +{ + tree node = *node_ptr; + location_t locus = EXPR_LOCATION (bsi_stmt (*bsi)); + tree op; + tree label_decl_neg = create_artificial_label (); + tree label_decl_sel = create_artificial_label (); + tree label_neg, label_sel; + tree sel_var; + tree orig_stmt, cmp_stmt, asn_op_stmt, asn_neg_stmt; + basic_block bb_comp, bb_neg, bb_sel; + edge tmp_edge; + block_stmt_iterator tmp_bsi; + gcov_type count; + + gcc_assert (TREE_CODE (node) == ABS_EXPR); + + /* Insert a statement that copies the operand. + This is always done: it is always useful because it avoids + generating an extra basic block. */ + op = TREE_OPERAND (node, 0); + sel_var = create_tmp_var (TREE_TYPE (op), "cilsimp"); + asn_op_stmt = build2 (MODIFY_EXPR, TREE_TYPE (op), sel_var, op); + SET_EXPR_LOCATION (asn_op_stmt, locus); + bsi_insert_before (bsi, asn_op_stmt, BSI_SAME_STMT); + + /* Insert the comparison statement */ + cmp_stmt = build3 (COND_EXPR, void_type_node, + build2 (GE_EXPR, boolean_type_node, sel_var, + build_int_cst (TREE_TYPE (op), 0)), + build1 (GOTO_EXPR, void_type_node, label_decl_sel), + build1 (GOTO_EXPR, void_type_node, label_decl_neg)); + SET_EXPR_LOCATION (cmp_stmt, locus); + bsi_insert_before (bsi, cmp_stmt, BSI_SAME_STMT); + + /* Update cfg */ + orig_stmt = bsi_stmt (*bsi); + bb_comp = bb_for_stmt (orig_stmt); + count = bb_comp->count; + tmp_edge = split_block (bb_comp, cmp_stmt); + bb_sel = tmp_edge->dest; + bb_sel->count = count; + remove_edge (tmp_edge); + bb_neg = create_empty_bb (bb_comp); + bb_neg->count = count / 2; + unchecked_make_edge (bb_comp, bb_neg, EDGE_FALSE_VALUE); + make_single_succ_edge (bb_neg, bb_sel, EDGE_FALLTHRU); + + /* Insert labels and statements into neg bb */ + label_neg = build1 (LABEL_EXPR, void_type_node, label_decl_neg); + asn_neg_stmt = build2 (MODIFY_EXPR, TREE_TYPE (op), + sel_var, + build1 (NEGATE_EXPR, TREE_TYPE (op), sel_var)); + SET_EXPR_LOCATION (asn_neg_stmt, locus); + tmp_bsi = bsi_start (bb_neg); + bsi_insert_after (&tmp_bsi, label_neg, BSI_NEW_STMT); + bsi_insert_after (&tmp_bsi, asn_neg_stmt, BSI_SAME_STMT); + + /* Insert a label into sel bb */ + label_sel = build1 (LABEL_EXPR, void_type_node, label_decl_sel); + tmp_bsi = bsi_start (bb_sel); + bsi_insert_before (&tmp_bsi, label_sel, BSI_SAME_STMT); + + /* Update current node in order to use the select variable */ + *node_ptr = sel_var; + + /* Update the basic block statement iterator */ + gcc_assert (bsi_stmt (tmp_bsi) == orig_stmt); + *bsi = tmp_bsi; +} + +/* Remove the MAX_EXPR or MIN_EXPR pointed by NODE_PTR by inserting + explicit control flow. + BSI points to the iterator of the statement that contains *NODE_PTR + (in order to allow insertion of new statements). + BSI is passed by reference because instructions are inserted, + new basic blocks created... + NODE is passed by reference because simplification requires + replacing the node. */ + +static void +simp_min_max (block_stmt_iterator *bsi, tree *node_ptr) +{ + tree node = *node_ptr; + location_t locus = EXPR_LOCATION (bsi_stmt (*bsi)); + bool is_max; + tree op0, op1; + tree label_decl_op0 = create_artificial_label (); + tree label_decl_op1 = create_artificial_label (); + tree label_op0, label_op1; + tree sel_var; + tree orig_stmt, cmp_stmt, asn_op0_stmt, asn_op1_stmt; + basic_block bb_comp, bb_op0, bb_op1, bb_sel; + edge tmp_edge; + block_stmt_iterator tmp_bsi; + gcov_type count; + + gcc_assert (TREE_CODE (node) == MAX_EXPR || TREE_CODE (node) == MIN_EXPR); + is_max = (TREE_CODE (node) == MAX_EXPR); + + /* Make sure that the two operands have no side effects */ + op0 = TREE_OPERAND (node, 0); + if (is_copy_required (op0)) + { + tree var = create_tmp_var (TREE_TYPE (op0), "cilsimp"); + tree stmt = build2 (MODIFY_EXPR, TREE_TYPE (op0), var, op0); + + SET_EXPR_LOCATION (stmt, locus); + bsi_insert_before (bsi, stmt, BSI_SAME_STMT); + TREE_OPERAND (node, 0) = var; + op0 = var; + } + op1 = TREE_OPERAND (node, 1); + if (is_copy_required (op1)) + { + tree var = create_tmp_var (TREE_TYPE (op1), "cilsimp"); + tree stmt = build2 (MODIFY_EXPR, TREE_TYPE (op1), var, op1); + + SET_EXPR_LOCATION (stmt, locus); + bsi_insert_before (bsi, stmt, BSI_SAME_STMT); + TREE_OPERAND (node, 1) = var; + op1 = var; + } + + /* Insert the comparison statement */ + cmp_stmt = build3 (COND_EXPR, void_type_node, + build2 (is_max ? GT_EXPR : LT_EXPR, + boolean_type_node, op0, op1), + build1 (GOTO_EXPR, void_type_node, label_decl_op0), + build1 (GOTO_EXPR, void_type_node, label_decl_op1)); + SET_EXPR_LOCATION (cmp_stmt, locus); + bsi_insert_before (bsi, cmp_stmt, BSI_SAME_STMT); + + /* Update cfg */ + orig_stmt = bsi_stmt (*bsi); + bb_comp = bb_for_stmt (orig_stmt); + count = bb_comp->count; + tmp_edge = split_block (bb_comp, cmp_stmt); + bb_sel = tmp_edge->dest; + bb_sel->count = count; + remove_edge (tmp_edge); + bb_op0 = create_empty_bb (bb_comp); + bb_op1 = create_empty_bb (bb_op0); + bb_op0->count = count / 2; + bb_op1->count = count - bb_op0->count; + unchecked_make_edge (bb_comp, bb_op0, EDGE_TRUE_VALUE); + make_single_succ_edge (bb_op0, bb_sel, EDGE_FALLTHRU); + unchecked_make_edge (bb_comp, bb_op1, EDGE_FALSE_VALUE); + make_single_succ_edge (bb_op1, bb_sel, EDGE_FALLTHRU); + + /* Insert labels and statements into op0 bb */ + sel_var = create_tmp_var (TREE_TYPE (node), "cilsimp"); + label_op0 = build1 (LABEL_EXPR, void_type_node, label_decl_op0); + asn_op0_stmt = build2 (MODIFY_EXPR, TREE_TYPE (op0), sel_var, op0); + SET_EXPR_LOCATION (asn_op0_stmt, locus); + tmp_bsi = bsi_start (bb_op0); + bsi_insert_after (&tmp_bsi, label_op0, BSI_NEW_STMT); + bsi_insert_after (&tmp_bsi, asn_op0_stmt, BSI_SAME_STMT); + + /* Insert labels and statements into op1 bb */ + label_op1 = build1 (LABEL_EXPR, void_type_node, label_decl_op1); + asn_op1_stmt = build2 (MODIFY_EXPR, TREE_TYPE (op1), sel_var, op1); + SET_EXPR_LOCATION (asn_op1_stmt, locus); + tmp_bsi = bsi_start (bb_op1); + bsi_insert_after (&tmp_bsi, label_op1, BSI_NEW_STMT); + bsi_insert_after (&tmp_bsi, asn_op1_stmt, BSI_SAME_STMT); + + /* Update current node in order to use the select variable */ + *node_ptr = sel_var; + + /* Update the basic block statement iterator */ + gcc_assert (bsi_stmt (bsi_start (bb_sel)) == orig_stmt); + *bsi = bsi_start (bb_sel); +} + +/* Remove the LROTATE_EXPR or RROTATE_EXPR pointed by NODE_PTR + by inserting shifts and bit operations. + BSI points to the iterator of the statement that contains *NODE_PTR + (in order to allow insertion of new statements). + BSI is passed by reference because instructions are inserted, + new basic blocks created... + NODE is passed by reference because simplification requires + replacing the node. */ + +static void +simp_rotate (block_stmt_iterator *bsi, tree *node_ptr) +{ + tree node = *node_ptr; + location_t locus = EXPR_LOCATION (bsi_stmt (*bsi)); + bool left = (TREE_CODE (node) == LROTATE_EXPR); + tree op0_uns_type; + tree op0, op1; + tree t1, t2; + + gcc_assert (TREE_CODE (node) == LROTATE_EXPR + || TREE_CODE (node) == RROTATE_EXPR); + + /* Rotation is replaced by shifts on unsigned values: + generate the unsigned version of first operand type. */ + op0 = TREE_OPERAND (node, 0); + op0_uns_type = build_distinct_type_copy (TREE_TYPE (op0)); + TYPE_UNSIGNED (op0_uns_type) = 1; + op0 = fold_convert (op0_uns_type, op0); + + /* Convert the second operand to 32-bit */ + op1 = fold_convert (uint32_type, TREE_OPERAND (node, 1)); + + /* Make sure that the two operands have no side effects */ + if (is_copy_required (op0)) + { + tree var = create_tmp_var (TREE_TYPE (op0), "cilsimp"); + tree stmt = build2 (MODIFY_EXPR, TREE_TYPE (op0), var, op0); + + SET_EXPR_LOCATION (stmt, locus); + bsi_insert_before (bsi, stmt, BSI_SAME_STMT); + TREE_OPERAND (node, 0) = var; + op0 = var; + } + if (is_copy_required (op1)) + { + tree var = create_tmp_var (TREE_TYPE (op1), "cilsimp"); + tree stmt = build2 (MODIFY_EXPR, TREE_TYPE (op1), var, op1); + + SET_EXPR_LOCATION (stmt, locus); + bsi_insert_before (bsi, stmt, BSI_SAME_STMT); + TREE_OPERAND (node, 1) = var; + op1 = var; + } + + /* Build first shift */ + t1 = build2 (left ? LSHIFT_EXPR : RSHIFT_EXPR, op0_uns_type, + op0, + op1); + + /* Build second shift */ + t2 = fold_build2 (left ? RSHIFT_EXPR : LSHIFT_EXPR, op0_uns_type, + op0, + build2 (MINUS_EXPR, uint32_type, + fold_convert (uint32_type, + TYPE_SIZE (TREE_TYPE (op0))), + op1)); + + /* Gimplify the two shifts */ + t1 = force_gimple_operand_bsi (bsi, t1, TRUE, NULL); + t2 = force_gimple_operand_bsi (bsi, t2, TRUE, NULL); + + /* Build the rotate result and gimplify it */ + t1 = build2 (BIT_IOR_EXPR, op0_uns_type, t1, t2); + t1 = fold_convert (TREE_TYPE (TREE_OPERAND (node, 0)), t1); + t1 = force_gimple_operand_bsi (bsi, t1, TRUE, NULL); + + /* Update the current node */ + *node_ptr = t1; +} + +/* Given the LSHIFT_EXPR or RSHIFT_EXPR + in NODE with the second operand of an integer type bigger than + 32 bits, convert such operand to a 32-bit type. + BSI points to the iterator of the statement that contains *NODE_PTR + (in order to allow insertion of new statements). + BSI is passed by reference because instructions are inserted. */ + +static void +simp_shift (block_stmt_iterator *bsi, tree node) +{ + tree t; + + gcc_assert (TREE_CODE (node) == LSHIFT_EXPR + || TREE_CODE (node) == RSHIFT_EXPR); + + /* Generate the type conversion */ + t = fold_convert (uint32_type, TREE_OPERAND (node, 1)); + + /* Gimplify the equivalent expression and update the current node */ + TREE_OPERAND (node, 1) = force_gimple_operand_bsi (bsi, t, FALSE, NULL); +} + +/* Expand the TARGET_MEM_REF pointed by NODE_PTR by inserting + the equivalent sums and multiplication. + BSI points to the iterator of the statement that contains *NODE_PTR + (in order to allow insertion of new statements). + BSI is passed by reference because instructions are inserted. + NODE is passed by reference because simplification requires + replacing the node. */ + +static void +simp_target_mem_ref (block_stmt_iterator *bsi, tree *node_ptr) +{ + tree node = *node_ptr; + tree t1, t2; + + gcc_assert (TREE_CODE (node) == TARGET_MEM_REF); + + /* Generate the equivalent expression */ + + if (TMR_BASE (node)) + { + if (TMR_SYMBOL (node)) + t1 = build2 (PLUS_EXPR, TREE_TYPE (TMR_BASE (node)), + TMR_BASE (node), + TMR_SYMBOL (node)); + else + t1 = TMR_BASE (node); + } + else + t1 = TMR_SYMBOL (node); + + if (TMR_INDEX (node)) + { + if (TMR_STEP (node)) + t2 = build2 (MULT_EXPR, TREE_TYPE (TMR_INDEX (node)), + TMR_INDEX (node), + TMR_STEP (node)); + else + t2 = TMR_INDEX (node); + + gcc_assert (t2); + if (TMR_OFFSET (node)) + t2 = build2 (PLUS_EXPR, TREE_TYPE (t2), + TMR_OFFSET (node), + t2); + } + else + { + gcc_assert (! TMR_STEP (node)); + t2 = TMR_OFFSET (node); + } + + if (t1) + { + if (t2) + t1 = build2 (PLUS_EXPR, TREE_TYPE (t1), t1, t2); + } + else + t1 = t2; + + gcc_assert (t1 && t1 != error_mark_node); + t1 = build1 (INDIRECT_REF, TREE_TYPE (node), t1); + + /* Gimplify the equivalent expression and update the current node */ + *node_ptr = force_gimple_operand_bsi (bsi, t1, FALSE, NULL); +} + +/* Expand the ARRAY_REF pointed by NODE_PTR by inserting + the equivalent sums and multiplication. + BSI points to the iterator of the statement that contains *NODE_PTR + (in order to allow insertion of new statements). + BSI is passed by reference because instructions are inserted. + NODE is passed by reference because simplification requires + replacing the node. */ + +static void +simp_array_ref (block_stmt_iterator *bsi, tree *node_ptr) +{ + tree node = *node_ptr; + location_t locus = EXPR_LOCATION (bsi_stmt (*bsi)); + tree base, index; + tree base_type; + tree t1, t2; + tree tmp_var, tmp_stmt; + + gcc_assert (TREE_CODE (node) == ARRAY_REF); + + base = TREE_OPERAND (node, 0); + index = TREE_OPERAND (node, 1); + + /* The type of base is a pointer, no longer an array! */ + base_type = build0 (POINTER_TYPE, TREE_TYPE (TREE_TYPE (base))); + layout_type (base_type); + + /* Generate the equivalent gimplified expression */ + + t1 = fold_convert (long_integer_type_node, index); + t1 = force_gimple_operand_bsi (bsi, t1, TRUE, NULL); + gcc_assert (t1 && t1 != error_mark_node); + + t2 = fold_convert (long_integer_type_node, array_ref_element_size (node)); + t2 = force_gimple_operand_bsi (bsi, t2, TRUE, NULL); + gcc_assert (t2 && t2 != error_mark_node); + + if (TREE_CODE (t1) == INTEGER_CST + && (double_int_zero_p (TREE_INT_CST (t1)) + || double_int_one_p (TREE_INT_CST (t1)))) + { + if (double_int_one_p (TREE_INT_CST (t1))) + t1 = t2; + } + else if (TREE_CODE (t2) == INTEGER_CST + && (double_int_zero_p (TREE_INT_CST (t2)) + || double_int_one_p (TREE_INT_CST (t2)))) + { + if (double_int_zero_p (TREE_INT_CST (t2))) + t1 = t2; + } + else + { + tmp_var = create_tmp_var (long_integer_type_node, "cilsimp"); + tmp_stmt = build2 (MODIFY_EXPR, long_integer_type_node, + tmp_var, + fold_build2 (MULT_EXPR, long_integer_type_node, + t1, + t2)); + SET_EXPR_LOCATION (tmp_stmt, locus); + t1 = tmp_var; + bsi_insert_before (bsi, tmp_stmt, BSI_SAME_STMT); + } + gcc_assert (t1 && t1 != error_mark_node); + + tmp_var = create_tmp_var (base_type, "cilsimp"); + tmp_stmt = build2 (MODIFY_EXPR, base_type, + tmp_var, + build1 (ADDR_EXPR, base_type, base)); + gcc_assert (TREE_CODE (base) != CALL_EXPR); + SET_EXPR_LOCATION (tmp_stmt, locus); + t2 = tmp_var; + bsi_insert_before (bsi, tmp_stmt, BSI_SAME_STMT); + + if (TREE_CODE (t1) == INTEGER_CST + && double_int_zero_p (TREE_INT_CST (t1))) + { + t1 = t2; + } + else + { + tmp_var = create_tmp_var (base_type, "cilsimp"); + tmp_stmt = build2 (MODIFY_EXPR, base_type, + tmp_var, + build2 (PLUS_EXPR, base_type, + t2, + t1)); + SET_EXPR_LOCATION (tmp_stmt, locus); + t1 = tmp_var; + bsi_insert_before (bsi, tmp_stmt, BSI_SAME_STMT); + } + gcc_assert (t1 && t1 != error_mark_node); + + /* Update the current node */ + *node_ptr = build1 (INDIRECT_REF, TREE_TYPE (node), t1); +} + +/* Expand the COMPONENT_REF (pointed by NODE_PTR) accessing + a BIT_FIELD_DECL and being on a right-hand side by transforming it + into an INDIRECT_REF and applying the necessary bit mask operations. + BSI points to the iterator of the statement that contains *NODE_PTR + (in order to allow insertion of new statements). + BSI is passed by reference because instructions are inserted. + NODE is passed by reference because simplification requires + replacing the node. */ + +static void +simp_rhs_bitfield_component_ref (block_stmt_iterator *bsi, tree *node_ptr) +{ + tree node = *node_ptr; + location_t locus = EXPR_LOCATION (bsi_stmt (*bsi)); + tree obj = TREE_OPERAND (node, 0); + tree fld = TREE_OPERAND (node, 1); + tree fld_type, fld_off ; + unsigned int cont_size, bfld_size, bfld_off; + tree new_type, obj_ptr_type; + tree tmp_var, tmp_stmt; + tree t; + HOST_WIDEST_INT off; + + gcc_assert (TREE_CODE (node) == COMPONENT_REF); + gcc_assert (DECL_BIT_FIELD (fld)); + + /* Extract bit field layout */ + fld_type = DECL_BIT_FIELD_TYPE (fld); + fld_off = DECL_FIELD_OFFSET (fld); + cont_size = TREE_INT_CST_LOW (TYPE_SIZE (fld_type)); + bfld_size = TYPE_PRECISION (TREE_TYPE (fld)); + bfld_off = TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (fld)) & (cont_size - 1); + gcc_assert (cont_size >= TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (node)))); + + /* Build the type corresponding of a pointer to the object */ + obj_ptr_type = build0 (POINTER_TYPE, TREE_TYPE (obj)); + layout_type (obj_ptr_type); + + /* Build the new type for the equivalent access */ + new_type = get_unsigned_integer_type (cont_size); + + /* Build the (gimplified) equivalent expression */ + + tmp_var = create_tmp_var (obj_ptr_type, "cilsimp"); + tmp_stmt = build2 (MODIFY_EXPR, obj_ptr_type, + tmp_var, + build1 (ADDR_EXPR, obj_ptr_type, obj)); + gcc_assert (TREE_CODE (obj) != CALL_EXPR); + SET_EXPR_LOCATION (tmp_stmt, locus); + t = tmp_var; + bsi_insert_before (bsi, tmp_stmt, BSI_SAME_STMT); + + off = TREE_INT_CST_LOW (fld_off) + + ((TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (fld)) + & ~(cont_size - 1)) + / 8); + + gcc_assert (TREE_INT_CST_HIGH (fld_off) == 0); + + if (off > 0) + { + tmp_var = create_tmp_var (obj_ptr_type, "cilsimp"); + tmp_stmt = build2 (MODIFY_EXPR, obj_ptr_type, + tmp_var, + build2 (PLUS_EXPR, obj_ptr_type, + t, + build_int_cst (long_integer_type_node, off))); + SET_EXPR_LOCATION (tmp_stmt, locus); + t = tmp_var; + bsi_insert_before (bsi, tmp_stmt, BSI_SAME_STMT); + } + + tmp_var = create_tmp_var (new_type, "cilsimp"); + tmp_stmt = build2 (MODIFY_EXPR, new_type, + tmp_var, + build1 (INDIRECT_REF, new_type, t)); + SET_EXPR_LOCATION (tmp_stmt, locus); + t = tmp_var; + bsi_insert_before (bsi, tmp_stmt, BSI_SAME_STMT); + + if (cont_size > bfld_size + bfld_off) + { + tmp_var = create_tmp_var (new_type, "cilsimp"); + tmp_stmt = build2 (MODIFY_EXPR, new_type, + tmp_var, + build2 (LSHIFT_EXPR, new_type, + t, + build_int_cstu (uint32_type, + cont_size + - bfld_size - bfld_off))); + SET_EXPR_LOCATION (tmp_stmt, locus); + t = tmp_var; + bsi_insert_before (bsi, tmp_stmt, BSI_SAME_STMT); + } + + if (cont_size > bfld_size) + { + tmp_var = create_tmp_var (new_type, "cilsimp"); + tmp_stmt = build2 (MODIFY_EXPR, new_type, + tmp_var, + build2 (RSHIFT_EXPR, new_type, + t, + build_int_cstu (uint32_type, + cont_size - bfld_size))); + SET_EXPR_LOCATION (tmp_stmt, locus); + t = tmp_var; + bsi_insert_before (bsi, tmp_stmt, BSI_SAME_STMT); + } + + gcc_assert (t && t != error_mark_node); + + /* Update the current node */ + *node_ptr = fold_convert (TREE_TYPE (node), t); +} + +/* Expand the COMPONENT_REF (pointed by NODE_PTR) accessing + a BIT_FIELD_DECL and being on a left-hand side by transforming it + into an INDIRECT_REF and applying the necessary bit mask operations. + BSI points to the iterator of the statement that contains *NODE_PTR + (in order to allow insertion of new statements). + BSI is passed by reference because instructions are inserted. + NODE is passed by reference because simplification requires + replacing the node. */ + +static void +simp_lhs_bitfield_component_ref (block_stmt_iterator *bsi, tree *node_ptr) +{ + tree node = *node_ptr; + location_t locus = EXPR_LOCATION (bsi_stmt (*bsi)); + tree obj = TREE_OPERAND (node, 0); + tree fld = TREE_OPERAND (node, 1); + tree fld_type, fld_off ; + unsigned int cont_size, bfld_size, bfld_off; + tree stmt = bsi_stmt (*bsi), rhs, addr; + tree new_type, obj_ptr_type; + tree tmp_var, tmp_stmt; + tree t; + HOST_WIDEST_INT off; + unsigned HOST_WIDEST_INT mask = 0; + + gcc_assert (TREE_CODE (node) == COMPONENT_REF); + gcc_assert (DECL_BIT_FIELD (fld)); + gcc_assert (TREE_CODE (stmt) == MODIFY_EXPR + && TREE_OPERAND (stmt, 0) == node); + + /* Extract bit field layout */ + fld_type = DECL_BIT_FIELD_TYPE (fld); + fld_off = DECL_FIELD_OFFSET (fld); + cont_size = TREE_INT_CST_LOW (TYPE_SIZE (fld_type)); + bfld_size = TYPE_PRECISION (TREE_TYPE (fld)); + bfld_off = TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (fld)) & (cont_size - 1); + gcc_assert (cont_size >= TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (node)))); + + /* Build the new type for the equivalent access */ + new_type = get_unsigned_integer_type (cont_size); + + /* Build the type corresponding of a pointer to the object */ + obj_ptr_type = build0 (POINTER_TYPE, TREE_TYPE (obj)); + layout_type (obj_ptr_type); + + /* Convert the original rhs into the new type */ + gcc_assert (TREE_CODE (TREE_OPERAND (stmt, 1)) == VAR_DECL); + tmp_var = create_tmp_var (new_type, "cilsimp"); + tmp_stmt = build2 (MODIFY_EXPR, new_type, + tmp_var, + build1 (NOP_EXPR, new_type, TREE_OPERAND (stmt, 1))); + SET_EXPR_LOCATION (tmp_stmt, locus); + rhs = tmp_var; + bsi_insert_before (bsi, tmp_stmt, BSI_SAME_STMT); + + /* Shift the rhs in order that it is in the right bit position */ + if (cont_size > bfld_size) + { + tmp_var = create_tmp_var (new_type, "cilsimp"); + tmp_stmt = build2 (MODIFY_EXPR, new_type, + tmp_var, + build2 (LSHIFT_EXPR, new_type, + rhs, + build_int_cstu (uint32_type, + cont_size - bfld_size))); + SET_EXPR_LOCATION (tmp_stmt, locus); + rhs = tmp_var; + bsi_insert_before (bsi, tmp_stmt, BSI_SAME_STMT); + } + if (cont_size > bfld_size + bfld_off) + { + /* Remark: new_type is unsigned by construction: + therefore, the right shift doesn't introduce any 1 */ + + tmp_var = create_tmp_var (new_type, "cilsimp"); + tmp_stmt = build2 (MODIFY_EXPR, new_type, + tmp_var, + build2 (RSHIFT_EXPR, new_type, + rhs, + build_int_cstu (uint32_type, + cont_size + - bfld_size - bfld_off))); + SET_EXPR_LOCATION (tmp_stmt, locus); + rhs = tmp_var; + bsi_insert_before (bsi, tmp_stmt, BSI_SAME_STMT); + } + + /* Build expression to compute the address to be accessed */ + tmp_var = create_tmp_var (obj_ptr_type, "cilsimp"); + tmp_stmt = build2 (MODIFY_EXPR, obj_ptr_type, + tmp_var, + build1 (ADDR_EXPR, obj_ptr_type, obj)); + gcc_assert (TREE_CODE (obj) != CALL_EXPR); + SET_EXPR_LOCATION (tmp_stmt, locus); + t = tmp_var; + bsi_insert_before (bsi, tmp_stmt, BSI_SAME_STMT); + + off = TREE_INT_CST_LOW (fld_off) + + ((TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (fld)) + & ~(cont_size - 1)) + / 8); + + gcc_assert (TREE_INT_CST_HIGH (fld_off) == 0); + + if (off > 0) + { + tmp_var = create_tmp_var (obj_ptr_type, "cilsimp"); + tmp_stmt = build2 (MODIFY_EXPR, obj_ptr_type, + tmp_var, + build2 (PLUS_EXPR, obj_ptr_type, + t, + build_int_cst (long_integer_type_node, off))); + SET_EXPR_LOCATION (tmp_stmt, locus); + t = tmp_var; + bsi_insert_before (bsi, tmp_stmt, BSI_SAME_STMT); + } + addr = t; + + /* Read existing value at the address just computed */ + tmp_var = create_tmp_var (new_type, "cilsimp"); + tmp_stmt = build2 (MODIFY_EXPR, new_type, + tmp_var, + build1 (INDIRECT_REF, new_type, t)); + SET_EXPR_LOCATION (tmp_stmt, locus); + t = tmp_var; + bsi_insert_before (bsi, tmp_stmt, BSI_SAME_STMT); + + /* Compute the mask to be applied to the existing value */ + gcc_assert (HOST_BITS_PER_WIDEST_INT >= 64); + mask |= (1 << (cont_size - bfld_size - bfld_off)) - 1; + mask <<= bfld_off + bfld_size; + mask |= (1 << bfld_off) - 1; + + /* Apply the mask to the existing value */ + tmp_var = create_tmp_var (new_type, "cilsimp"); + tmp_stmt = build2 (MODIFY_EXPR, new_type, + tmp_var, + build2 (BIT_AND_EXPR, new_type, + t, + build_int_cstu (new_type, mask))); + SET_EXPR_LOCATION (tmp_stmt, locus); + t = tmp_var; + bsi_insert_before (bsi, tmp_stmt, BSI_SAME_STMT); + + /* Compute the new value for the rhs of the current statement */ + tmp_var = create_tmp_var (new_type, "cilsimp"); + tmp_stmt = build2 (MODIFY_EXPR, new_type, + tmp_var, + build2 (BIT_IOR_EXPR, new_type, + rhs, + t)); + SET_EXPR_LOCATION (tmp_stmt, locus); + rhs = tmp_var; + bsi_insert_before (bsi, tmp_stmt, BSI_SAME_STMT); + + /* Update the current statement (and the current node) */ + *node_ptr = build1 (INDIRECT_REF, new_type, addr); + TREE_OPERAND (stmt, 1) = rhs; +} + +/* Expand the INIT_EXPR (or MODIFY_EXPR) in NODE having + a CONSTRUCTOR or STRING_CST on the right side into a sequence + of simpler (here, it means "not involving CONSTRUCTOR or + STRING_CST nodes) initializer statements. + Beware that, differently from most simplification functions, + this expansion leaves GIMPLE nodes that need further simplifications; + hence, this function should only be called during + the pre-simplification. + BSI points to the iterator of the statement that contains NODE + (in order to allow insertion of new statements). + BSI is passed by reference because instructions are inserted. */ + +static void +pre_simp_init (block_stmt_iterator *bsi, tree node) +{ + location_t locus = EXPR_LOCATION (bsi_stmt (*bsi)); + tree lhs = TREE_OPERAND (node, 0); + tree rhs = TREE_OPERAND (node, 1); + tree stmt_list = 0; + block_stmt_iterator tmp_bsi; + tree_stmt_iterator i; + + gcc_assert (bsi_stmt (*bsi) == node); + gcc_assert (TREE_CODE (node) == INIT_EXPR + || TREE_CODE (node) == MODIFY_EXPR); + gcc_assert (TREE_CODE (rhs) == CONSTRUCTOR || TREE_CODE (rhs) == STRING_CST); + + /* Expand the constructor into a separate statement list */ + expand_init_to_stmt_list (lhs, rhs, &stmt_list, FALSE); + gcc_assert (TREE_CODE (stmt_list) == STATEMENT_LIST); + + /* Gimplify the new statements and insert them */ + tmp_bsi = *bsi; + for (i = tsi_start (stmt_list); !tsi_end_p (i); tsi_next (&i)) + { + tree stmt; + + /* Gimplify the new statement */ + gimplify_stmt (tsi_stmt_ptr (i)); + + /* Insert the new statements before the old */ + stmt = tsi_stmt (i); + if (TREE_CODE (stmt) == STATEMENT_LIST) + { + tree_stmt_iterator j; + + for (j = tsi_start (stmt); !tsi_end_p (j); tsi_next (&j)) + { + tree t = tsi_stmt (j); + + SET_EXPR_LOCATION (t, locus); + bsi_insert_after (&tmp_bsi, t, BSI_NEW_STMT); + } + } + else if (stmt != NULL) + { + SET_EXPR_LOCATION (stmt, locus); + bsi_insert_after (&tmp_bsi, stmt, BSI_NEW_STMT); + } + } + + /* Remove the old statement */ + bsi_remove (bsi, false); + + /* Update the basic block statement iterator */ + *bsi = tmp_bsi; +} + +/* Make sure that the tree pointed by NODE_PTR is a VAR_DECL. + In case, split the statement containing NODE_PTR into two + by creating a new local variable. + BSI points to the iterator of the statement that contains NODE_PTR + (in order to allow insertion of new statements). */ + +static void +split_use (block_stmt_iterator bsi, tree *node_ptr) +{ + tree node = *node_ptr; + location_t locus = EXPR_LOCATION (bsi_stmt (bsi)); + tree type = TREE_TYPE (node); + tree var, stmt; + + if (TREE_CODE (node) == VAR_DECL) + return; + + /* Split the current statement by creating a new local variable */ + var = create_tmp_var (type, "cilsimp"); + stmt = build2 (MODIFY_EXPR, type, var, node); + SET_EXPR_LOCATION (stmt, locus); + bsi_insert_before (&bsi, stmt, BSI_SAME_STMT); + *node_ptr = var; +} + +/* Return TRUE if EXP contains mostly (3/4) zeros. */ +static bool +mostly_zeros_p (tree exp) +{ + HOST_WIDE_INT nz_elts, count, elts; + bool must_clear; + + gcc_assert (TREE_CODE (exp) == CONSTRUCTOR); + + categorize_ctor_elements (exp, &nz_elts, &count, &must_clear); + + if (must_clear) + return TRUE; + + elts = count_type_elements (TREE_TYPE (exp), false); + + return (nz_elts < elts / 4); +} + +/* Return TRUE if EXP contains all zeros. */ +static bool +all_zeros_p (tree exp) +{ + HOST_WIDE_INT nz_elts, count; + bool must_clear; + + gcc_assert (TREE_CODE (exp) == CONSTRUCTOR); + + categorize_ctor_elements (exp, &nz_elts, &count, &must_clear); + + return (nz_elts == 0); +} + +/* Expand the initialization of tree DECL to tree INIT + into the statement list pointed by STMT_LIST. + Beware that statements inserted into the list cannot be assumed + to be in GIMPLE form and/or simplified for CIL. + If GIMPLE CIL-simplified statements are required, explicit + gimplification and CIL simplification have to be performed on them. + CLEARED tells whether unmentioned fields in the initializer + statement may be considered already initialized to zero or not. + The expansion is especially meant to expand a CONSTRUCTOR into + an equivalent statement sequence; anyway, any initialization + is properly handled: in case of no expansion, a simple MODIFY_EXPR + is appended to STMT_LIST. + STMT_LIST may be NULL; in this case a statement list is allocated. +*/ + +void +expand_init_to_stmt_list (tree decl, tree init, tree *stmt_list, bool cleared) +{ + tree decl_size = TYPE_SIZE_UNIT (TREE_TYPE (decl)); + unsigned int size = TREE_INT_CST_LOW (decl_size); + bool need_to_clear = FALSE; + + if (! *stmt_list) + { + *stmt_list = alloc_stmt_list (); + } + + gcc_assert (TREE_CODE (*stmt_list) == STATEMENT_LIST); + + if (TREE_CODE (init) == CONST_DECL) + { + init = DECL_INITIAL (init); + gcc_assert (init && init != error_mark_node); + } + + if (! cleared && TREE_CODE (init) == CONSTRUCTOR && all_zeros_p (init)) + { + tree args, t, decl_ptr; + + args = tree_cons (NULL, decl_size, NULL); + args = tree_cons (NULL, integer_zero_node, args); + decl_ptr = build_fold_addr_expr (decl); + args = tree_cons (NULL, decl_ptr, args); + t = implicit_built_in_decls[BUILT_IN_MEMSET]; + t = build_function_call_expr (t, args); + + append_to_statement_list (t, stmt_list); + + return; + } + + switch (TREE_CODE (init)) + { + case STRING_CST: + { + tree args, t, to_ptr, from_ptr; + + gcc_assert (TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE); + + args = tree_cons (NULL, decl_size, NULL); + + from_ptr = build_fold_addr_expr (init); + args = tree_cons (NULL, from_ptr, args); + + to_ptr = build_fold_addr_expr (decl); + args = tree_cons (NULL, to_ptr, args); + + /* We know they do not overlap */ + t = implicit_built_in_decls[BUILT_IN_MEMCPY]; + t = build_function_call_expr (t, args); + + append_to_statement_list (t, stmt_list); + } + break; + + case CONSTRUCTOR: + switch (TREE_CODE (TREE_TYPE (init))) + { + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + { + unsigned HOST_WIDE_INT idx; + tree init_type = TREE_TYPE (init); + tree field, value; + + /* If size is zero or the target is already cleared, do nothing */ + if (size == 0 || cleared) + { + need_to_clear = FALSE; + cleared = TRUE; + } + + /* We either clear the aggregate or indicate the value is dead. */ + else if ((TREE_CODE (init_type) == UNION_TYPE + || TREE_CODE (init_type) == QUAL_UNION_TYPE) + && ! CONSTRUCTOR_ELTS (init)) + /* If the constructor is empty, clear the union. */ + need_to_clear = TRUE; + + /* If the constructor has fewer fields than the structure or + if we are initializing the structure to mostly zeros, clear + the whole structure first. */ + else if (size > 0 + && (((int)VEC_length (constructor_elt, + CONSTRUCTOR_ELTS (init)) + != fields_length (init_type)) + || mostly_zeros_p (init))) + need_to_clear = TRUE; + + if (need_to_clear && size > 0) + { + tree args, t, decl_ptr; + + args = tree_cons (NULL, decl_size, NULL); + args = tree_cons (NULL, integer_zero_node, args); + decl_ptr = build_fold_addr_expr (decl); + args = tree_cons (NULL, decl_ptr, args); + t = implicit_built_in_decls[BUILT_IN_MEMSET]; + t = build_function_call_expr (t, args); + + append_to_statement_list (t, stmt_list); + + cleared = TRUE; + } + + /* Store each element of the constructor into the + corresponding field of TARGET. */ + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), idx, + field, value) + { + tree ltarget; + + /* Just ignore missing fields. We cleared the whole + structure, above, if any fields are missing. */ + if (field == 0) + continue; + + if (cleared && initializer_zerop (value)) + continue; + + ltarget = build3 (COMPONENT_REF, TREE_TYPE (field), decl, + field, NULL); + + expand_init_to_stmt_list (ltarget, value, stmt_list, cleared); + } + } + break; + + case ARRAY_TYPE: + { + tree value, index; + unsigned HOST_WIDE_INT i; + tree domain; + tree elttype = TREE_TYPE (TREE_TYPE (init)); + int const_bounds_p; + HOST_WIDE_INT minelt = 0; + HOST_WIDE_INT maxelt = 0; + + domain = TYPE_DOMAIN (TREE_TYPE (init)); + const_bounds_p = (TYPE_MIN_VALUE (domain) + && TYPE_MAX_VALUE (domain) + && host_integerp (TYPE_MIN_VALUE (domain), 0) + && host_integerp (TYPE_MAX_VALUE (domain), 0)); + + /* If we have constant bounds for the range + of the type, get them. */ + if (const_bounds_p) + { + minelt = tree_low_cst (TYPE_MIN_VALUE (domain), 0); + maxelt = tree_low_cst (TYPE_MAX_VALUE (domain), 0); + } + + /* If the constructor has fewer elements than the array, clear + the whole array first. */ + if (cleared) + need_to_clear = FALSE; + else + { + unsigned HOST_WIDE_INT idx; + tree index, value; + HOST_WIDE_INT count = 0; + HOST_WIDE_INT zero_count = 0; + need_to_clear = ! const_bounds_p; + + /* This loop is a more accurate version of the loop in + mostly_zeros_p (it handles RANGE_EXPR in an index). It + is also needed to check for missing elements. */ + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), idx, + index, value) + { + HOST_WIDE_INT this_node_count; + + if (need_to_clear) + break; + + if (index != NULL_TREE && TREE_CODE (index) == RANGE_EXPR) + { + tree lo_index = TREE_OPERAND (index, 0); + tree hi_index = TREE_OPERAND (index, 1); + + if (! host_integerp (lo_index, 1) + || ! host_integerp (hi_index, 1)) + { + need_to_clear = TRUE; + break; + } + + this_node_count = tree_low_cst (hi_index, 1) + - tree_low_cst (lo_index, 1) + 1; + } + else + this_node_count = 1; + + count += this_node_count; + if (TREE_CODE (value) == CONSTRUCTOR + && mostly_zeros_p (value)) + zero_count += this_node_count; + } + + /* Clear the entire array first if there are any missing + elements, or if the incidence of zero elements is >= + 75%. */ + if (! need_to_clear + && (count < maxelt - minelt + 1 + || 4 * zero_count >= 3 * count)) + need_to_clear = TRUE; + } + + if (need_to_clear && size > 0) + { + tree args, t, decl_ptr; + + args = tree_cons (NULL, decl_size, NULL); + args = tree_cons (NULL, integer_zero_node, args); + decl_ptr = build_fold_addr_expr (decl); + args = tree_cons (NULL, decl_ptr, args); + t = implicit_built_in_decls[BUILT_IN_MEMSET]; + t = build_function_call_expr (t, args); + + append_to_statement_list (t, stmt_list); + + cleared = TRUE; + } + + /* Store each element of the constructor into the + corresponding element of TARGET, determined by counting the + elements. */ + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), i, index, value) + { + enum machine_mode mode; + HOST_WIDE_INT bitsize; + int unsignedp; + tree t; + + if (initializer_zerop (value)) + continue; + + unsignedp = TYPE_UNSIGNED (elttype); + mode = TYPE_MODE (elttype); + + if (mode == BLKmode) + bitsize = (host_integerp (TYPE_SIZE (elttype), 1) + ? tree_low_cst (TYPE_SIZE (elttype), 1) + : -1); + else + bitsize = GET_MODE_BITSIZE (mode); + + gcc_assert (index == NULL_TREE + || TREE_CODE (index) != RANGE_EXPR); + + if (minelt) + index = fold_convert (ssizetype, + fold_build2 (MINUS_EXPR, + TREE_TYPE (index), + index, + TYPE_MIN_VALUE (domain))); + + t = build4 (ARRAY_REF, elttype, decl, index, NULL, NULL); + expand_init_to_stmt_list (t, value, stmt_list, cleared); + } + } + break; + + case VECTOR_TYPE: + fprintf (stderr, "CIL: Cannot handle rhs: "); + debug_generic_expr (init); + gcc_assert (0); + break; + + default: + gcc_unreachable (); + break; + } + break; + + default: + append_to_statement_list (build2 (MODIFY_EXPR, TREE_TYPE (decl), + decl, init), + stmt_list); + break; + } +} + +/* Rename a single variable using the specified suffix */ + +static void +rename_var (tree var, const char* suffix) +{ + const char *orig_name = IDENTIFIER_POINTER (DECL_NAME (var)); + char *newsym = alloca (strlen (orig_name) + strlen (suffix) + 10 + 1); + DECL_NAME (var) = get_identifier_with_length (newsym, + sprintf (newsym, + "%s%s%lu", + orig_name, + suffix, + (unsigned long)var)); +} + +/* Simplify variables: rename inlined variables + rename and globalize function static variables + inline init for local variables. */ + +static void +simp_vars (void) +{ + block_stmt_iterator bsi = bsi_start (ENTRY_BLOCK_PTR); + tree *p = &cfun->unexpanded_var_list; + + for (; *p; ) + { + tree var = TREE_VALUE (*p); + tree init = DECL_INITIAL (var); + + if (TREE_STATIC (var) && DECL_CONTEXT (var) != 0) + { + rename_var (var, "?fs"); + DECL_CONTEXT (var) = 0; + } + + if (DECL_FROM_INLINE (var) && DECL_NAME (var) != NULL) + rename_var (var, "?in"); + + if (!TREE_STATIC (var) && init && init != error_mark_node) + { + /* DECL_INITIAL (var) = NULL_TREE; */ + bsi_insert_after (&bsi, + build2 (INIT_EXPR, TREE_TYPE (var), var, init), + BSI_NEW_STMT); + } + + p = &TREE_CHAIN (*p); + } +} + +/* Simplify pass that makes CIL emission easier. */ + +static unsigned int +simp_cil (void) +{ + basic_block bb; + block_stmt_iterator bsi; + + res_var = NULL_TREE; + uint32_type = get_unsigned_integer_type (32); + + simp_vars (); + + /* Some pre-simplification is needed for INIT_EXPR and MODIFY_EXPR: + *) in their expansion, it's sometimes convenient to generate ARRAY_REF + nodes, which require further simplification. + *) if the lhs is a bitfield COMPONENT_REF, then the rhs must be + a VAR_DECL because the following simplification pass expects + that. */ + FOR_EACH_BB (bb) + { + for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi)) + { + tree stmt = bsi_stmt (bsi); + + if (TREE_CODE (stmt) == INIT_EXPR || TREE_CODE (stmt) == MODIFY_EXPR) + { + tree lhs = TREE_OPERAND (stmt, 0); + tree rhs = TREE_OPERAND (stmt, 1); + + if (TREE_CODE (rhs) == CONSTRUCTOR + || TREE_CODE (rhs) == STRING_CST) + pre_simp_init (&bsi, stmt); + else if (TREE_CODE (lhs) == COMPONENT_REF + && DECL_BIT_FIELD (TREE_OPERAND (lhs, 1))) + split_use (bsi, &TREE_OPERAND (stmt, 1)); + } + } + } + + /* Statement simplification loop. + At this point, the code is still in proper GIMPLE form, + but with no constructors nor string initializers. */ + FOR_EACH_BB (bb) + { + for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi)) + { + tree *stmt_ptr = bsi_stmt_ptr (bsi); + simp_cil_node (&bsi, stmt_ptr); + bb = bb_for_stmt (*stmt_ptr); + } + } + +#if 0 + FOR_EACH_BB (bb) + { + dump_generic_bb (stdout, bb, 4, 0); + } +#endif + + return 0; +} + +/* Gate function of CIL simplify pass. */ + +static bool +simp_cil_gate (void) +{ + return current_function_decl != NULL; +} + +/* Define the parameters of the tree-simp-CIL pass. */ + +struct tree_opt_pass pass_simp_cil = +{ + "simpcil", /* name */ + simp_cil_gate, /* gate */ + simp_cil, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_REST_OF_COMPILATION, /* tv_id */ + PROP_cfg, /* properties_required */ + 0, /* properties_provided */ + /* ??? If TER is enabled, we also kill gimple. */ + 0, /* properties_destroyed */ + 0, + 0, + 0 /* letter */ +}; + +/* + * Local variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/gcc/config/cil32/tree-simp-cil.h b/gcc/config/cil32/tree-simp-cil.h new file mode 100644 index 00000000000..51f0970a707 --- /dev/null +++ b/gcc/config/cil32/tree-simp-cil.h @@ -0,0 +1,39 @@ +/* CIL simplification definitions for GNU compiler. + + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GCC. + +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 2, 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 COPYING. If not, write to the Free +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. + +Authors: + Andrea Bona + Andrea Ornstein + Erven Rohou + Roberto Costa + +Contact information at STMicroelectronics: +Roberto Costa */ + +#ifndef TREE_SIMP_CIL_H +#define TREE_SIMP_CIL_H + +#include "tree.h" + +void +expand_init_to_stmt_list (tree, tree, tree *, bool); + +#endif /* TREE_SIMP_CIL_H */ diff --git a/gcc/opts.c b/gcc/opts.c index 01ec30d6ceb..c4cc2f436dd 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -1123,9 +1123,10 @@ set_debug_level (enum debug_info_type type, int extended, const char *arg) write_symbols = DBX_DEBUG; #endif } - +#ifndef DISABLE_RTL_PASSES if (write_symbols == NO_DEBUG) warning (0, "target system does not support debug output"); +#endif } } else diff --git a/gcc/passes.c b/gcc/passes.c index fe6f637e868..e037f8169d4 100644 --- a/gcc/passes.c +++ b/gcc/passes.c @@ -214,6 +214,7 @@ finish_optimization_passes (void) char *name; timevar_push (TV_DUMP); +#if !defined(DISABLE_RTL_PASSES) if (profile_arc_flag || flag_test_coverage || flag_branch_probabilities) { dump_file = dump_begin (pass_profile.static_pass_number, NULL); @@ -231,6 +232,7 @@ finish_optimization_passes (void) dump_end (pass_combine.static_pass_number, dump_file); } } +#endif /* Do whatever is necessary to finish printing the graphs. */ if (graph_dump_format != no_graph) @@ -478,11 +480,18 @@ init_optimization_passes (void) NEXT_PASS (pass_all_optimizations); NEXT_PASS (pass_warn_function_noreturn); NEXT_PASS (pass_mudflap_2); +#if defined(DISABLE_RTL_PASSES) + NEXT_PASS (pass_simp_cil); + NEXT_PASS (pass_gen_cil); /* <--- CIL */ + NEXT_PASS (pass_free_datastructures); + NEXT_PASS (pass_free_cfg_annotations); +#else NEXT_PASS (pass_free_datastructures); NEXT_PASS (pass_free_cfg_annotations); NEXT_PASS (pass_expand); NEXT_PASS (pass_rest_of_compilation); NEXT_PASS (pass_clean_state); +#endif *p = NULL; p = &pass_all_optimizations.sub; diff --git a/gcc/pointer-set.c b/gcc/pointer-set.c index 5daf88bd0bc..cd23d32ded8 100644 --- a/gcc/pointer-set.c +++ b/gcc/pointer-set.c @@ -171,3 +171,30 @@ pointer_set_insert (struct pointer_set_t *pset, void *p) return 0; } + +struct pointer_set_iter_t +pointer_set_begin (struct pointer_set_t *pset) +{ + struct pointer_set_iter_t iter = { 0, -1 }; + + return pointer_set_next (pset, iter); +} + +struct pointer_set_iter_t +pointer_set_next (struct pointer_set_t *pset, struct pointer_set_iter_t it) +{ + struct pointer_set_iter_t res = { 0, -1 }; + size_t pos = (size_t)(it.slot + 1); + + for (; pos < pset->n_slots; ++pos) + { + if (pset->slots[pos]) + { + res.elem = pset->slots[pos]; + res.slot = pos; + break; + } + } + + return res; +} diff --git a/gcc/pointer-set.h b/gcc/pointer-set.h index 7b2e6bc3d2c..9ae22c9f307 100644 --- a/gcc/pointer-set.h +++ b/gcc/pointer-set.h @@ -23,10 +23,22 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA struct pointer_set_t; +struct pointer_set_iter_t { + void* elem; + int slot; +}; + +#define POINTER_SET_ITER_ELEM(it) ((it).elem) +#define POINTER_SET_ITER_IS_END(it) ((it).slot < 0) + struct pointer_set_t *pointer_set_create (void); void pointer_set_destroy (struct pointer_set_t *pset); int pointer_set_contains (struct pointer_set_t *pset, void *p); int pointer_set_insert (struct pointer_set_t *pset, void *p); +struct pointer_set_iter_t pointer_set_begin (struct pointer_set_t *pset); +struct pointer_set_iter_t pointer_set_next (struct pointer_set_t *pset, + struct pointer_set_iter_t it); + #endif /* POINTER_SET_H */ diff --git a/gcc/varasm.c b/gcc/varasm.c index 9837e0de147..22e21b15ea8 100644 --- a/gcc/varasm.c +++ b/gcc/varasm.c @@ -1826,6 +1826,11 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED, && (sect->common.flags & SECTION_COMMON) == 0) globalize_decl (decl); +#ifdef TARGET_DECLARE_VARIABLE + TARGET_DECLARE_VARIABLE(asm_out_file,decl); + return; +#endif + /* Output any data that we will need to use the address of. */ if (DECL_INITIAL (decl) && DECL_INITIAL (decl) != error_mark_node) output_addressed_constants (DECL_INITIAL (decl)); -- cgit v1.2.3