| /* Subroutines shared by all languages that are variants of C. |
| Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, |
| 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 |
| 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 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "intl.h" |
| #include "tree.h" |
| #include "flags.h" |
| #include "output.h" |
| #include "c-pragma.h" |
| #include "rtl.h" |
| #include "ggc.h" |
| #include "varray.h" |
| #include "expr.h" |
| #include "c-common.h" |
| #include "diagnostic.h" |
| #include "tm_p.h" |
| #include "obstack.h" |
| #include "cpplib.h" |
| #include "target.h" |
| #include "langhooks.h" |
| #include "tree-inline.h" |
| #include "c-tree.h" |
| #include "toplev.h" |
| #include "tree-iterator.h" |
| #include "hashtab.h" |
| #include "tree-mudflap.h" |
| #include "opts.h" |
| #include "real.h" |
| #include "cgraph.h" |
| #include "target-def.h" |
| #include "gimple.h" |
| #include "fixed-value.h" |
| #include "libfuncs.h" |
| |
| cpp_reader *parse_in; /* Declared in c-pragma.h. */ |
| |
| /* We let tm.h override the types used here, to handle trivial differences |
| such as the choice of unsigned int or long unsigned int for size_t. |
| When machines start needing nontrivial differences in the size type, |
| it would be best to do something here to figure out automatically |
| from other information what type to use. */ |
| |
| #ifndef SIZE_TYPE |
| #define SIZE_TYPE "long unsigned int" |
| #endif |
| |
| #ifndef PID_TYPE |
| #define PID_TYPE "int" |
| #endif |
| |
| #ifndef CHAR16_TYPE |
| #define CHAR16_TYPE "short unsigned int" |
| #endif |
| |
| #ifndef CHAR32_TYPE |
| #define CHAR32_TYPE "unsigned int" |
| #endif |
| |
| #ifndef WCHAR_TYPE |
| #define WCHAR_TYPE "int" |
| #endif |
| |
| /* WCHAR_TYPE gets overridden by -fshort-wchar. */ |
| #define MODIFIED_WCHAR_TYPE \ |
| (flag_short_wchar ? "short unsigned int" : WCHAR_TYPE) |
| |
| #ifndef PTRDIFF_TYPE |
| #define PTRDIFF_TYPE "long int" |
| #endif |
| |
| #ifndef WINT_TYPE |
| #define WINT_TYPE "unsigned int" |
| #endif |
| |
| #ifndef INTMAX_TYPE |
| #define INTMAX_TYPE ((INT_TYPE_SIZE == LONG_LONG_TYPE_SIZE) \ |
| ? "int" \ |
| : ((LONG_TYPE_SIZE == LONG_LONG_TYPE_SIZE) \ |
| ? "long int" \ |
| : "long long int")) |
| #endif |
| |
| #ifndef UINTMAX_TYPE |
| #define UINTMAX_TYPE ((INT_TYPE_SIZE == LONG_LONG_TYPE_SIZE) \ |
| ? "unsigned int" \ |
| : ((LONG_TYPE_SIZE == LONG_LONG_TYPE_SIZE) \ |
| ? "long unsigned int" \ |
| : "long long unsigned int")) |
| #endif |
| |
| /* The following symbols are subsumed in the c_global_trees array, and |
| listed here individually for documentation purposes. |
| |
| INTEGER_TYPE and REAL_TYPE nodes for the standard data types. |
| |
| tree short_integer_type_node; |
| tree long_integer_type_node; |
| tree long_long_integer_type_node; |
| |
| tree short_unsigned_type_node; |
| tree long_unsigned_type_node; |
| tree long_long_unsigned_type_node; |
| |
| tree truthvalue_type_node; |
| tree truthvalue_false_node; |
| tree truthvalue_true_node; |
| |
| tree ptrdiff_type_node; |
| |
| tree unsigned_char_type_node; |
| tree signed_char_type_node; |
| tree wchar_type_node; |
| tree signed_wchar_type_node; |
| tree unsigned_wchar_type_node; |
| |
| tree char16_type_node; |
| tree char32_type_node; |
| |
| tree float_type_node; |
| tree double_type_node; |
| tree long_double_type_node; |
| |
| tree complex_integer_type_node; |
| tree complex_float_type_node; |
| tree complex_double_type_node; |
| tree complex_long_double_type_node; |
| |
| tree dfloat32_type_node; |
| tree dfloat64_type_node; |
| tree_dfloat128_type_node; |
| |
| tree intQI_type_node; |
| tree intHI_type_node; |
| tree intSI_type_node; |
| tree intDI_type_node; |
| tree intTI_type_node; |
| |
| tree unsigned_intQI_type_node; |
| tree unsigned_intHI_type_node; |
| tree unsigned_intSI_type_node; |
| tree unsigned_intDI_type_node; |
| tree unsigned_intTI_type_node; |
| |
| tree widest_integer_literal_type_node; |
| tree widest_unsigned_literal_type_node; |
| |
| Nodes for types `void *' and `const void *'. |
| |
| tree ptr_type_node, const_ptr_type_node; |
| |
| Nodes for types `char *' and `const char *'. |
| |
| tree string_type_node, const_string_type_node; |
| |
| Type `char[SOMENUMBER]'. |
| Used when an array of char is needed and the size is irrelevant. |
| |
| tree char_array_type_node; |
| |
| Type `int[SOMENUMBER]' or something like it. |
| Used when an array of int needed and the size is irrelevant. |
| |
| tree int_array_type_node; |
| |
| Type `wchar_t[SOMENUMBER]' or something like it. |
| Used when a wide string literal is created. |
| |
| tree wchar_array_type_node; |
| |
| Type `char16_t[SOMENUMBER]' or something like it. |
| Used when a UTF-16 string literal is created. |
| |
| tree char16_array_type_node; |
| |
| Type `char32_t[SOMENUMBER]' or something like it. |
| Used when a UTF-32 string literal is created. |
| |
| tree char32_array_type_node; |
| |
| Type `int ()' -- used for implicit declaration of functions. |
| |
| tree default_function_type; |
| |
| A VOID_TYPE node, packaged in a TREE_LIST. |
| |
| tree void_list_node; |
| |
| The lazily created VAR_DECLs for __FUNCTION__, __PRETTY_FUNCTION__, |
| and __func__. (C doesn't generate __FUNCTION__ and__PRETTY_FUNCTION__ |
| VAR_DECLS, but C++ does.) |
| |
| tree function_name_decl_node; |
| tree pretty_function_name_decl_node; |
| tree c99_function_name_decl_node; |
| |
| Stack of nested function name VAR_DECLs. |
| |
| tree saved_function_name_decls; |
| |
| */ |
| |
| tree c_global_trees[CTI_MAX]; |
| |
| /* Switches common to the C front ends. */ |
| |
| /* Nonzero if preprocessing only. */ |
| |
| int flag_preprocess_only; |
| |
| /* Nonzero means don't output line number information. */ |
| |
| char flag_no_line_commands; |
| |
| /* Nonzero causes -E output not to be done, but directives such as |
| #define that have side effects are still obeyed. */ |
| |
| char flag_no_output; |
| |
| /* Nonzero means dump macros in some fashion. */ |
| |
| char flag_dump_macros; |
| |
| /* Nonzero means pass #include lines through to the output. */ |
| |
| char flag_dump_includes; |
| |
| /* Nonzero means process PCH files while preprocessing. */ |
| |
| bool flag_pch_preprocess; |
| |
| /* The file name to which we should write a precompiled header, or |
| NULL if no header will be written in this compile. */ |
| |
| const char *pch_file; |
| |
| /* Nonzero if an ISO standard was selected. It rejects macros in the |
| user's namespace. */ |
| int flag_iso; |
| |
| /* Nonzero if -undef was given. It suppresses target built-in macros |
| and assertions. */ |
| int flag_undef; |
| |
| /* Nonzero means don't recognize the non-ANSI builtin functions. */ |
| |
| int flag_no_builtin; |
| |
| /* Nonzero means don't recognize the non-ANSI builtin functions. |
| -ansi sets this. */ |
| |
| int flag_no_nonansi_builtin; |
| |
| /* Nonzero means give `double' the same size as `float'. */ |
| |
| int flag_short_double; |
| |
| /* Nonzero means give `wchar_t' the same size as `short'. */ |
| |
| int flag_short_wchar; |
| |
| /* Nonzero means allow implicit conversions between vectors with |
| differing numbers of subparts and/or differing element types. */ |
| int flag_lax_vector_conversions; |
| |
| /* Nonzero means allow Microsoft extensions without warnings or errors. */ |
| int flag_ms_extensions; |
| |
| /* Nonzero means don't recognize the keyword `asm'. */ |
| |
| int flag_no_asm; |
| |
| /* Nonzero means to treat bitfields as signed unless they say `unsigned'. */ |
| |
| int flag_signed_bitfields = 1; |
| |
| /* Warn about #pragma directives that are not recognized. */ |
| |
| int warn_unknown_pragmas; /* Tri state variable. */ |
| |
| /* Warn about format/argument anomalies in calls to formatted I/O functions |
| (*printf, *scanf, strftime, strfmon, etc.). */ |
| |
| int warn_format; |
| |
| /* Warn about using __null (as NULL in C++) as sentinel. For code compiled |
| with GCC this doesn't matter as __null is guaranteed to have the right |
| size. */ |
| |
| int warn_strict_null_sentinel; |
| |
| /* Zero means that faster, ...NonNil variants of objc_msgSend... |
| calls will be used in ObjC; passing nil receivers to such calls |
| will most likely result in crashes. */ |
| int flag_nil_receivers = 1; |
| |
| /* Nonzero means that code generation will be altered to support |
| "zero-link" execution. This currently affects ObjC only, but may |
| affect other languages in the future. */ |
| int flag_zero_link = 0; |
| |
| /* Nonzero means emit an '__OBJC, __image_info' for the current translation |
| unit. It will inform the ObjC runtime that class definition(s) herein |
| contained are to replace one(s) previously loaded. */ |
| int flag_replace_objc_classes = 0; |
| |
| /* C/ObjC language option variables. */ |
| |
| |
| /* Nonzero means allow type mismatches in conditional expressions; |
| just make their values `void'. */ |
| |
| int flag_cond_mismatch; |
| |
| /* Nonzero means enable C89 Amendment 1 features. */ |
| |
| int flag_isoc94; |
| |
| /* Nonzero means use the ISO C99 dialect of C. */ |
| |
| int flag_isoc99; |
| |
| /* Nonzero means that we have builtin functions, and main is an int. */ |
| |
| int flag_hosted = 1; |
| |
| |
| /* ObjC language option variables. */ |
| |
| |
| /* Open and close the file for outputting class declarations, if |
| requested (ObjC). */ |
| |
| int flag_gen_declaration; |
| |
| /* Tells the compiler that this is a special run. Do not perform any |
| compiling, instead we are to test some platform dependent features |
| and output a C header file with appropriate definitions. */ |
| |
| int print_struct_values; |
| |
| /* Tells the compiler what is the constant string class for ObjC. */ |
| |
| const char *constant_string_class_name; |
| |
| |
| /* C++ language option variables. */ |
| |
| |
| /* Nonzero means don't recognize any extension keywords. */ |
| |
| int flag_no_gnu_keywords; |
| |
| /* Nonzero means do emit exported implementations of functions even if |
| they can be inlined. */ |
| |
| int flag_implement_inlines = 1; |
| |
| /* Nonzero means that implicit instantiations will be emitted if needed. */ |
| |
| int flag_implicit_templates = 1; |
| |
| /* Nonzero means that implicit instantiations of inline templates will be |
| emitted if needed, even if instantiations of non-inline templates |
| aren't. */ |
| |
| int flag_implicit_inline_templates = 1; |
| |
| /* Nonzero means generate separate instantiation control files and |
| juggle them at link time. */ |
| |
| int flag_use_repository; |
| |
| /* Nonzero if we want to issue diagnostics that the standard says are not |
| required. */ |
| |
| int flag_optional_diags = 1; |
| |
| /* Nonzero means we should attempt to elide constructors when possible. */ |
| |
| int flag_elide_constructors = 1; |
| |
| /* Nonzero means that member functions defined in class scope are |
| inline by default. */ |
| |
| int flag_default_inline = 1; |
| |
| /* Controls whether compiler generates 'type descriptor' that give |
| run-time type information. */ |
| |
| int flag_rtti = 1; |
| |
| /* Nonzero if we want to conserve space in the .o files. We do this |
| by putting uninitialized data and runtime initialized data into |
| .common instead of .data at the expense of not flagging multiple |
| definitions. */ |
| |
| int flag_conserve_space; |
| |
| /* Nonzero if we want to obey access control semantics. */ |
| |
| int flag_access_control = 1; |
| |
| /* Nonzero if we want to check the return value of new and avoid calling |
| constructors if it is a null pointer. */ |
| |
| int flag_check_new; |
| |
| /* The C++ dialect being used. C++98 is the default. */ |
| |
| enum cxx_dialect cxx_dialect = cxx98; |
| |
| /* Nonzero if we want the new ISO rules for pushing a new scope for `for' |
| initialization variables. |
| 0: Old rules, set by -fno-for-scope. |
| 2: New ISO rules, set by -ffor-scope. |
| 1: Try to implement new ISO rules, but with backup compatibility |
| (and warnings). This is the default, for now. */ |
| |
| int flag_new_for_scope = 1; |
| |
| /* Nonzero if we want to emit defined symbols with common-like linkage as |
| weak symbols where possible, in order to conform to C++ semantics. |
| Otherwise, emit them as local symbols. */ |
| |
| int flag_weak = 1; |
| |
| /* 0 means we want the preprocessor to not emit line directives for |
| the current working directory. 1 means we want it to do it. -1 |
| means we should decide depending on whether debugging information |
| is being emitted or not. */ |
| |
| int flag_working_directory = -1; |
| |
| /* Nonzero to use __cxa_atexit, rather than atexit, to register |
| destructors for local statics and global objects. '2' means it has been |
| set nonzero as a default, not by a command-line flag. */ |
| |
| int flag_use_cxa_atexit = DEFAULT_USE_CXA_ATEXIT; |
| |
| /* Nonzero to use __cxa_get_exception_ptr in C++ exception-handling |
| code. '2' means it has not been set explicitly on the command line. */ |
| |
| int flag_use_cxa_get_exception_ptr = 2; |
| |
| /* Nonzero means to implement standard semantics for exception |
| specifications, calling unexpected if an exception is thrown that |
| doesn't match the specification. Zero means to treat them as |
| assertions and optimize accordingly, but not check them. */ |
| |
| int flag_enforce_eh_specs = 1; |
| |
| /* Nonzero means to generate thread-safe code for initializing local |
| statics. */ |
| |
| int flag_threadsafe_statics = 1; |
| |
| /* Nonzero means warn about implicit declarations. */ |
| |
| int warn_implicit = 1; |
| |
| /* Maximum template instantiation depth. This limit is rather |
| arbitrary, but it exists to limit the time it takes to notice |
| infinite template instantiations. */ |
| |
| int max_tinst_depth = 500; |
| |
| |
| |
| /* The elements of `ridpointers' are identifier nodes for the reserved |
| type names and storage classes. It is indexed by a RID_... value. */ |
| tree *ridpointers; |
| |
| tree (*make_fname_decl) (tree, int); |
| |
| /* Nonzero means the expression being parsed will never be evaluated. |
| This is a count, since unevaluated expressions can nest. */ |
| int skip_evaluation; |
| |
| /* Information about how a function name is generated. */ |
| struct fname_var_t |
| { |
| tree *const decl; /* pointer to the VAR_DECL. */ |
| const unsigned rid; /* RID number for the identifier. */ |
| const int pretty; /* How pretty is it? */ |
| }; |
| |
| /* The three ways of getting then name of the current function. */ |
| |
| const struct fname_var_t fname_vars[] = |
| { |
| /* C99 compliant __func__, must be first. */ |
| {&c99_function_name_decl_node, RID_C99_FUNCTION_NAME, 0}, |
| /* GCC __FUNCTION__ compliant. */ |
| {&function_name_decl_node, RID_FUNCTION_NAME, 0}, |
| /* GCC __PRETTY_FUNCTION__ compliant. */ |
| {&pretty_function_name_decl_node, RID_PRETTY_FUNCTION_NAME, 1}, |
| {NULL, 0, 0}, |
| }; |
| |
| static tree check_case_value (tree); |
| static bool check_case_bounds (tree, tree, tree *, tree *); |
| |
| static tree handle_packed_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_nocommon_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_common_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_noreturn_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_hot_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_cold_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_noinline_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_always_inline_attribute (tree *, tree, tree, int, |
| bool *); |
| static tree handle_gnu_inline_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_artificial_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_flatten_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_error_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_used_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_unused_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_externally_visible_attribute (tree *, tree, tree, int, |
| bool *); |
| static tree handle_const_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_transparent_union_attribute (tree *, tree, tree, |
| int, bool *); |
| static tree handle_constructor_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_destructor_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_mode_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_section_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_aligned_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ; |
| static tree handle_alias_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_weakref_attribute (tree *, tree, tree, int, bool *) ; |
| static tree handle_visibility_attribute (tree *, tree, tree, int, |
| bool *); |
| static tree handle_tls_model_attribute (tree *, tree, tree, int, |
| bool *); |
| static tree handle_no_instrument_function_attribute (tree *, tree, |
| tree, int, bool *); |
| static tree handle_malloc_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_no_limit_stack_attribute (tree *, tree, tree, int, |
| bool *); |
| static tree handle_pure_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_novops_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_deprecated_attribute (tree *, tree, tree, int, |
| bool *); |
| static tree handle_vector_size_attribute (tree *, tree, tree, int, |
| bool *); |
| static tree handle_nonnull_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_nothrow_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_cleanup_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_warn_unused_result_attribute (tree *, tree, tree, int, |
| bool *); |
| static tree handle_sentinel_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_alloc_size_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_target_attribute (tree *, tree, tree, int, bool *); |
| static tree handle_optimize_attribute (tree *, tree, tree, int, bool *); |
| |
| static void check_function_nonnull (tree, int, tree *); |
| static void check_nonnull_arg (void *, tree, unsigned HOST_WIDE_INT); |
| static bool nonnull_check_p (tree, unsigned HOST_WIDE_INT); |
| static bool get_nonnull_operand (tree, unsigned HOST_WIDE_INT *); |
| static int resort_field_decl_cmp (const void *, const void *); |
| |
| /* Reserved words. The third field is a mask: keywords are disabled |
| if they match the mask. |
| |
| Masks for languages: |
| C --std=c89: D_C99 | D_CXXONLY | D_OBJC | D_CXX_OBJC |
| C --std=c99: D_CXXONLY | D_OBJC |
| ObjC is like C except that D_OBJC and D_CXX_OBJC are not set |
| C++ --std=c98: D_CONLY | D_CXXOX | D_OBJC |
| C++ --std=c0x: D_CONLY | D_OBJC |
| ObjC++ is like C++ except that D_OBJC is not set |
| |
| If -fno-asm is used, D_ASM is added to the mask. If |
| -fno-gnu-keywords is used, D_EXT is added. If -fno-asm and C in |
| C89 mode, D_EXT89 is added for both -fno-asm and -fno-gnu-keywords. |
| In C with -Wc++-compat, we warn if D_CXXWARN is set. */ |
| |
| const struct c_common_resword c_common_reswords[] = |
| { |
| { "_Bool", RID_BOOL, D_CONLY }, |
| { "_Complex", RID_COMPLEX, 0 }, |
| { "_Decimal32", RID_DFLOAT32, D_CONLY | D_EXT }, |
| { "_Decimal64", RID_DFLOAT64, D_CONLY | D_EXT }, |
| { "_Decimal128", RID_DFLOAT128, D_CONLY | D_EXT }, |
| { "_Fract", RID_FRACT, D_CONLY | D_EXT }, |
| { "_Accum", RID_ACCUM, D_CONLY | D_EXT }, |
| { "_Sat", RID_SAT, D_CONLY | D_EXT }, |
| { "__FUNCTION__", RID_FUNCTION_NAME, 0 }, |
| { "__PRETTY_FUNCTION__", RID_PRETTY_FUNCTION_NAME, 0 }, |
| { "__alignof", RID_ALIGNOF, 0 }, |
| { "__alignof__", RID_ALIGNOF, 0 }, |
| { "__asm", RID_ASM, 0 }, |
| { "__asm__", RID_ASM, 0 }, |
| { "__attribute", RID_ATTRIBUTE, 0 }, |
| { "__attribute__", RID_ATTRIBUTE, 0 }, |
| { "__builtin_choose_expr", RID_CHOOSE_EXPR, D_CONLY }, |
| { "__builtin_offsetof", RID_OFFSETOF, 0 }, |
| { "__builtin_types_compatible_p", RID_TYPES_COMPATIBLE_P, D_CONLY }, |
| { "__builtin_va_arg", RID_VA_ARG, 0 }, |
| { "__complex", RID_COMPLEX, 0 }, |
| { "__complex__", RID_COMPLEX, 0 }, |
| { "__const", RID_CONST, 0 }, |
| { "__const__", RID_CONST, 0 }, |
| { "__decltype", RID_DECLTYPE, D_CXXONLY }, |
| { "__extension__", RID_EXTENSION, 0 }, |
| { "__func__", RID_C99_FUNCTION_NAME, 0 }, |
| { "__has_nothrow_assign", RID_HAS_NOTHROW_ASSIGN, D_CXXONLY }, |
| { "__has_nothrow_constructor", RID_HAS_NOTHROW_CONSTRUCTOR, D_CXXONLY }, |
| { "__has_nothrow_copy", RID_HAS_NOTHROW_COPY, D_CXXONLY }, |
| { "__has_trivial_assign", RID_HAS_TRIVIAL_ASSIGN, D_CXXONLY }, |
| { "__has_trivial_constructor", RID_HAS_TRIVIAL_CONSTRUCTOR, D_CXXONLY }, |
| { "__has_trivial_copy", RID_HAS_TRIVIAL_COPY, D_CXXONLY }, |
| { "__has_trivial_destructor", RID_HAS_TRIVIAL_DESTRUCTOR, D_CXXONLY }, |
| { "__has_virtual_destructor", RID_HAS_VIRTUAL_DESTRUCTOR, D_CXXONLY }, |
| { "__is_abstract", RID_IS_ABSTRACT, D_CXXONLY }, |
| { "__is_base_of", RID_IS_BASE_OF, D_CXXONLY }, |
| { "__is_class", RID_IS_CLASS, D_CXXONLY }, |
| { "__is_convertible_to", RID_IS_CONVERTIBLE_TO, D_CXXONLY }, |
| { "__is_empty", RID_IS_EMPTY, D_CXXONLY }, |
| { "__is_enum", RID_IS_ENUM, D_CXXONLY }, |
| { "__is_pod", RID_IS_POD, D_CXXONLY }, |
| { "__is_polymorphic", RID_IS_POLYMORPHIC, D_CXXONLY }, |
| { "__is_union", RID_IS_UNION, D_CXXONLY }, |
| { "__imag", RID_IMAGPART, 0 }, |
| { "__imag__", RID_IMAGPART, 0 }, |
| { "__inline", RID_INLINE, 0 }, |
| { "__inline__", RID_INLINE, 0 }, |
| { "__label__", RID_LABEL, 0 }, |
| { "__null", RID_NULL, 0 }, |
| { "__real", RID_REALPART, 0 }, |
| { "__real__", RID_REALPART, 0 }, |
| { "__restrict", RID_RESTRICT, 0 }, |
| { "__restrict__", RID_RESTRICT, 0 }, |
| { "__signed", RID_SIGNED, 0 }, |
| { "__signed__", RID_SIGNED, 0 }, |
| { "__thread", RID_THREAD, 0 }, |
| { "__typeof", RID_TYPEOF, 0 }, |
| { "__typeof__", RID_TYPEOF, 0 }, |
| { "__volatile", RID_VOLATILE, 0 }, |
| { "__volatile__", RID_VOLATILE, 0 }, |
| { "asm", RID_ASM, D_ASM }, |
| { "auto", RID_AUTO, 0 }, |
| { "bool", RID_BOOL, D_CXXONLY | D_CXXWARN }, |
| { "break", RID_BREAK, 0 }, |
| { "case", RID_CASE, 0 }, |
| { "catch", RID_CATCH, D_CXX_OBJC | D_CXXWARN }, |
| { "char", RID_CHAR, 0 }, |
| { "char16_t", RID_CHAR16, D_CXXONLY | D_CXX0X | D_CXXWARN }, |
| { "char32_t", RID_CHAR32, D_CXXONLY | D_CXX0X | D_CXXWARN }, |
| { "class", RID_CLASS, D_CXX_OBJC | D_CXXWARN }, |
| { "const", RID_CONST, 0 }, |
| { "const_cast", RID_CONSTCAST, D_CXXONLY | D_CXXWARN }, |
| { "continue", RID_CONTINUE, 0 }, |
| { "decltype", RID_DECLTYPE, D_CXXONLY | D_CXX0X | D_CXXWARN }, |
| { "default", RID_DEFAULT, 0 }, |
| { "delete", RID_DELETE, D_CXXONLY | D_CXXWARN }, |
| { "do", RID_DO, 0 }, |
| { "double", RID_DOUBLE, 0 }, |
| { "dynamic_cast", RID_DYNCAST, D_CXXONLY | D_CXXWARN }, |
| { "else", RID_ELSE, 0 }, |
| { "enum", RID_ENUM, 0 }, |
| { "explicit", RID_EXPLICIT, D_CXXONLY | D_CXXWARN }, |
| { "export", RID_EXPORT, D_CXXONLY | D_CXXWARN }, |
| { "extern", RID_EXTERN, 0 }, |
| { "false", RID_FALSE, D_CXXONLY | D_CXXWARN }, |
| { "float", RID_FLOAT, 0 }, |
| { "for", RID_FOR, 0 }, |
| { "friend", RID_FRIEND, D_CXXONLY | D_CXXWARN }, |
| { "goto", RID_GOTO, 0 }, |
| { "if", RID_IF, 0 }, |
| { "inline", RID_INLINE, D_EXT89 }, |
| { "int", RID_INT, 0 }, |
| { "long", RID_LONG, 0 }, |
| { "mutable", RID_MUTABLE, D_CXXONLY | D_CXXWARN }, |
| { "namespace", RID_NAMESPACE, D_CXXONLY | D_CXXWARN }, |
| { "new", RID_NEW, D_CXXONLY | D_CXXWARN }, |
| { "operator", RID_OPERATOR, D_CXXONLY | D_CXXWARN }, |
| { "private", RID_PRIVATE, D_CXX_OBJC | D_CXXWARN }, |
| { "protected", RID_PROTECTED, D_CXX_OBJC | D_CXXWARN }, |
| { "public", RID_PUBLIC, D_CXX_OBJC | D_CXXWARN }, |
| { "register", RID_REGISTER, 0 }, |
| { "reinterpret_cast", RID_REINTCAST, D_CXXONLY | D_CXXWARN }, |
| { "restrict", RID_RESTRICT, D_CONLY | D_C99 }, |
| { "return", RID_RETURN, 0 }, |
| { "short", RID_SHORT, 0 }, |
| { "signed", RID_SIGNED, 0 }, |
| { "sizeof", RID_SIZEOF, 0 }, |
| { "static", RID_STATIC, 0 }, |
| { "static_assert", RID_STATIC_ASSERT, D_CXXONLY | D_CXX0X | D_CXXWARN }, |
| { "static_cast", RID_STATCAST, D_CXXONLY | D_CXXWARN }, |
| { "struct", RID_STRUCT, 0 }, |
| { "switch", RID_SWITCH, 0 }, |
| { "template", RID_TEMPLATE, D_CXXONLY | D_CXXWARN }, |
| { "this", RID_THIS, D_CXXONLY | D_CXXWARN }, |
| { "throw", RID_THROW, D_CXX_OBJC | D_CXXWARN }, |
| { "true", RID_TRUE, D_CXXONLY | D_CXXWARN }, |
| { "try", RID_TRY, D_CXX_OBJC | D_CXXWARN }, |
| { "typedef", RID_TYPEDEF, 0 }, |
| { "typename", RID_TYPENAME, D_CXXONLY | D_CXXWARN }, |
| { "typeid", RID_TYPEID, D_CXXONLY | D_CXXWARN }, |
| { "typeof", RID_TYPEOF, D_ASM | D_EXT }, |
| { "union", RID_UNION, 0 }, |
| { "unsigned", RID_UNSIGNED, 0 }, |
| { "using", RID_USING, D_CXXONLY | D_CXXWARN }, |
| { "virtual", RID_VIRTUAL, D_CXXONLY | D_CXXWARN }, |
| { "void", RID_VOID, 0 }, |
| { "volatile", RID_VOLATILE, 0 }, |
| { "wchar_t", RID_WCHAR, D_CXXONLY }, |
| { "while", RID_WHILE, 0 }, |
| /* These Objective-C keywords are recognized only immediately after |
| an '@'. */ |
| { "compatibility_alias", RID_AT_ALIAS, D_OBJC }, |
| { "defs", RID_AT_DEFS, D_OBJC }, |
| { "encode", RID_AT_ENCODE, D_OBJC }, |
| { "end", RID_AT_END, D_OBJC }, |
| { "implementation", RID_AT_IMPLEMENTATION, D_OBJC }, |
| { "interface", RID_AT_INTERFACE, D_OBJC }, |
| { "protocol", RID_AT_PROTOCOL, D_OBJC }, |
| { "selector", RID_AT_SELECTOR, D_OBJC }, |
| { "finally", RID_AT_FINALLY, D_OBJC }, |
| { "synchronized", RID_AT_SYNCHRONIZED, D_OBJC }, |
| /* These are recognized only in protocol-qualifier context |
| (see above) */ |
| { "bycopy", RID_BYCOPY, D_OBJC }, |
| { "byref", RID_BYREF, D_OBJC }, |
| { "in", RID_IN, D_OBJC }, |
| { "inout", RID_INOUT, D_OBJC }, |
| { "oneway", RID_ONEWAY, D_OBJC }, |
| { "out", RID_OUT, D_OBJC }, |
| }; |
| |
| const unsigned int num_c_common_reswords = |
| sizeof c_common_reswords / sizeof (struct c_common_resword); |
| |
| /* Table of machine-independent attributes common to all C-like languages. */ |
| const struct attribute_spec c_common_attribute_table[] = |
| { |
| /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ |
| { "packed", 0, 0, false, false, false, |
| handle_packed_attribute }, |
| { "nocommon", 0, 0, true, false, false, |
| handle_nocommon_attribute }, |
| { "common", 0, 0, true, false, false, |
| handle_common_attribute }, |
| /* FIXME: logically, noreturn attributes should be listed as |
| "false, true, true" and apply to function types. But implementing this |
| would require all the places in the compiler that use TREE_THIS_VOLATILE |
| on a decl to identify non-returning functions to be located and fixed |
| to check the function type instead. */ |
| { "noreturn", 0, 0, true, false, false, |
| handle_noreturn_attribute }, |
| { "volatile", 0, 0, true, false, false, |
| handle_noreturn_attribute }, |
| { "noinline", 0, 0, true, false, false, |
| handle_noinline_attribute }, |
| { "always_inline", 0, 0, true, false, false, |
| handle_always_inline_attribute }, |
| { "gnu_inline", 0, 0, true, false, false, |
| handle_gnu_inline_attribute }, |
| { "artificial", 0, 0, true, false, false, |
| handle_artificial_attribute }, |
| { "flatten", 0, 0, true, false, false, |
| handle_flatten_attribute }, |
| { "used", 0, 0, true, false, false, |
| handle_used_attribute }, |
| { "unused", 0, 0, false, false, false, |
| handle_unused_attribute }, |
| { "externally_visible", 0, 0, true, false, false, |
| handle_externally_visible_attribute }, |
| /* The same comments as for noreturn attributes apply to const ones. */ |
| { "const", 0, 0, true, false, false, |
| handle_const_attribute }, |
| { "transparent_union", 0, 0, false, false, false, |
| handle_transparent_union_attribute }, |
| { "constructor", 0, 1, true, false, false, |
| handle_constructor_attribute }, |
| { "destructor", 0, 1, true, false, false, |
| handle_destructor_attribute }, |
| { "mode", 1, 1, false, true, false, |
| handle_mode_attribute }, |
| { "section", 1, 1, true, false, false, |
| handle_section_attribute }, |
| { "aligned", 0, 1, false, false, false, |
| handle_aligned_attribute }, |
| { "weak", 0, 0, true, false, false, |
| handle_weak_attribute }, |
| { "alias", 1, 1, true, false, false, |
| handle_alias_attribute }, |
| { "weakref", 0, 1, true, false, false, |
| handle_weakref_attribute }, |
| { "no_instrument_function", 0, 0, true, false, false, |
| handle_no_instrument_function_attribute }, |
| { "malloc", 0, 0, true, false, false, |
| handle_malloc_attribute }, |
| { "returns_twice", 0, 0, true, false, false, |
| handle_returns_twice_attribute }, |
| { "no_stack_limit", 0, 0, true, false, false, |
| handle_no_limit_stack_attribute }, |
| { "pure", 0, 0, true, false, false, |
| handle_pure_attribute }, |
| /* For internal use (marking of builtins) only. The name contains space |
| to prevent its usage in source code. */ |
| { "no vops", 0, 0, true, false, false, |
| handle_novops_attribute }, |
| { "deprecated", 0, 0, false, false, false, |
| handle_deprecated_attribute }, |
| { "vector_size", 1, 1, false, true, false, |
| handle_vector_size_attribute }, |
| { "visibility", 1, 1, false, false, false, |
| handle_visibility_attribute }, |
| { "tls_model", 1, 1, true, false, false, |
| handle_tls_model_attribute }, |
| { "nonnull", 0, -1, false, true, true, |
| handle_nonnull_attribute }, |
| { "nothrow", 0, 0, true, false, false, |
| handle_nothrow_attribute }, |
| { "may_alias", 0, 0, false, true, false, NULL }, |
| { "cleanup", 1, 1, true, false, false, |
| handle_cleanup_attribute }, |
| { "warn_unused_result", 0, 0, false, true, true, |
| handle_warn_unused_result_attribute }, |
| { "sentinel", 0, 1, false, true, true, |
| handle_sentinel_attribute }, |
| /* For internal use (marking of builtins) only. The name contains space |
| to prevent its usage in source code. */ |
| { "type generic", 0, 0, false, true, true, |
| handle_type_generic_attribute }, |
| { "alloc_size", 1, 2, false, true, true, |
| handle_alloc_size_attribute }, |
| { "cold", 0, 0, true, false, false, |
| handle_cold_attribute }, |
| { "hot", 0, 0, true, false, false, |
| handle_hot_attribute }, |
| { "warning", 1, 1, true, false, false, |
| handle_error_attribute }, |
| { "error", 1, 1, true, false, false, |
| handle_error_attribute }, |
| { "target", 1, -1, true, false, false, |
| handle_target_attribute }, |
| { "optimize", 1, -1, true, false, false, |
| handle_optimize_attribute }, |
| { NULL, 0, 0, false, false, false, NULL } |
| }; |
| |
| /* Give the specifications for the format attributes, used by C and all |
| descendants. */ |
| |
| const struct attribute_spec c_common_format_attribute_table[] = |
| { |
| /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ |
| { "format", 3, 3, false, true, true, |
| handle_format_attribute }, |
| { "format_arg", 1, 1, false, true, true, |
| handle_format_arg_attribute }, |
| { NULL, 0, 0, false, false, false, NULL } |
| }; |
| |
| /* Push current bindings for the function name VAR_DECLS. */ |
| |
| void |
| start_fname_decls (void) |
| { |
| unsigned ix; |
| tree saved = NULL_TREE; |
| |
| for (ix = 0; fname_vars[ix].decl; ix++) |
| { |
| tree decl = *fname_vars[ix].decl; |
| |
| if (decl) |
| { |
| saved = tree_cons (decl, build_int_cst (NULL_TREE, ix), saved); |
| *fname_vars[ix].decl = NULL_TREE; |
| } |
| } |
| if (saved || saved_function_name_decls) |
| /* Normally they'll have been NULL, so only push if we've got a |
| stack, or they are non-NULL. */ |
| saved_function_name_decls = tree_cons (saved, NULL_TREE, |
| saved_function_name_decls); |
| } |
| |
| /* Finish up the current bindings, adding them into the current function's |
| statement tree. This must be done _before_ finish_stmt_tree is called. |
| If there is no current function, we must be at file scope and no statements |
| are involved. Pop the previous bindings. */ |
| |
| void |
| finish_fname_decls (void) |
| { |
| unsigned ix; |
| tree stmts = NULL_TREE; |
| tree stack = saved_function_name_decls; |
| |
| for (; stack && TREE_VALUE (stack); stack = TREE_CHAIN (stack)) |
| append_to_statement_list (TREE_VALUE (stack), &stmts); |
| |
| if (stmts) |
| { |
| tree *bodyp = &DECL_SAVED_TREE (current_function_decl); |
| |
| if (TREE_CODE (*bodyp) == BIND_EXPR) |
| bodyp = &BIND_EXPR_BODY (*bodyp); |
| |
| append_to_statement_list_force (*bodyp, &stmts); |
| *bodyp = stmts; |
| } |
| |
| for (ix = 0; fname_vars[ix].decl; ix++) |
| *fname_vars[ix].decl = NULL_TREE; |
| |
| if (stack) |
| { |
| /* We had saved values, restore them. */ |
| tree saved; |
| |
| for (saved = TREE_PURPOSE (stack); saved; saved = TREE_CHAIN (saved)) |
| { |
| tree decl = TREE_PURPOSE (saved); |
| unsigned ix = TREE_INT_CST_LOW (TREE_VALUE (saved)); |
| |
| *fname_vars[ix].decl = decl; |
| } |
| stack = TREE_CHAIN (stack); |
| } |
| saved_function_name_decls = stack; |
| } |
| |
| /* Return the text name of the current function, suitably prettified |
| by PRETTY_P. Return string must be freed by caller. */ |
| |
| const char * |
| fname_as_string (int pretty_p) |
| { |
| const char *name = "top level"; |
| char *namep; |
| int vrb = 2, len; |
| cpp_string cstr = { 0, 0 }, strname; |
| |
| if (!pretty_p) |
| { |
| name = ""; |
| vrb = 0; |
| } |
| |
| if (current_function_decl) |
| name = lang_hooks.decl_printable_name (current_function_decl, vrb); |
| |
| len = strlen (name) + 3; /* Two for '"'s. One for NULL. */ |
| |
| namep = XNEWVEC (char, len); |
| snprintf (namep, len, "\"%s\"", name); |
| strname.text = (unsigned char *) namep; |
| strname.len = len - 1; |
| |
| if (cpp_interpret_string (parse_in, &strname, 1, &cstr, CPP_STRING)) |
| { |
| XDELETEVEC (namep); |
| return (const char *) cstr.text; |
| } |
| |
| return namep; |
| } |
| |
| /* Return the VAR_DECL for a const char array naming the current |
| function. If the VAR_DECL has not yet been created, create it |
| now. RID indicates how it should be formatted and IDENTIFIER_NODE |
| ID is its name (unfortunately C and C++ hold the RID values of |
| keywords in different places, so we can't derive RID from ID in |
| this language independent code. LOC is the location of the |
| function. */ |
| |
| tree |
| fname_decl (location_t loc, unsigned int rid, tree id) |
| { |
| unsigned ix; |
| tree decl = NULL_TREE; |
| |
| for (ix = 0; fname_vars[ix].decl; ix++) |
| if (fname_vars[ix].rid == rid) |
| break; |
| |
| decl = *fname_vars[ix].decl; |
| if (!decl) |
| { |
| /* If a tree is built here, it would normally have the lineno of |
| the current statement. Later this tree will be moved to the |
| beginning of the function and this line number will be wrong. |
| To avoid this problem set the lineno to 0 here; that prevents |
| it from appearing in the RTL. */ |
| tree stmts; |
| location_t saved_location = input_location; |
| input_location = UNKNOWN_LOCATION; |
| |
| stmts = push_stmt_list (); |
| decl = (*make_fname_decl) (id, fname_vars[ix].pretty); |
| stmts = pop_stmt_list (stmts); |
| if (!IS_EMPTY_STMT (stmts)) |
| saved_function_name_decls |
| = tree_cons (decl, stmts, saved_function_name_decls); |
| *fname_vars[ix].decl = decl; |
| input_location = saved_location; |
| } |
| if (!ix && !current_function_decl) |
| pedwarn (loc, 0, "%qD is not defined outside of function scope", decl); |
| |
| return decl; |
| } |
| |
| /* Given a STRING_CST, give it a suitable array-of-chars data type. */ |
| |
| tree |
| fix_string_type (tree value) |
| { |
| int length = TREE_STRING_LENGTH (value); |
| int nchars; |
| tree e_type, i_type, a_type; |
| |
| /* Compute the number of elements, for the array type. */ |
| if (TREE_TYPE (value) == char_array_type_node || !TREE_TYPE (value)) |
| { |
| nchars = length; |
| e_type = char_type_node; |
| } |
| else if (TREE_TYPE (value) == char16_array_type_node) |
| { |
| nchars = length / (TYPE_PRECISION (char16_type_node) / BITS_PER_UNIT); |
| e_type = char16_type_node; |
| } |
| else if (TREE_TYPE (value) == char32_array_type_node) |
| { |
| nchars = length / (TYPE_PRECISION (char32_type_node) / BITS_PER_UNIT); |
| e_type = char32_type_node; |
| } |
| else |
| { |
| nchars = length / (TYPE_PRECISION (wchar_type_node) / BITS_PER_UNIT); |
| e_type = wchar_type_node; |
| } |
| |
| /* C89 2.2.4.1, C99 5.2.4.1 (Translation limits). The analogous |
| limit in C++98 Annex B is very large (65536) and is not normative, |
| so we do not diagnose it (warn_overlength_strings is forced off |
| in c_common_post_options). */ |
| if (warn_overlength_strings) |
| { |
| const int nchars_max = flag_isoc99 ? 4095 : 509; |
| const int relevant_std = flag_isoc99 ? 99 : 90; |
| if (nchars - 1 > nchars_max) |
| /* Translators: The %d after 'ISO C' will be 90 or 99. Do not |
| separate the %d from the 'C'. 'ISO' should not be |
| translated, but it may be moved after 'C%d' in languages |
| where modifiers follow nouns. */ |
| pedwarn (input_location, OPT_Woverlength_strings, |
| "string length %qd is greater than the length %qd " |
| "ISO C%d compilers are required to support", |
| nchars - 1, nchars_max, relevant_std); |
| } |
| |
| /* Create the array type for the string constant. The ISO C++ |
| standard says that a string literal has type `const char[N]' or |
| `const wchar_t[N]'. We use the same logic when invoked as a C |
| front-end with -Wwrite-strings. |
| ??? We should change the type of an expression depending on the |
| state of a warning flag. We should just be warning -- see how |
| this is handled in the C++ front-end for the deprecated implicit |
| conversion from string literals to `char*' or `wchar_t*'. |
| |
| The C++ front end relies on TYPE_MAIN_VARIANT of a cv-qualified |
| array type being the unqualified version of that type. |
| Therefore, if we are constructing an array of const char, we must |
| construct the matching unqualified array type first. The C front |
| end does not require this, but it does no harm, so we do it |
| unconditionally. */ |
| i_type = build_index_type (build_int_cst (NULL_TREE, nchars - 1)); |
| a_type = build_array_type (e_type, i_type); |
| if (c_dialect_cxx() || warn_write_strings) |
| a_type = c_build_qualified_type (a_type, TYPE_QUAL_CONST); |
| |
| TREE_TYPE (value) = a_type; |
| TREE_CONSTANT (value) = 1; |
| TREE_READONLY (value) = 1; |
| TREE_STATIC (value) = 1; |
| return value; |
| } |
| |
| /* Print a warning if a constant expression had overflow in folding. |
| Invoke this function on every expression that the language |
| requires to be a constant expression. |
| Note the ANSI C standard says it is erroneous for a |
| constant expression to overflow. */ |
| |
| void |
| constant_expression_warning (tree value) |
| { |
| if (warn_overflow && pedantic |
| && (TREE_CODE (value) == INTEGER_CST || TREE_CODE (value) == REAL_CST |
| || TREE_CODE (value) == FIXED_CST |
| || TREE_CODE (value) == VECTOR_CST |
| || TREE_CODE (value) == COMPLEX_CST) |
| && TREE_OVERFLOW (value)) |
| pedwarn (input_location, OPT_Woverflow, "overflow in constant expression"); |
| } |
| |
| /* The same as above but print an unconditional error. */ |
| void |
| constant_expression_error (tree value) |
| { |
| if ((TREE_CODE (value) == INTEGER_CST || TREE_CODE (value) == REAL_CST |
| || TREE_CODE (value) == FIXED_CST |
| || TREE_CODE (value) == VECTOR_CST |
| || TREE_CODE (value) == COMPLEX_CST) |
| && TREE_OVERFLOW (value)) |
| error ("overflow in constant expression"); |
| } |
| |
| /* Print a warning if an expression had overflow in folding and its |
| operands hadn't. |
| |
| Invoke this function on every expression that |
| (1) appears in the source code, and |
| (2) is a constant expression that overflowed, and |
| (3) is not already checked by convert_and_check; |
| however, do not invoke this function on operands of explicit casts |
| or when the expression is the result of an operator and any operand |
| already overflowed. */ |
| |
| void |
| overflow_warning (tree value) |
| { |
| if (skip_evaluation) return; |
| |
| switch (TREE_CODE (value)) |
| { |
| case INTEGER_CST: |
| warning (OPT_Woverflow, "integer overflow in expression"); |
| break; |
| |
| case REAL_CST: |
| warning (OPT_Woverflow, "floating point overflow in expression"); |
| break; |
| |
| case FIXED_CST: |
| warning (OPT_Woverflow, "fixed-point overflow in expression"); |
| break; |
| |
| case VECTOR_CST: |
| warning (OPT_Woverflow, "vector overflow in expression"); |
| break; |
| |
| case COMPLEX_CST: |
| if (TREE_CODE (TREE_REALPART (value)) == INTEGER_CST) |
| warning (OPT_Woverflow, "complex integer overflow in expression"); |
| else if (TREE_CODE (TREE_REALPART (value)) == REAL_CST) |
| warning (OPT_Woverflow, "complex floating point overflow in expression"); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| |
| /* Warn about use of a logical || / && operator being used in a |
| context where it is likely that the bitwise equivalent was intended |
| by the programmer. CODE is the TREE_CODE of the operator, ARG1 |
| and ARG2 the arguments. */ |
| |
| void |
| warn_logical_operator (enum tree_code code, tree arg1, tree |
| arg2) |
| { |
| switch (code) |
| { |
| case TRUTH_ANDIF_EXPR: |
| case TRUTH_ORIF_EXPR: |
| case TRUTH_OR_EXPR: |
| case TRUTH_AND_EXPR: |
| if (!TREE_NO_WARNING (arg1) |
| && INTEGRAL_TYPE_P (TREE_TYPE (arg1)) |
| && !CONSTANT_CLASS_P (arg1) |
| && TREE_CODE (arg2) == INTEGER_CST |
| && !integer_zerop (arg2)) |
| { |
| warning (OPT_Wlogical_op, |
| "logical %<%s%> with non-zero constant " |
| "will always evaluate as true", |
| ((code == TRUTH_ANDIF_EXPR) |
| || (code == TRUTH_AND_EXPR)) ? "&&" : "||"); |
| TREE_NO_WARNING (arg1) = true; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| |
| /* Print a warning about casts that might indicate violation |
| of strict aliasing rules if -Wstrict-aliasing is used and |
| strict aliasing mode is in effect. OTYPE is the original |
| TREE_TYPE of EXPR, and TYPE the type we're casting to. */ |
| |
| bool |
| strict_aliasing_warning (tree otype, tree type, tree expr) |
| { |
| if (!(flag_strict_aliasing |
| && POINTER_TYPE_P (type) |
| && POINTER_TYPE_P (otype) |
| && !VOID_TYPE_P (TREE_TYPE (type))) |
| /* If the type we are casting to is a ref-all pointer |
| dereferencing it is always valid. */ |
| || TYPE_REF_CAN_ALIAS_ALL (type)) |
| return false; |
| |
| if ((warn_strict_aliasing > 1) && TREE_CODE (expr) == ADDR_EXPR |
| && (DECL_P (TREE_OPERAND (expr, 0)) |
| || handled_component_p (TREE_OPERAND (expr, 0)))) |
| { |
| /* Casting the address of an object to non void pointer. Warn |
| if the cast breaks type based aliasing. */ |
| if (!COMPLETE_TYPE_P (TREE_TYPE (type)) && warn_strict_aliasing == 2) |
| { |
| warning (OPT_Wstrict_aliasing, "type-punning to incomplete type " |
| "might break strict-aliasing rules"); |
| return true; |
| } |
| else |
| { |
| /* warn_strict_aliasing >= 3. This includes the default (3). |
| Only warn if the cast is dereferenced immediately. */ |
| alias_set_type set1 = |
| get_alias_set (TREE_TYPE (TREE_OPERAND (expr, 0))); |
| alias_set_type set2 = get_alias_set (TREE_TYPE (type)); |
| |
| if (set1 != set2 && set2 != 0 |
| && (set1 == 0 || !alias_sets_conflict_p (set1, set2))) |
| { |
| warning (OPT_Wstrict_aliasing, "dereferencing type-punned " |
| "pointer will break strict-aliasing rules"); |
| return true; |
| } |
| else if (warn_strict_aliasing == 2 |
| && !alias_sets_must_conflict_p (set1, set2)) |
| { |
| warning (OPT_Wstrict_aliasing, "dereferencing type-punned " |
| "pointer might break strict-aliasing rules"); |
| return true; |
| } |
| } |
| } |
| else |
| if ((warn_strict_aliasing == 1) && !VOID_TYPE_P (TREE_TYPE (otype))) |
| { |
| /* At this level, warn for any conversions, even if an address is |
| not taken in the same statement. This will likely produce many |
| false positives, but could be useful to pinpoint problems that |
| are not revealed at higher levels. */ |
| alias_set_type set1 = get_alias_set (TREE_TYPE (otype)); |
| alias_set_type set2 = get_alias_set (TREE_TYPE (type)); |
| if (!COMPLETE_TYPE_P (type) |
| || !alias_sets_must_conflict_p (set1, set2)) |
| { |
| warning (OPT_Wstrict_aliasing, "dereferencing type-punned " |
| "pointer might break strict-aliasing rules"); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /* Warn for unlikely, improbable, or stupid DECL declarations |
| of `main'. */ |
| |
| void |
| check_main_parameter_types (tree decl) |
| { |
| tree args; |
| int argct = 0; |
| |
| for (args = TYPE_ARG_TYPES (TREE_TYPE (decl)); args; |
| args = TREE_CHAIN (args)) |
| { |
| tree type = args ? TREE_VALUE (args) : 0; |
| |
| if (type == void_type_node || type == error_mark_node ) |
| break; |
| |
| ++argct; |
| switch (argct) |
| { |
| case 1: |
| if (TYPE_MAIN_VARIANT (type) != integer_type_node) |
| pedwarn (input_location, OPT_Wmain, "first argument of %q+D should be %<int%>", |
| decl); |
| break; |
| |
| case 2: |
| if (TREE_CODE (type) != POINTER_TYPE |
| || TREE_CODE (TREE_TYPE (type)) != POINTER_TYPE |
| || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (type))) |
| != char_type_node)) |
| pedwarn (input_location, OPT_Wmain, "second argument of %q+D should be %<char **%>", |
| decl); |
| break; |
| |
| case 3: |
| if (TREE_CODE (type) != POINTER_TYPE |
| || TREE_CODE (TREE_TYPE (type)) != POINTER_TYPE |
| || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (type))) |
| != char_type_node)) |
| pedwarn (input_location, OPT_Wmain, "third argument of %q+D should probably be " |
| "%<char **%>", decl); |
| break; |
| } |
| } |
| |
| /* It is intentional that this message does not mention the third |
| argument because it's only mentioned in an appendix of the |
| standard. */ |
| if (argct > 0 && (argct < 2 || argct > 3)) |
| pedwarn (input_location, OPT_Wmain, "%q+D takes only zero or two arguments", decl); |
| } |
| |
| /* True if pointers to distinct types T1 and T2 can be converted to |
| each other without an explicit cast. Only returns true for opaque |
| vector types. */ |
| bool |
| vector_targets_convertible_p (const_tree t1, const_tree t2) |
| { |
| if (TREE_CODE (t1) == VECTOR_TYPE && TREE_CODE (t2) == VECTOR_TYPE |
| && (targetm.vector_opaque_p (t1) || targetm.vector_opaque_p (t2)) |
| && tree_int_cst_equal (TYPE_SIZE (t1), TYPE_SIZE (t2))) |
| return true; |
| |
| return false; |
| } |
| |
| /* True if vector types T1 and T2 can be converted to each other |
| without an explicit cast. If EMIT_LAX_NOTE is true, and T1 and T2 |
| can only be converted with -flax-vector-conversions yet that is not |
| in effect, emit a note telling the user about that option if such |
| a note has not previously been emitted. */ |
| bool |
| vector_types_convertible_p (const_tree t1, const_tree t2, bool emit_lax_note) |
| { |
| static bool emitted_lax_note = false; |
| bool convertible_lax; |
| |
| if ((targetm.vector_opaque_p (t1) || targetm.vector_opaque_p (t2)) |
| && tree_int_cst_equal (TYPE_SIZE (t1), TYPE_SIZE (t2))) |
| return true; |
| |
| convertible_lax = |
| (tree_int_cst_equal (TYPE_SIZE (t1), TYPE_SIZE (t2)) |
| && (TREE_CODE (TREE_TYPE (t1)) != REAL_TYPE || |
| TYPE_PRECISION (t1) == TYPE_PRECISION (t2)) |
| && (INTEGRAL_TYPE_P (TREE_TYPE (t1)) |
| == INTEGRAL_TYPE_P (TREE_TYPE (t2)))); |
| |
| if (!convertible_lax || flag_lax_vector_conversions) |
| return convertible_lax; |
| |
| if (TYPE_VECTOR_SUBPARTS (t1) == TYPE_VECTOR_SUBPARTS (t2) |
| && lang_hooks.types_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2))) |
| return true; |
| |
| if (emit_lax_note && !emitted_lax_note) |
| { |
| emitted_lax_note = true; |
| inform (input_location, "use -flax-vector-conversions to permit " |
| "conversions between vectors with differing " |
| "element types or numbers of subparts"); |
| } |
| |
| return false; |
| } |
| |
| /* This is a helper function of build_binary_op. |
| |
| For certain operations if both args were extended from the same |
| smaller type, do the arithmetic in that type and then extend. |
| |
| BITWISE indicates a bitwise operation. |
| For them, this optimization is safe only if |
| both args are zero-extended or both are sign-extended. |
| Otherwise, we might change the result. |
| Eg, (short)-1 | (unsigned short)-1 is (int)-1 |
| but calculated in (unsigned short) it would be (unsigned short)-1. |
| */ |
| tree shorten_binary_op (tree result_type, tree op0, tree op1, bool bitwise) |
| { |
| int unsigned0, unsigned1; |
| tree arg0, arg1; |
| int uns; |
| tree type; |
| |
| /* Cast OP0 and OP1 to RESULT_TYPE. Doing so prevents |
| excessive narrowing when we call get_narrower below. For |
| example, suppose that OP0 is of unsigned int extended |
| from signed char and that RESULT_TYPE is long long int. |
| If we explicitly cast OP0 to RESULT_TYPE, OP0 would look |
| like |
| |
| (long long int) (unsigned int) signed_char |
| |
| which get_narrower would narrow down to |
| |
| (unsigned int) signed char |
| |
| If we do not cast OP0 first, get_narrower would return |
| signed_char, which is inconsistent with the case of the |
| explicit cast. */ |
| op0 = convert (result_type, op0); |
| op1 = convert (result_type, op1); |
| |
| arg0 = get_narrower (op0, &unsigned0); |
| arg1 = get_narrower (op1, &unsigned1); |
| |
| /* UNS is 1 if the operation to be done is an unsigned one. */ |
| uns = TYPE_UNSIGNED (result_type); |
| |
| /* Handle the case that OP0 (or OP1) does not *contain* a conversion |
| but it *requires* conversion to FINAL_TYPE. */ |
| |
| if ((TYPE_PRECISION (TREE_TYPE (op0)) |
| == TYPE_PRECISION (TREE_TYPE (arg0))) |
| && TREE_TYPE (op0) != result_type) |
| unsigned0 = TYPE_UNSIGNED (TREE_TYPE (op0)); |
| if ((TYPE_PRECISION (TREE_TYPE (op1)) |
| == TYPE_PRECISION (TREE_TYPE (arg1))) |
| && TREE_TYPE (op1) != result_type) |
| unsigned1 = TYPE_UNSIGNED (TREE_TYPE (op1)); |
| |
| /* Now UNSIGNED0 is 1 if ARG0 zero-extends to FINAL_TYPE. */ |
| |
| /* For bitwise operations, signedness of nominal type |
| does not matter. Consider only how operands were extended. */ |
| if (bitwise) |
| uns = unsigned0; |
| |
| /* Note that in all three cases below we refrain from optimizing |
| an unsigned operation on sign-extended args. |
| That would not be valid. */ |
| |
| /* Both args variable: if both extended in same way |
| from same width, do it in that width. |
| Do it unsigned if args were zero-extended. */ |
| if ((TYPE_PRECISION (TREE_TYPE (arg0)) |
| < TYPE_PRECISION (result_type)) |
| && (TYPE_PRECISION (TREE_TYPE (arg1)) |
| == TYPE_PRECISION (TREE_TYPE (arg0))) |
| && unsigned0 == unsigned1 |
| && (unsigned0 || !uns)) |
| return c_common_signed_or_unsigned_type |
| (unsigned0, common_type (TREE_TYPE (arg0), TREE_TYPE (arg1))); |
| |
| else if (TREE_CODE (arg0) == INTEGER_CST |
| && (unsigned1 || !uns) |
| && (TYPE_PRECISION (TREE_TYPE (arg1)) |
| < TYPE_PRECISION (result_type)) |
| && (type |
| = c_common_signed_or_unsigned_type (unsigned1, |
| TREE_TYPE (arg1))) |
| && !POINTER_TYPE_P (type) |
| && int_fits_type_p (arg0, type)) |
| return type; |
| |
| else if (TREE_CODE (arg1) == INTEGER_CST |
| && (unsigned0 || !uns) |
| && (TYPE_PRECISION (TREE_TYPE (arg0)) |
| < TYPE_PRECISION (result_type)) |
| && (type |
| = c_common_signed_or_unsigned_type (unsigned0, |
| TREE_TYPE (arg0))) |
| && !POINTER_TYPE_P (type) |
| && int_fits_type_p (arg1, type)) |
| return type; |
| |
| return result_type; |
| } |
| |
| /* Warns if the conversion of EXPR to TYPE may alter a value. |
| This is a helper function for warnings_for_convert_and_check. */ |
| |
| static void |
| conversion_warning (tree type, tree expr) |
| { |
| bool give_warning = false; |
| |
| int i; |
| const int expr_num_operands = TREE_OPERAND_LENGTH (expr); |
| tree expr_type = TREE_TYPE (expr); |
| |
| if (!warn_conversion && !warn_sign_conversion) |
| return; |
| |
| /* If any operand is artificial, then this expression was generated |
| by the compiler and we do not warn. */ |
| for (i = 0; i < expr_num_operands; i++) |
| { |
| tree op = TREE_OPERAND (expr, i); |
| if (op && DECL_P (op) && DECL_ARTIFICIAL (op)) |
| return; |
| } |
| |
| switch (TREE_CODE (expr)) |
| { |
| case EQ_EXPR: |
| case NE_EXPR: |
| case LE_EXPR: |
| case GE_EXPR: |
| case LT_EXPR: |
| case GT_EXPR: |
| case TRUTH_ANDIF_EXPR: |
| case TRUTH_ORIF_EXPR: |
| case TRUTH_AND_EXPR: |
| case TRUTH_OR_EXPR: |
| case TRUTH_XOR_EXPR: |
| case TRUTH_NOT_EXPR: |
| /* Conversion from boolean to a signed:1 bit-field (which only |
| can hold the values 0 and -1) doesn't lose information - but |
| it does change the value. */ |
| if (TYPE_PRECISION (type) == 1 && !TYPE_UNSIGNED (type)) |
| warning (OPT_Wconversion, |
| "conversion to %qT from boolean expression", type); |
| return; |
| |
| case REAL_CST: |
| case INTEGER_CST: |
| |
| /* Warn for real constant that is not an exact integer converted |
| to integer type. */ |
| if (TREE_CODE (expr_type) == REAL_TYPE |
| && TREE_CODE (type) == INTEGER_TYPE) |
| { |
| if (!real_isinteger (TREE_REAL_CST_PTR (expr), TYPE_MODE (expr_type))) |
| give_warning = true; |
| } |
| /* Warn for an integer constant that does not fit into integer type. */ |
| else if (TREE_CODE (expr_type) == INTEGER_TYPE |
| && TREE_CODE (type) == INTEGER_TYPE |
| && !int_fits_type_p (expr, type)) |
| { |
| if (TYPE_UNSIGNED (type) && !TYPE_UNSIGNED (expr_type) |
| && tree_int_cst_sgn (expr) < 0) |
| warning (OPT_Wsign_conversion, |
| "negative integer implicitly converted to unsigned type"); |
| else if (!TYPE_UNSIGNED (type) && TYPE_UNSIGNED (expr_type)) |
| warning (OPT_Wsign_conversion, "conversion of unsigned constant " |
| "value to negative integer"); |
| else |
| give_warning = true; |
| } |
| else if (TREE_CODE (type) == REAL_TYPE) |
| { |
| /* Warn for an integer constant that does not fit into real type. */ |
| if (TREE_CODE (expr_type) == INTEGER_TYPE) |
| { |
| REAL_VALUE_TYPE a = real_value_from_int_cst (0, expr); |
| if (!exact_real_truncate (TYPE_MODE (type), &a)) |
| give_warning = true; |
| } |
| /* Warn for a real constant that does not fit into a smaller |
| real type. */ |
| else if (TREE_CODE (expr_type) == REAL_TYPE |
| && TYPE_PRECISION (type) < TYPE_PRECISION (expr_type)) |
| { |
| REAL_VALUE_TYPE a = TREE_REAL_CST (expr); |
| if (!exact_real_truncate (TYPE_MODE (type), &a)) |
| give_warning = true; |
| } |
| } |
| |
| if (give_warning) |
| warning (OPT_Wconversion, |
| "conversion to %qT alters %qT constant value", |
| type, expr_type); |
| |
| return; |
| |
| case COND_EXPR: |
| { |
| /* In case of COND_EXPR, if both operands are constants or |
| COND_EXPR, then we do not care about the type of COND_EXPR, |
| only about the conversion of each operand. */ |
| tree op1 = TREE_OPERAND (expr, 1); |
| tree op2 = TREE_OPERAND (expr, 2); |
| |
| if ((TREE_CODE (op1) == REAL_CST || TREE_CODE (op1) == INTEGER_CST |
| || TREE_CODE (op1) == COND_EXPR) |
| && (TREE_CODE (op2) == REAL_CST || TREE_CODE (op2) == INTEGER_CST |
| || TREE_CODE (op2) == COND_EXPR)) |
| { |
| conversion_warning (type, op1); |
| conversion_warning (type, op2); |
| return; |
| } |
| /* Fall through. */ |
| } |
| |
| default: /* 'expr' is not a constant. */ |
| |
| /* Warn for real types converted to integer types. */ |
| if (TREE_CODE (expr_type) == REAL_TYPE |
| && TREE_CODE (type) == INTEGER_TYPE) |
| give_warning = true; |
| |
| else if (TREE_CODE (expr_type) == INTEGER_TYPE |
| && TREE_CODE (type) == INTEGER_TYPE) |
| { |
| /* Don't warn about unsigned char y = 0xff, x = (int) y; */ |
| expr = get_unwidened (expr, 0); |
| expr_type = TREE_TYPE (expr); |
| |
| /* Don't warn for short y; short x = ((int)y & 0xff); */ |
| if (TREE_CODE (expr) == BIT_AND_EXPR |
| || TREE_CODE (expr) == BIT_IOR_EXPR |
| || TREE_CODE (expr) == BIT_XOR_EXPR) |
| { |
| /* If both args were extended from a shortest type, |
| use that type if that is safe. */ |
| expr_type = shorten_binary_op (expr_type, |
| TREE_OPERAND (expr, 0), |
| TREE_OPERAND (expr, 1), |
| /* bitwise */1); |
| |
| if (TREE_CODE (expr) == BIT_AND_EXPR) |
| { |
| tree op0 = TREE_OPERAND (expr, 0); |
| tree op1 = TREE_OPERAND (expr, 1); |
| bool unsigned0 = TYPE_UNSIGNED (TREE_TYPE (op0)); |
| bool unsigned1 = TYPE_UNSIGNED (TREE_TYPE (op1)); |
| |
| /* If one of the operands is a non-negative constant |
| that fits in the target type, then the type of the |
| other operand does not matter. */ |
| if ((TREE_CODE (op0) == INTEGER_CST |
| && int_fits_type_p (op0, c_common_signed_type (type)) |
| && int_fits_type_p (op0, c_common_unsigned_type (type))) |
| || (TREE_CODE (op1) == INTEGER_CST |
| && int_fits_type_p (op1, c_common_signed_type (type)) |
| && int_fits_type_p (op1, |
| c_common_unsigned_type (type)))) |
| return; |
| /* If constant is unsigned and fits in the target |
| type, then the result will also fit. */ |
| else if ((TREE_CODE (op0) == INTEGER_CST |
| && unsigned0 |
| && int_fits_type_p (op0, type)) |
| || (TREE_CODE (op1) == INTEGER_CST |
| && unsigned1 |
| && int_fits_type_p (op1, type))) |
| return; |
| } |
| } |
| /* Warn for integer types converted to smaller integer types. */ |
| if (TYPE_PRECISION (type) < TYPE_PRECISION (expr_type)) |
| give_warning = true; |
| |
| /* When they are the same width but different signedness, |
| then the value may change. */ |
| else if ((TYPE_PRECISION (type) == TYPE_PRECISION (expr_type) |
| && TYPE_UNSIGNED (expr_type) != TYPE_UNSIGNED (type)) |
| /* Even when converted to a bigger type, if the type is |
| unsigned but expr is signed, then negative values |
| will be changed. */ |
| || (TYPE_UNSIGNED (type) && !TYPE_UNSIGNED (expr_type))) |
| warning (OPT_Wsign_conversion, "conversion to %qT from %qT " |
| "may change the sign of the result", |
| type, expr_type); |
| } |
| |
| /* Warn for integer types converted to real types if and only if |
| all the range of values of the integer type cannot be |
| represented by the real type. */ |
| else if (TREE_CODE (expr_type) == INTEGER_TYPE |
| && TREE_CODE (type) == REAL_TYPE) |
| { |
| tree type_low_bound = TYPE_MIN_VALUE (expr_type); |
| tree type_high_bound = TYPE_MAX_VALUE (expr_type); |
| REAL_VALUE_TYPE real_low_bound |
| = real_value_from_int_cst (0, type_low_bound); |
| REAL_VALUE_TYPE real_high_bound |
| = real_value_from_int_cst (0, type_high_bound); |
| |
| if (!exact_real_truncate (TYPE_MODE (type), &real_low_bound) |
| || !exact_real_truncate (TYPE_MODE (type), &real_high_bound)) |
| give_warning = true; |
| } |
| |
| /* Warn for real types converted to smaller real types. */ |
| else if (TREE_CODE (expr_type) == REAL_TYPE |
| && TREE_CODE (type) == REAL_TYPE |
| && TYPE_PRECISION (type) < TYPE_PRECISION (expr_type)) |
| give_warning = true; |
| |
| |
| if (give_warning) |
| warning (OPT_Wconversion, |
| "conversion to %qT from %qT may alter its value", |
| type, expr_type); |
| } |
| } |
| |
| /* Produce warnings after a conversion. RESULT is the result of |
| converting EXPR to TYPE. This is a helper function for |
| convert_and_check and cp_convert_and_check. */ |
| |
| void |
| warnings_for_convert_and_check (tree type, tree expr, tree result) |
| { |
| if (TREE_CODE (expr) == INTEGER_CST |
| && (TREE_CODE (type) == INTEGER_TYPE |
| || TREE_CODE (type) == ENUMERAL_TYPE) |
| && !int_fits_type_p (expr, type)) |
| { |
| /* Do not diagnose overflow in a constant expression merely |
| because a conversion overflowed. */ |
| if (TREE_OVERFLOW (result)) |
| TREE_OVERFLOW (result) = TREE_OVERFLOW (expr); |
| |
| if (TYPE_UNSIGNED (type)) |
| { |
| /* This detects cases like converting -129 or 256 to |
| unsigned char. */ |
| if (!int_fits_type_p (expr, c_common_signed_type (type))) |
| warning (OPT_Woverflow, |
| "large integer implicitly truncated to unsigned type"); |
| else |
| conversion_warning (type, expr); |
| } |
| else if (!int_fits_type_p (expr, c_common_unsigned_type (type))) |
| warning (OPT_Woverflow, |
| "overflow in implicit constant conversion"); |
| /* No warning for converting 0x80000000 to int. */ |
| else if (pedantic |
| && (TREE_CODE (TREE_TYPE (expr)) != INTEGER_TYPE |
| || TYPE_PRECISION (TREE_TYPE (expr)) |
| != TYPE_PRECISION (type))) |
| warning (OPT_Woverflow, |
| "overflow in implicit constant conversion"); |
| |
| else |
| conversion_warning (type, expr); |
| } |
| else if ((TREE_CODE (result) == INTEGER_CST |
| || TREE_CODE (result) == FIXED_CST) && TREE_OVERFLOW (result)) |
| warning (OPT_Woverflow, |
| "overflow in implicit constant conversion"); |
| else |
| conversion_warning (type, expr); |
| } |
| |
| |
| /* Convert EXPR to TYPE, warning about conversion problems with constants. |
| Invoke this function on every expression that is converted implicitly, |
| i.e. because of language rules and not because of an explicit cast. */ |
| |
| tree |
| convert_and_check (tree type, tree expr) |
| { |
| tree result; |
| |
| if (TREE_TYPE (expr) == type) |
| return expr; |
| |
| result = convert (type, expr); |
| |
| if (!skip_evaluation && !TREE_OVERFLOW_P (expr) && result != error_mark_node) |
| warnings_for_convert_and_check (type, expr, result); |
| |
| return result; |
| } |
| |
| /* A node in a list that describes references to variables (EXPR), which are |
| either read accesses if WRITER is zero, or write accesses, in which case |
| WRITER is the parent of EXPR. */ |
| struct tlist |
| { |
| struct tlist *next; |
| tree expr, writer; |
| }; |
| |
| /* Used to implement a cache the results of a call to verify_tree. We only |
| use this for SAVE_EXPRs. */ |
| struct tlist_cache |
| { |
| struct tlist_cache *next; |
| struct tlist *cache_before_sp; |
| struct tlist *cache_after_sp; |
| tree expr; |
| }; |
| |
| /* Obstack to use when allocating tlist structures, and corresponding |
| firstobj. */ |
| static struct obstack tlist_obstack; |
| static char *tlist_firstobj = 0; |
| |
| /* Keep track of the identifiers we've warned about, so we can avoid duplicate |
| warnings. */ |
| static struct tlist *warned_ids; |
| /* SAVE_EXPRs need special treatment. We process them only once and then |
| cache the results. */ |
| static struct tlist_cache *save_expr_cache; |
| |
| static void add_tlist (struct tlist **, struct tlist *, tree, int); |
| static void merge_tlist (struct tlist **, struct tlist *, int); |
| static void verify_tree (tree, struct tlist **, struct tlist **, tree); |
| static int warning_candidate_p (tree); |
| static void warn_for_collisions (struct tlist *); |
| static void warn_for_collisions_1 (tree, tree, struct tlist *, int); |
| static struct tlist *new_tlist (struct tlist *, tree, tree); |
| |
| /* Create a new struct tlist and fill in its fields. */ |
| static struct tlist * |
| new_tlist (struct tlist *next, tree t, tree writer) |
| { |
| struct tlist *l; |
| l = XOBNEW (&tlist_obstack, struct tlist); |
| l->next = next; |
| l->expr = t; |
| l->writer = writer; |
| return l; |
| } |
| |
| /* Add duplicates of the nodes found in ADD to the list *TO. If EXCLUDE_WRITER |
| is nonnull, we ignore any node we find which has a writer equal to it. */ |
| |
| static void |
| add_tlist (struct tlist **to, struct tlist *add, tree exclude_writer, int copy) |
| { |
| while (add) |
| { |
| struct tlist *next = add->next; |
| if (!copy) |
| add->next = *to; |
| if (!exclude_writer || add->writer != exclude_writer) |
| *to = copy ? new_tlist (*to, add->expr, add->writer) : add; |
| add = next; |
| } |
| } |
| |
| /* Merge the nodes of ADD into TO. This merging process is done so that for |
| each variable that already exists in TO, no new node is added; however if |
| there is a write access recorded in ADD, and an occurrence on TO is only |
| a read access, then the occurrence in TO will be modified to record the |
| write. */ |
| |
| static void |
| merge_tlist (struct tlist **to, struct tlist *add, int copy) |
| { |
| struct tlist **end = to; |
| |
| while (*end) |
| end = &(*end)->next; |
| |
| while (add) |
| { |
| int found = 0; |
| struct tlist *tmp2; |
| struct tlist *next = add->next; |
| |
| for (tmp2 = *to; tmp2; tmp2 = tmp2->next) |
| if (tmp2->expr == add->expr) |
| { |
| found = 1; |
| if (!tmp2->writer) |
| tmp2->writer = add->writer; |
| } |
| if (!found) |
| { |
| *end = copy ? add : new_tlist (NULL, add->expr, add->writer); |
| end = &(*end)->next; |
| *end = 0; |
| } |
| add = next; |
| } |
| } |
| |
| /* WRITTEN is a variable, WRITER is its parent. Warn if any of the variable |
| references in list LIST conflict with it, excluding reads if ONLY writers |
| is nonzero. */ |
| |
| static void |
| warn_for_collisions_1 (tree written, tree writer, struct tlist *list, |
| int only_writes) |
| { |
| struct tlist *tmp; |
| |
| /* Avoid duplicate warnings. */ |
| for (tmp = warned_ids; tmp; tmp = tmp->next) |
| if (tmp->expr == written) |
| return; |
| |
| while (list) |
| { |
| if (list->expr == written |
| && list->writer != writer |
| && (!only_writes || list->writer) |
| && DECL_NAME (list->expr)) |
| { |
| warned_ids = new_tlist (warned_ids, written, NULL_TREE); |
| warning_at (EXPR_HAS_LOCATION (writer) |
| ? EXPR_LOCATION (writer) : input_location, |
| OPT_Wsequence_point, "operation on %qE may be undefined", |
| list->expr); |
| } |
| list = list->next; |
| } |
| } |
| |
| /* Given a list LIST of references to variables, find whether any of these |
| can cause conflicts due to missing sequence points. */ |
| |
| static void |
| warn_for_collisions (struct tlist *list) |
| { |
| struct tlist *tmp; |
| |
| for (tmp = list; tmp; tmp = tmp->next) |
| { |
| if (tmp->writer) |
| warn_for_collisions_1 (tmp->expr, tmp->writer, list, 0); |
| } |
| } |
| |
| /* Return nonzero if X is a tree that can be verified by the sequence point |
| warnings. */ |
| static int |
| warning_candidate_p (tree x) |
| { |
| return TREE_CODE (x) == VAR_DECL || TREE_CODE (x) == PARM_DECL; |
| } |
| |
| /* Walk the tree X, and record accesses to variables. If X is written by the |
| parent tree, WRITER is the parent. |
| We store accesses in one of the two lists: PBEFORE_SP, and PNO_SP. If this |
| expression or its only operand forces a sequence point, then everything up |
| to the sequence point is stored in PBEFORE_SP. Everything else gets stored |
| in PNO_SP. |
| Once we return, we will have emitted warnings if any subexpression before |
| such a sequence point could be undefined. On a higher level, however, the |
| sequence point may not be relevant, and we'll merge the two lists. |
| |
| Example: (b++, a) + b; |
| The call that processes the COMPOUND_EXPR will store the increment of B |
| in PBEFORE_SP, and the use of A in PNO_SP. The higher-level call that |
| processes the PLUS_EXPR will need to merge the two lists so that |
| eventually, all accesses end up on the same list (and we'll warn about the |
| unordered subexpressions b++ and b. |
| |
| A note on merging. If we modify the former example so that our expression |
| becomes |
| (b++, b) + a |
| care must be taken not simply to add all three expressions into the final |
| PNO_SP list. The function merge_tlist takes care of that by merging the |
| before-SP list of the COMPOUND_EXPR into its after-SP list in a special |
| way, so that no more than one access to B is recorded. */ |
| |
| static void |
| verify_tree (tree x, struct tlist **pbefore_sp, struct tlist **pno_sp, |
| tree writer) |
| { |
| struct tlist *tmp_before, *tmp_nosp, *tmp_list2, *tmp_list3; |
| enum tree_code code; |
| enum tree_code_class cl; |
| |
| /* X may be NULL if it is the operand of an empty statement expression |
| ({ }). */ |
| if (x == NULL) |
| return; |
| |
| restart: |
| code = TREE_CODE (x); |
| cl = TREE_CODE_CLASS (code); |
| |
| if (warning_candidate_p (x)) |
| { |
| *pno_sp = new_tlist (*pno_sp, x, writer); |
| return; |
| } |
| |
| switch (code) |
| { |
| case CONSTRUCTOR: |
| return; |
| |
| case COMPOUND_EXPR: |
| case TRUTH_ANDIF_EXPR: |
| case TRUTH_ORIF_EXPR: |
| tmp_before = tmp_nosp = tmp_list3 = 0; |
| verify_tree (TREE_OPERAND (x, 0), &tmp_before, &tmp_nosp, NULL_TREE); |
| warn_for_collisions (tmp_nosp); |
| merge_tlist (pbefore_sp, tmp_before, 0); |
| merge_tlist (pbefore_sp, tmp_nosp, 0); |
| verify_tree (TREE_OPERAND (x, 1), &tmp_list3, pno_sp, NULL_TREE); |
| merge_tlist (pbefore_sp, tmp_list3, 0); |
| return; |
| |
| case COND_EXPR: |
| tmp_before = tmp_list2 = 0; |
| verify_tree (TREE_OPERAND (x, 0), &tmp_before, &tmp_list2, NULL_TREE); |
| warn_for_collisions (tmp_list2); |
| merge_tlist (pbefore_sp, tmp_before, 0); |
| merge_tlist (pbefore_sp, tmp_list2, 1); |
| |
| tmp_list3 = tmp_nosp = 0; |
| verify_tree (TREE_OPERAND (x, 1), &tmp_list3, &tmp_nosp, NULL_TREE); |
| warn_for_collisions (tmp_nosp); |
| merge_tlist (pbefore_sp, tmp_list3, 0); |
| |
| tmp_list3 = tmp_list2 = 0; |
| verify_tree (TREE_OPERAND (x, 2), &tmp_list3, &tmp_list2, NULL_TREE); |
| warn_for_collisions (tmp_list2); |
| merge_tlist (pbefore_sp, tmp_list3, 0); |
| /* Rather than add both tmp_nosp and tmp_list2, we have to merge the |
| two first, to avoid warning for (a ? b++ : b++). */ |
| merge_tlist (&tmp_nosp, tmp_list2, 0); |
| add_tlist (pno_sp, tmp_nosp, NULL_TREE, 0); |
| return; |
| |
| case PREDECREMENT_EXPR: |
| case PREINCREMENT_EXPR: |
| case POSTDECREMENT_EXPR: |
| case POSTINCREMENT_EXPR: |
| verify_tree (TREE_OPERAND (x, 0), pno_sp, pno_sp, x); |
| return; |
| |
| case MODIFY_EXPR: |
| tmp_before = tmp_nosp = tmp_list3 = 0; |
| verify_tree (TREE_OPERAND (x, 1), &tmp_before, &tmp_nosp, NULL_TREE); |
| verify_tree (TREE_OPERAND (x, 0), &tmp_list3, &tmp_list3, x); |
| /* Expressions inside the LHS are not ordered wrt. the sequence points |
| in the RHS. Example: |
| *a = (a++, 2) |
| Despite the fact that the modification of "a" is in the before_sp |
| list (tmp_before), it conflicts with the use of "a" in the LHS. |
| We can handle this by adding the contents of tmp_list3 |
| to those of tmp_before, and redoing the collision warnings for that |
| list. */ |
| add_tlist (&tmp_before, tmp_list3, x, 1); |
| warn_for_collisions (tmp_before); |
| /* Exclude the LHS itself here; we first have to merge it into the |
| tmp_nosp list. This is done to avoid warning for "a = a"; if we |
| didn't exclude the LHS, we'd get it twice, once as a read and once |
| as a write. */ |
| add_tlist (pno_sp, tmp_list3, x, 0); |
| warn_for_collisions_1 (TREE_OPERAND (x, 0), x, tmp_nosp, 1); |
| |
| merge_tlist (pbefore_sp, tmp_before, 0); |
| if (warning_candidate_p (TREE_OPERAND (x, 0))) |
| merge_tlist (&tmp_nosp, new_tlist (NULL, TREE_OPERAND (x, 0), x), 0); |
| add_tlist (pno_sp, tmp_nosp, NULL_TREE, 1); |
| return; |
| |
| case CALL_EXPR: |
| /* We need to warn about conflicts among arguments and conflicts between |
| args and the function address. Side effects of the function address, |
| however, are not ordered by the sequence point of the call. */ |
| { |
| call_expr_arg_iterator iter; |
| tree arg; |
| tmp_before = tmp_nosp = 0; |
| verify_tree (CALL_EXPR_FN (x), &tmp_before, &tmp_nosp, NULL_TREE); |
| FOR_EACH_CALL_EXPR_ARG (arg, iter, x) |
| { |
| tmp_list2 = tmp_list3 = 0; |
| verify_tree (arg, &tmp_list2, &tmp_list3, NULL_TREE); |
| merge_tlist (&tmp_list3, tmp_list2, 0); |
| add_tlist (&tmp_before, tmp_list3, NULL_TREE, 0); |
| } |
| add_tlist (&tmp_before, tmp_nosp, NULL_TREE, 0); |
| warn_for_collisions (tmp_before); |
| add_tlist (pbefore_sp, tmp_before, NULL_TREE, 0); |
| return; |
| } |
| |
| case TREE_LIST: |
| /* Scan all the list, e.g. indices of multi dimensional array. */ |
| while (x) |
| { |
| tmp_before = tmp_nosp = 0; |
| verify_tree (TREE_VALUE (x), &tmp_before, &tmp_nosp, NULL_TREE); |
| merge_tlist (&tmp_nosp, tmp_before, 0); |
| add_tlist (pno_sp, tmp_nosp, NULL_TREE, 0); |
| x = TREE_CHAIN (x); |
| } |
| return; |
| |
| case SAVE_EXPR: |
| { |
| struct tlist_cache *t; |
| for (t = save_expr_cache; t; t = t->next) |
| if (t->expr == x) |
| break; |
| |
| if (!t) |
| { |
| t = XOBNEW (&tlist_obstack, struct tlist_cache); |
| t->next = save_expr_cache; |
| t->expr = x; |
| save_expr_cache = t; |
| |
| tmp_before = tmp_nosp = 0; |
| verify_tree (TREE_OPERAND (x, 0), &tmp_before, &tmp_nosp, NULL_TREE); |
| warn_for_collisions (tmp_nosp); |
| |
| tmp_list3 = 0; |
| while (tmp_nosp) |
| { |
| struct tlist *t = tmp_nosp; |
| tmp_nosp = t->next; |
| merge_tlist (&tmp_list3, t, 0); |
| } |
| t->cache_before_sp = tmp_before; |
| t->cache_after_sp = tmp_list3; |
| } |
| merge_tlist (pbefore_sp, t->cache_before_sp, 1); |
| add_tlist (pno_sp, t->cache_after_sp, NULL_TREE, 1); |
| return; |
| } |
| |
| case ADDR_EXPR: |
| x = TREE_OPERAND (x, 0); |
| if (DECL_P (x)) |
| return; |
| writer = 0; |
| goto restart; |
| |
| default: |
| /* For other expressions, simply recurse on their operands. |
| Manual tail recursion for unary expressions. |
| Other non-expressions need not be processed. */ |
| if (cl == tcc_unary) |
| { |
| x = TREE_OPERAND (x, 0); |
| writer = 0; |
| goto restart; |
| } |
| else if (IS_EXPR_CODE_CLASS (cl)) |
| { |
| int lp; |
| int max = TREE_OPERAND_LENGTH (x); |
| for (lp = 0; lp < max; lp++) |
| { |
| tmp_before = tmp_nosp = 0; |
| verify_tree (TREE_OPERAND (x, lp), &tmp_before, &tmp_nosp, 0); |
| merge_tlist (&tmp_nosp, tmp_before, 0); |
| add_tlist (pno_sp, tmp_nosp, NULL_TREE, 0); |
| } |
| } |
| return; |
| } |
| } |
| |
| /* Try to warn for undefined behavior in EXPR due to missing sequence |
| points. */ |
| |
| void |
| verify_sequence_points (tree expr) |
| { |
| struct tlist *before_sp = 0, *after_sp = 0; |
| |
| warned_ids = 0; |
| save_expr_cache = 0; |
| if (tlist_firstobj == 0) |
| { |
| gcc_obstack_init (&tlist_obstack); |
| tlist_firstobj = (char *) obstack_alloc (&tlist_obstack, 0); |
| } |
| |
| verify_tree (expr, &before_sp, &after_sp, 0); |
| warn_for_collisions (after_sp); |
| obstack_free (&tlist_obstack, tlist_firstobj); |
| } |
| |
| /* Validate the expression after `case' and apply default promotions. */ |
| |
| static tree |
| check_case_value (tree value) |
| { |
| if (value == NULL_TREE) |
| return value; |
| |
| /* ??? Can we ever get nops here for a valid case value? We |
| shouldn't for C. */ |
| STRIP_TYPE_NOPS (value); |
| /* In C++, the following is allowed: |
| |
| const int i = 3; |
| switch (...) { case i: ... } |
| |
| So, we try to reduce the VALUE to a constant that way. */ |
| if (c_dialect_cxx ()) |
| { |
| value = decl_constant_value (value); |
| STRIP_TYPE_NOPS (value); |
| value = fold (value); |
| } |
| |
| if (TREE_CODE (value) == INTEGER_CST) |
| /* Promote char or short to int. */ |
| value = perform_integral_promotions (value); |
| else if (value != error_mark_node) |
| { |
| error ("case label does not reduce to an integer constant"); |
| value = error_mark_node; |
| } |
| |
| constant_expression_warning (value); |
| |
| return value; |
| } |
| |
| /* See if the case values LOW and HIGH are in the range of the original |
| type (i.e. before the default conversion to int) of the switch testing |
| expression. |
| TYPE is the promoted type of the testing expression, and ORIG_TYPE is |
| the type before promoting it. CASE_LOW_P is a pointer to the lower |
| bound of the case label, and CASE_HIGH_P is the upper bound or NULL |
| if the case is not a case range. |
| The caller has to make sure that we are not called with NULL for |
| CASE_LOW_P (i.e. the default case). |
| Returns true if the case label is in range of ORIG_TYPE (saturated or |
| untouched) or false if the label is out of range. */ |
| |
| static bool |
| check_case_bounds (tree type, tree orig_type, |
| tree *case_low_p, tree *case_high_p) |
| { |
| tree min_value, max_value; |
| tree case_low = *case_low_p; |
| tree case_high = case_high_p ? *case_high_p : case_low; |
| |
| /* If there was a problem with the original type, do nothing. */ |
| if (orig_type == error_mark_node) |
| return true; |
| |
| min_value = TYPE_MIN_VALUE (orig_type); |
| max_value = TYPE_MAX_VALUE (orig_type); |
| |
| /* Case label is less than minimum for type. */ |
| if (tree_int_cst_compare (case_low, min_value) < 0 |
| && tree_int_cst_compare (case_high, min_value) < 0) |
| { |
| warning (0, "case label value is less than minimum value for type"); |
| return false; |
| } |
| |
| /* Case value is greater than maximum for type. */ |
| if (tree_int_cst_compare (case_low, max_value) > 0 |
| && tree_int_cst_compare (case_high, max_value) > 0) |
| { |
| warning (0, "case label value exceeds maximum value for type"); |
| return false; |
| } |
| |
| /* Saturate lower case label value to minimum. */ |
| if (tree_int_cst_compare (case_high, min_value) >= 0 |
| && tree_int_cst_compare (case_low, min_value) < 0) |
| { |
| warning (0, "lower value in case label range" |
| " less than minimum value for type"); |
| case_low = min_value; |
| } |
| |
| /* Saturate upper case label value to maximum. */ |
| if (tree_int_cst_compare (case_low, max_value) <= 0 |
| && tree_int_cst_compare (case_high, max_value) > 0) |
| { |
| warning (0, "upper value in case label range" |
| " exceeds maximum value for type"); |
| case_high = max_value; |
| } |
| |
| if (*case_low_p != case_low) |
| *case_low_p = convert (type, case_low); |
| if (case_high_p && *case_high_p != case_high) |
| *case_high_p = convert (type, case_high); |
| |
| return true; |
| } |
| |
| /* Return an integer type with BITS bits of precision, |
| that is unsigned if UNSIGNEDP is nonzero, otherwise signed. */ |
| |
| tree |
| c_common_type_for_size (unsigned int bits, int unsignedp) |
| { |
| if (bits == TYPE_PRECISION (integer_type_node)) |
| return unsignedp ? unsigned_type_node : integer_type_node; |
| |
| if (bits == TYPE_PRECISION (signed_char_type_node)) |
| return unsignedp ? unsigned_char_type_node : signed_char_type_node; |
| |
| if (bits == TYPE_PRECISION (short_integer_type_node)) |
| return unsignedp ? short_unsigned_type_node : short_integer_type_node; |
| |
| if (bits == TYPE_PRECISION (long_integer_type_node)) |
| return unsignedp ? long_unsigned_type_node : long_integer_type_node; |
| |
| if (bits == TYPE_PRECISION (long_long_integer_type_node)) |
| return (unsignedp ? long_long_unsigned_type_node |
| : long_long_integer_type_node); |
| |
| if (bits == TYPE_PRECISION (widest_integer_literal_type_node)) |
| return (unsignedp ? widest_unsigned_literal_type_node |
| : widest_integer_literal_type_node); |
| |
| if (bits <= TYPE_PRECISION (intQI_type_node)) |
| return unsignedp ? unsigned_intQI_type_node : intQI_type_node; |
| |
| if (bits <= TYPE_PRECISION (intHI_type_node)) |
| return unsignedp ? unsigned_intHI_type_node : intHI_type_node; |
| |
| if (bits <= TYPE_PRECISION (intSI_type_node)) |
| return unsignedp ? unsigned_intSI_type_node : intSI_type_node; |
| |
| if (bits <= TYPE_PRECISION (intDI_type_node)) |
| return unsignedp ? unsigned_intDI_type_node : intDI_type_node; |
| |
| return 0; |
| } |
| |
| /* Return a fixed-point type that has at least IBIT ibits and FBIT fbits |
| that is unsigned if UNSIGNEDP is nonzero, otherwise signed; |
| and saturating if SATP is nonzero, otherwise not saturating. */ |
| |
| tree |
| c_common_fixed_point_type_for_size (unsigned int ibit, unsigned int fbit, |
| int unsignedp, int satp) |
| { |
| enum machine_mode mode; |
| if (ibit == 0) |
| mode = unsignedp ? UQQmode : QQmode; |
| else |
| mode = unsignedp ? UHAmode : HAmode; |
| |
| for (; mode != VOIDmode; mode = GET_MODE_WIDER_MODE (mode)) |
| if (GET_MODE_IBIT (mode) >= ibit && GET_MODE_FBIT (mode) >= fbit) |
| break; |
| |
| if (mode == VOIDmode || !targetm.scalar_mode_supported_p (mode)) |
| { |
| sorry ("GCC cannot support operators with integer types and " |
| "fixed-point types that have too many integral and " |
| "fractional bits together"); |
| return 0; |
| } |
| |
| return c_common_type_for_mode (mode, satp); |
| } |
| |
| /* Used for communication between c_common_type_for_mode and |
| c_register_builtin_type. */ |
| static GTY(()) tree registered_builtin_types; |
| |
| /* Return a data type that has machine mode MODE. |
| If the mode is an integer, |
| then UNSIGNEDP selects between signed and unsigned types. |
| If the mode is a fixed-point mode, |
| then UNSIGNEDP selects between saturating and nonsaturating types. */ |
| |
| tree |
| c_common_type_for_mode (enum machine_mode mode, int unsignedp) |
| { |
| tree t; |
| |
| if (mode == TYPE_MODE (integer_type_node)) |
| return unsignedp ? unsigned_type_node : integer_type_node; |
| |
| if (mode == TYPE_MODE (signed_char_type_node)) |
| return unsignedp ? unsigned_char_type_node : signed_char_type_node; |
| |
| if (mode == TYPE_MODE (short_integer_type_node)) |
| return unsignedp ? short_unsigned_type_node : short_integer_type_node; |
| |
| if (mode == TYPE_MODE (long_integer_type_node)) |
| return unsignedp ? long_unsigned_type_node : long_integer_type_node; |
| |
| if (mode == TYPE_MODE (long_long_integer_type_node)) |
| return unsignedp ? long_long_unsigned_type_node : long_long_integer_type_node; |
| |
| if (mode == TYPE_MODE (widest_integer_literal_type_node)) |
| return unsignedp ? widest_unsigned_literal_type_node |
| : widest_integer_literal_type_node; |
| |
| if (mode == QImode) |
| return unsignedp ? unsigned_intQI_type_node : intQI_type_node; |
| |
| if (mode == HImode) |
| return unsignedp ? unsigned_intHI_type_node : intHI_type_node; |
| |
| if (mode == SImode) |
| return unsignedp ? unsigned_intSI_type_node : intSI_type_node; |
| |
| if (mode == DImode) |
| return unsignedp ? unsigned_intDI_type_node : intDI_type_node; |
| |
| #if HOST_BITS_PER_WIDE_INT >= 64 |
| if (mode == TYPE_MODE (intTI_type_node)) |
| return unsignedp ? unsigned_intTI_type_node : intTI_type_node; |
| #endif |
| |
| if (mode == TYPE_MODE (float_type_node)) |
| return float_type_node; |
| |
| if (mode == TYPE_MODE (double_type_node)) |
| return double_type_node; |
| |
| if (mode == TYPE_MODE (long_double_type_node)) |
| return long_double_type_node; |
| |
| if (mode == TYPE_MODE (void_type_node)) |
| return void_type_node; |
| |
| if (mode == TYPE_MODE (build_pointer_type (char_type_node))) |
| return (unsignedp |
| ? make_unsigned_type (GET_MODE_PRECISION (mode)) |
| : make_signed_type (GET_MODE_PRECISION (mode))); |
| |
| if (mode == TYPE_MODE (build_pointer_type (integer_type_node))) |
| return (unsignedp |
| ? make_unsigned_type (GET_MODE_PRECISION (mode)) |
| : make_signed_type (GET_MODE_PRECISION (mode))); |
| |
| if (COMPLEX_MODE_P (mode)) |
| { |
| enum machine_mode inner_mode; |
| tree inner_type; |
| |
| if (mode == TYPE_MODE (complex_float_type_node)) |
| return complex_float_type_node; |
| if (mode == TYPE_MODE (complex_double_type_node)) |
| return complex_double_type_node; |
| if (mode == TYPE_MODE (complex_long_double_type_node)) |
| return complex_long_double_type_node; |
| |
| if (mode == TYPE_MODE (complex_integer_type_node) && !unsignedp) |
| return complex_integer_type_node; |
| |
| inner_mode = GET_MODE_INNER (mode); |
| inner_type = c_common_type_for_mode (inner_mode, unsignedp); |
| if (inner_type != NULL_TREE) |
| return build_complex_type (inner_type); |
| } |
| else if (VECTOR_MODE_P (mode)) |
| { |
| enum machine_mode inner_mode = GET_MODE_INNER (mode); |
| tree inner_type = c_common_type_for_mode (inner_mode, unsignedp); |
| if (inner_type != NULL_TREE) |
| return build_vector_type_for_mode (inner_type, mode); |
| } |
| |
| if (mode == TYPE_MODE (dfloat32_type_node)) |
| return dfloat32_type_node; |
| if (mode == TYPE_MODE (dfloat64_type_node)) |
| return dfloat64_type_node; |
| if (mode == TYPE_MODE (dfloat128_type_node)) |
| return dfloat128_type_node; |
| |
| if (ALL_SCALAR_FIXED_POINT_MODE_P (mode)) |
| { |
| if (mode == TYPE_MODE (short_fract_type_node)) |
| return unsignedp ? sat_short_fract_type_node : short_fract_type_node; |
| if (mode == TYPE_MODE (fract_type_node)) |
| return unsignedp ? sat_fract_type_node : fract_type_node; |
| if (mode == TYPE_MODE (long_fract_type_node)) |
| return unsignedp ? sat_long_fract_type_node : long_fract_type_node; |
| if (mode == TYPE_MODE (long_long_fract_type_node)) |
| return unsignedp ? sat_long_long_fract_type_node |
| : long_long_fract_type_node; |
| |
| if (mode == TYPE_MODE (unsigned_short_fract_type_node)) |
| return unsignedp ? sat_unsigned_short_fract_type_node |
| : unsigned_short_fract_type_node; |
| if (mode == TYPE_MODE (unsigned_fract_type_node)) |
| return unsignedp ? sat_unsigned_fract_type_node |
| : unsigned_fract_type_node; |
| if (mode == TYPE_MODE (unsigned_long_fract_type_node)) |
| return unsignedp ? sat_unsigned_long_fract_type_node |
| : unsigned_long_fract_type_node; |
| if (mode == TYPE_MODE (unsigned_long_long_fract_type_node)) |
| return unsignedp ? sat_unsigned_long_long_fract_type_node |
| : unsigned_long_long_fract_type_node; |
| |
| if (mode == TYPE_MODE (short_accum_type_node)) |
| return unsignedp ? sat_short_accum_type_node : short_accum_type_node; |
| if (mode == TYPE_MODE (accum_type_node)) |
| return unsignedp ? sat_accum_type_node : accum_type_node; |
| if (mode == TYPE_MODE (long_accum_type_node)) |
| return unsignedp ? sat_long_accum_type_node : long_accum_type_node; |
| if (mode == TYPE_MODE (long_long_accum_type_node)) |
| return unsignedp ? sat_long_long_accum_type_node |
| : long_long_accum_type_node; |
| |
| if (mode == TYPE_MODE (unsigned_short_accum_type_node)) |
| return unsignedp ? sat_unsigned_short_accum_type_node |
| : unsigned_short_accum_type_node; |
| if (mode == TYPE_MODE (unsigned_accum_type_node)) |
| return unsignedp ? sat_unsigned_accum_type_node |
| : unsigned_accum_type_node; |
| if (mode == TYPE_MODE (unsigned_long_accum_type_node)) |
| return unsignedp ? sat_unsigned_long_accum_type_node |
| : unsigned_long_accum_type_node; |
| if (mode == TYPE_MODE (unsigned_long_long_accum_type_node)) |
| return unsignedp ? sat_unsigned_long_long_accum_type_node |
| : unsigned_long_long_accum_type_node; |
| |
| if (mode == QQmode) |
| return unsignedp ? sat_qq_type_node : qq_type_node; |
| if (mode == HQmode) |
| return unsignedp ? sat_hq_type_node : hq_type_node; |
| if (mode == SQmode) |
| return unsignedp ? sat_sq_type_node : sq_type_node; |
| if (mode == DQmode) |
| return unsignedp ? sat_dq_type_node : dq_type_node; |
| if (mode == TQmode) |
| return unsignedp ? sat_tq_type_node : tq_type_node; |
| |
| if (mode == UQQmode) |
| return unsignedp ? sat_uqq_type_node : uqq_type_node; |
| if (mode == UHQmode) |
| return unsignedp ? sat_uhq_type_node : uhq_type_node; |
| if (mode == USQmode) |
| return unsignedp ? sat_usq_type_node : usq_type_node; |
| if (mode == UDQmode) |
| return unsignedp ? sat_udq_type_node : udq_type_node; |
| if (mode == UTQmode) |
| return unsignedp ? sat_utq_type_node : utq_type_node; |
| |
| if (mode == HAmode) |
| return unsignedp ? sat_ha_type_node : ha_type_node; |
| if (mode == SAmode) |
| return unsignedp ? sat_sa_type_node : sa_type_node; |
| if (mode == DAmode) |
| return unsignedp ? sat_da_type_node : da_type_node; |
| if (mode == TAmode) |
| return unsignedp ? sat_ta_type_node : ta_type_node; |
| |
| if (mode == UHAmode) |
| return unsignedp ? sat_uha_type_node : uha_type_node; |
| if (mode == USAmode) |
| return unsignedp ? sat_usa_type_node : usa_type_node; |
| if (mode == UDAmode) |
| return unsignedp ? sat_uda_type_node : uda_type_node; |
| if (mode == UTAmode) |
| return unsignedp ? sat_uta_type_node : uta_type_node; |
| } |
| |
| for (t = registered_builtin_types; t; t = TREE_CHAIN (t)) |
| if (TYPE_MODE (TREE_VALUE (t)) == mode) |
| return TREE_VALUE (t); |
| |
| return 0; |
| } |
| |
| tree |
| c_common_unsigned_type (tree type) |
| { |
| return c_common_signed_or_unsigned_type (1, type); |
| } |
| |
| /* Return a signed type the same as TYPE in other respects. */ |
| |
| tree |
| c_common_signed_type (tree type) |
| { |
| return c_common_signed_or_unsigned_type (0, type); |
| } |
| |
| /* Return a type the same as TYPE except unsigned or |
| signed according to UNSIGNEDP. */ |
| |
| tree |
| c_common_signed_or_unsigned_type (int unsignedp, tree type) |
| { |
| tree type1; |
| |
| /* This block of code emulates the behavior of the old |
| c_common_unsigned_type. In particular, it returns |
| long_unsigned_type_node if passed a long, even when a int would |
| have the same size. This is necessary for warnings to work |
| correctly in archs where sizeof(int) == sizeof(long) */ |
| |
| type1 = TYPE_MAIN_VARIANT (type); |
| if (type1 == signed_char_type_node || type1 == char_type_node || type1 == unsigned_char_type_node) |
| return unsignedp ? unsigned_char_type_node : signed_char_type_node; |
| if (type1 == integer_type_node || type1 == unsigned_type_node) |
| return unsignedp ? unsigned_type_node : integer_type_node; |
| if (type1 == short_integer_type_node || type1 == short_unsigned_type_node) |
| return unsignedp ? short_unsigned_type_node : short_integer_type_node; |
| if (type1 == long_integer_type_node || type1 == long_unsigned_type_node) |
| return unsignedp ? long_unsigned_type_node : long_integer_type_node; |
| if (type1 == long_long_integer_type_node || type1 == long_long_unsigned_type_node) |
| return unsignedp ? long_long_unsigned_type_node : long_long_integer_type_node; |
| if (type1 == widest_integer_literal_type_node || type1 == widest_unsigned_literal_type_node) |
| return unsignedp ? widest_unsigned_literal_type_node : widest_integer_literal_type_node; |
| #if HOST_BITS_PER_WIDE_INT >= 64 |
| if (type1 == intTI_type_node || type1 == unsigned_intTI_type_node) |
| return unsignedp ? unsigned_intTI_type_node : intTI_type_node; |
| #endif |
| if (type1 == intDI_type_node || type1 == unsigned_intDI_type_node) |
| return unsignedp ? unsigned_intDI_type_node : intDI_type_node; |
| if (type1 == intSI_type_node || type1 == unsigned_intSI_type_node) |
| return unsignedp ? unsigned_intSI_type_node : intSI_type_node; |
| if (type1 == intHI_type_node || type1 == unsigned_intHI_type_node) |
| return unsignedp ? unsigned_intHI_type_node : intHI_type_node; |
| if (type1 == intQI_type_node || type1 == unsigned_intQI_type_node) |
| return unsignedp ? unsigned_intQI_type_node : intQI_type_node; |
| |
| #define C_COMMON_FIXED_TYPES(NAME) \ |
| if (type1 == short_ ## NAME ## _type_node \ |
| || type1 == unsigned_short_ ## NAME ## _type_node) \ |
| return unsignedp ? unsigned_short_ ## NAME ## _type_node \ |
| : short_ ## NAME ## _type_node; \ |
| if (type1 == NAME ## _type_node \ |
| || type1 == unsigned_ ## NAME ## _type_node) \ |
| return unsignedp ? unsigned_ ## NAME ## _type_node \ |
| : NAME ## _type_node; \ |
| if (type1 == long_ ## NAME ## _type_node \ |
| || type1 == unsigned_long_ ## NAME ## _type_node) \ |
| return unsignedp ? unsigned_long_ ## NAME ## _type_node \ |
| : long_ ## NAME ## _type_node; \ |
| if (type1 == long_long_ ## NAME ## _type_node \ |
| || type1 == unsigned_long_long_ ## NAME ## _type_node) \ |
| return unsignedp ? unsigned_long_long_ ## NAME ## _type_node \ |
| : long_long_ ## NAME ## _type_node; |
| |
| #define C_COMMON_FIXED_MODE_TYPES(NAME) \ |
| if (type1 == NAME ## _type_node \ |
| || type1 == u ## NAME ## _type_node) \ |
| return unsignedp ? u ## NAME ## _type_node \ |
| : NAME ## _type_node; |
| |
| #define C_COMMON_FIXED_TYPES_SAT(NAME) \ |
| if (type1 == sat_ ## short_ ## NAME ## _type_node \ |
| || type1 == sat_ ## unsigned_short_ ## NAME ## _type_node) \ |
| return unsignedp ? sat_ ## unsigned_short_ ## NAME ## _type_node \ |
| : sat_ ## short_ ## NAME ## _type_node; \ |
| if (type1 == sat_ ## NAME ## _type_node \ |
| || type1 == sat_ ## unsigned_ ## NAME ## _type_node) \ |
| return unsignedp ? sat_ ## unsigned_ ## NAME ## _type_node \ |
| : sat_ ## NAME ## _type_node; \ |
| if (type1 == sat_ ## long_ ## NAME ## _type_node \ |
| || type1 == sat_ ## unsigned_long_ ## NAME ## _type_node) \ |
| return unsignedp ? sat_ ## unsigned_long_ ## NAME ## _type_node \ |
| : sat_ ## long_ ## NAME ## _type_node; \ |
| if (type1 == sat_ ## long_long_ ## NAME ## _type_node \ |
| || type1 == sat_ ## unsigned_long_long_ ## NAME ## _type_node) \ |
| return unsignedp ? sat_ ## unsigned_long_long_ ## NAME ## _type_node \ |
| : sat_ ## long_long_ ## NAME ## _type_node; |
| |
| #define C_COMMON_FIXED_MODE_TYPES_SAT(NAME) \ |
| if (type1 == sat_ ## NAME ## _type_node \ |
| || type1 == sat_ ## u ## NAME ## _type_node) \ |
| return unsignedp ? sat_ ## u ## NAME ## _type_node \ |
| : sat_ ## NAME ## _type_node; |
| |
| C_COMMON_FIXED_TYPES (fract); |
| C_COMMON_FIXED_TYPES_SAT (fract); |
| C_COMMON_FIXED_TYPES (accum); |
| C_COMMON_FIXED_TYPES_SAT (accum); |
| |
| C_COMMON_FIXED_MODE_TYPES (qq); |
| C_COMMON_FIXED_MODE_TYPES (hq); |
| C_COMMON_FIXED_MODE_TYPES (sq); |
| C_COMMON_FIXED_MODE_TYPES (dq); |
| C_COMMON_FIXED_MODE_TYPES (tq); |
| C_COMMON_FIXED_MODE_TYPES_SAT (qq); |
| C_COMMON_FIXED_MODE_TYPES_SAT (hq); |
| C_COMMON_FIXED_MODE_TYPES_SAT (sq); |
| C_COMMON_FIXED_MODE_TYPES_SAT (dq); |
| C_COMMON_FIXED_MODE_TYPES_SAT (tq); |
| C_COMMON_FIXED_MODE_TYPES (ha); |
| C_COMMON_FIXED_MODE_TYPES (sa); |
| C_COMMON_FIXED_MODE_TYPES (da); |
| C_COMMON_FIXED_MODE_TYPES (ta); |
| C_COMMON_FIXED_MODE_TYPES_SAT (ha); |
| C_COMMON_FIXED_MODE_TYPES_SAT (sa); |
| C_COMMON_FIXED_MODE_TYPES_SAT (da); |
| C_COMMON_FIXED_MODE_TYPES_SAT (ta); |
| |
| /* For ENUMERAL_TYPEs in C++, must check the mode of the types, not |
| the precision; they have precision set to match their range, but |
| may use a wider mode to match an ABI. If we change modes, we may |
| wind up with bad conversions. For INTEGER_TYPEs in C, must check |
| the precision as well, so as to yield correct results for |
| bit-field types. C++ does not have these separate bit-field |
| types, and producing a signed or unsigned variant of an |
| ENUMERAL_TYPE may cause other problems as well. */ |
| |
| if (!INTEGRAL_TYPE_P (type) |
| || TYPE_UNSIGNED (type) == unsignedp) |
| return type; |
| |
| #define TYPE_OK(node) \ |
| (TYPE_MODE (type) == TYPE_MODE (node) \ |
| && TYPE_PRECISION (type) == TYPE_PRECISION (node)) |
| if (TYPE_OK (signed_char_type_node)) |
| return unsignedp ? unsigned_char_type_node : signed_char_type_node; |
| if (TYPE_OK (integer_type_node)) |
| return unsignedp ? unsigned_type_node : integer_type_node; |
| if (TYPE_OK (short_integer_type_node)) |
| return unsignedp ? short_unsigned_type_node : short_integer_type_node; |
| if (TYPE_OK (long_integer_type_node)) |
| return unsignedp ? long_unsigned_type_node : long_integer_type_node; |
| if (TYPE_OK (long_long_integer_type_node)) |
| return (unsignedp ? long_long_unsigned_type_node |
| : long_long_integer_type_node); |
| if (TYPE_OK (widest_integer_literal_type_node)) |
| return (unsignedp ? widest_unsigned_literal_type_node |
| : widest_integer_literal_type_node); |
| |
| #if HOST_BITS_PER_WIDE_INT >= 64 |
| if (TYPE_OK (intTI_type_node)) |
| return unsignedp ? unsigned_intTI_type_node : intTI_type_node; |
| #endif |
| if (TYPE_OK (intDI_type_node)) |
| return unsignedp ? unsigned_intDI_type_node : intDI_type_node; |
| if (TYPE_OK (intSI_type_node)) |
| return unsignedp ? unsigned_intSI_type_node : intSI_type_node; |
| if (TYPE_OK (intHI_type_node)) |
| return unsignedp ? unsigned_intHI_type_node : intHI_type_node; |
| if (TYPE_OK (intQI_type_node)) |
| return unsignedp ? unsigned_intQI_type_node : intQI_type_node; |
| #undef TYPE_OK |
| |
| return build_nonstandard_integer_type (TYPE_PRECISION (type), unsignedp); |
| } |
| |
| /* Build a bit-field integer type for the given WIDTH and UNSIGNEDP. */ |
| |
| tree |
| c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp) |
| { |
| /* Extended integer types of the same width as a standard type have |
| lesser rank, so those of the same width as int promote to int or |
| unsigned int and are valid for printf formats expecting int or |
| unsigned int. To avoid such special cases, avoid creating |
| extended integer types for bit-fields if a standard integer type |
| is available. */ |
| if (width == TYPE_PRECISION (integer_type_node)) |
| return unsignedp ? unsigned_type_node : integer_type_node; |
| if (width == TYPE_PRECISION (signed_char_type_node)) |
| return unsignedp ? unsigned_char_type_node : signed_char_type_node; |
| if (width == TYPE_PRECISION (short_integer_type_node)) |
| return unsignedp ? short_unsigned_type_node : short_integer_type_node; |
| if (width == TYPE_PRECISION (long_integer_type_node)) |
| return unsignedp ? long_unsigned_type_node : long_integer_type_node; |
| if (width == TYPE_PRECISION (long_long_integer_type_node)) |
| return (unsignedp ? long_long_unsigned_type_node |
| : long_long_integer_type_node); |
| return build_nonstandard_integer_type (width, unsignedp); |
| } |
| |
| /* The C version of the register_builtin_type langhook. */ |
| |
| void |
| c_register_builtin_type (tree type, const char* name) |
| { |
| tree decl; |
| |
| decl = build_decl (TYPE_DECL, get_identifier (name), type); |
| DECL_ARTIFICIAL (decl) = 1; |
| if (!TYPE_NAME (type)) |
| TYPE_NAME (type) = decl; |
| pushdecl (decl); |
| |
| registered_builtin_types = tree_cons (0, type, registered_builtin_types); |
| } |
| |
| /* Print an error message for invalid operands to arith operation |
| CODE with TYPE0 for operand 0, and TYPE1 for operand 1. |
| LOCATION is the location of the message. */ |
| |
| void |
| binary_op_error (location_t location, enum tree_code code, |
| tree type0, tree type1) |
| { |
| const char *opname; |
| |
| switch (code) |
| { |
| case PLUS_EXPR: |
| opname = "+"; break; |
| case MINUS_EXPR: |
| opname = "-"; break; |
| case MULT_EXPR: |
| opname = "*"; break; |
| case MAX_EXPR: |
| opname = "max"; break; |
| case MIN_EXPR: |
| opname = "min"; break; |
| case EQ_EXPR: |
| opname = "=="; break; |
| case NE_EXPR: |
| opname = "!="; break; |
| case LE_EXPR: |
| opname = "<="; break; |
| case GE_EXPR: |
| opname = ">="; break; |
| case LT_EXPR: |
| opname = "<"; break; |
| case GT_EXPR: |
| opname = ">"; break; |
| case LSHIFT_EXPR: |
| opname = "<<"; break; |
| case RSHIFT_EXPR: |
| opname = ">>"; break; |
| case TRUNC_MOD_EXPR: |
| case FLOOR_MOD_EXPR: |
| opname = "%"; break; |
| case TRUNC_DIV_EXPR: |
| case FLOOR_DIV_EXPR: |
| opname = "/"; break; |
| case BIT_AND_EXPR: |
| opname = "&"; break; |
| case BIT_IOR_EXPR: |
| opname = "|"; break; |
| case TRUTH_ANDIF_EXPR: |
| opname = "&&"; break; |
| case TRUTH_ORIF_EXPR: |
| opname = "||"; break; |
| case BIT_XOR_EXPR: |
| opname = "^"; break; |
| default: |
| gcc_unreachable (); |
| } |
| error_at (location, |
| "invalid operands to binary %s (have %qT and %qT)", opname, |
| type0, type1); |
| } |
| |
| /* Subroutine of build_binary_op, used for comparison operations. |
| See if the operands have both been converted from subword integer types |
| and, if so, perhaps change them both back to their original type. |
| This function is also responsible for converting the two operands |
| to the proper common type for comparison. |
| |
| The arguments of this function are all pointers to local variables |
| of build_binary_op: OP0_PTR is &OP0, OP1_PTR is &OP1, |
| RESTYPE_PTR is &RESULT_TYPE and RESCODE_PTR is &RESULTCODE. |
| |
| If this function returns nonzero, it means that the comparison has |
| a constant value. What this function returns is an expression for |
| that value. */ |
| |
| tree |
| shorten_compare (tree *op0_ptr, tree *op1_ptr, tree *restype_ptr, |
| enum tree_code *rescode_ptr) |
| { |
| tree type; |
| tree op0 = *op0_ptr; |
| tree op1 = *op1_ptr; |
| int unsignedp0, unsignedp1; |
| int real1, real2; |
| tree primop0, primop1; |
| enum tree_code code = *rescode_ptr; |
| |
| /* Throw away any conversions to wider types |
| already present in the operands. */ |
| |
| primop0 = get_narrower (op0, &unsignedp0); |
| primop1 = get_narrower (op1, &unsignedp1); |
| |
| /* Handle the case that OP0 does not *contain* a conversion |
| but it *requires* conversion to FINAL_TYPE. */ |
| |
| if (op0 == primop0 && TREE_TYPE (op0) != *restype_ptr) |
| unsignedp0 = TYPE_UNSIGNED (TREE_TYPE (op0)); |
| if (op1 == primop1 && TREE_TYPE (op1) != *restype_ptr) |
| unsignedp1 = TYPE_UNSIGNED (TREE_TYPE (op1)); |
| |
| /* If one of the operands must be floated, we cannot optimize. */ |
| real1 = TREE_CODE (TREE_TYPE (primop0)) == REAL_TYPE; |
| real2 = TREE_CODE (TREE_TYPE (primop1)) == REAL_TYPE; |
| |
| /* If first arg is constant, swap the args (changing operation |
| so value is preserved), for canonicalization. Don't do this if |
| the second arg is 0. */ |
| |
| if (TREE_CONSTANT (primop0) |
| && !integer_zerop (primop1) && !real_zerop (primop1) |
| && !fixed_zerop (primop1)) |
| { |
| tree tem = primop0; |
| int temi = unsignedp0; |
| primop0 = primop1; |
| primop1 = tem; |
| tem = op0; |
| op0 = op1; |
| op1 = tem; |
| *op0_ptr = op0; |
| *op1_ptr = op1; |
| unsignedp0 = unsignedp1; |
| unsignedp1 = temi; |
| temi = real1; |
| real1 = real2; |
| real2 = temi; |
| |
| switch (code) |
| { |
| case LT_EXPR: |
| code = GT_EXPR; |
| break; |
| case GT_EXPR: |
| code = LT_EXPR; |
| break; |
| case LE_EXPR: |
| code = GE_EXPR; |
| break; |
| case GE_EXPR: |
| code = LE_EXPR; |
| break; |
| default: |
| break; |
| } |
| *rescode_ptr = code; |
| } |
| |
| /* If comparing an integer against a constant more bits wide, |
| maybe we can deduce a value of 1 or 0 independent of the data. |
| Or else truncate the constant now |
| rather than extend the variable at run time. |
| |
| This is only interesting if the constant is the wider arg. |
| Also, it is not safe if the constant is unsigned and the |
| variable arg is signed, since in this case the variable |
| would be sign-extended and then regarded as unsigned. |
| Our technique fails in this case because the lowest/highest |
| possible unsigned results don't follow naturally from the |
| lowest/highest possible values of the variable operand. |
| For just EQ_EXPR and NE_EXPR there is another technique that |
| could be used: see if the constant can be faithfully represented |
| in the other operand's type, by truncating it and reextending it |
| and see if that preserves the constant's value. */ |
| |
| if (!real1 && !real2 |
| && TREE_CODE (TREE_TYPE (primop0)) != FIXED_POINT_TYPE |
| && TREE_CODE (primop1) == INTEGER_CST |
| && TYPE_PRECISION (TREE_TYPE (primop0)) < TYPE_PRECISION (*restype_ptr)) |
| { |
| int min_gt, max_gt, min_lt, max_lt; |
| tree maxval, minval; |
| /* 1 if comparison is nominally unsigned. */ |
| int unsignedp = TYPE_UNSIGNED (*restype_ptr); |
| tree val; |
| |
| type = c_common_signed_or_unsigned_type (unsignedp0, |
| TREE_TYPE (primop0)); |
| |
| maxval = TYPE_MAX_VALUE (type); |
| minval = TYPE_MIN_VALUE (type); |
| |
| if (unsignedp && !unsignedp0) |
| *restype_ptr = c_common_signed_type (*restype_ptr); |
| |
| if (TREE_TYPE (primop1) != *restype_ptr) |
| { |
| /* Convert primop1 to target type, but do not introduce |
| additional overflow. We know primop1 is an int_cst. */ |
| primop1 = force_fit_type_double (*restype_ptr, |
| TREE_INT_CST_LOW (primop1), |
| TREE_INT_CST_HIGH (primop1), 0, |
| TREE_OVERFLOW (primop1)); |
| } |
| if (type != *restype_ptr) |
| { |
| minval = convert (*restype_ptr, minval); |
| maxval = convert (*restype_ptr, maxval); |
| } |
| |
| if (unsignedp && unsignedp0) |
| { |
| min_gt = INT_CST_LT_UNSIGNED (primop1, minval); |
| max_gt = INT_CST_LT_UNSIGNED (primop1, maxval); |
| min_lt = INT_CST_LT_UNSIGNED (minval, primop1); |
| max_lt = INT_CST_LT_UNSIGNED (maxval, primop1); |
| } |
| else |
| { |
| min_gt = INT_CST_LT (primop1, minval); |
| max_gt = INT_CST_LT (primop1, maxval); |
| min_lt = INT_CST_LT (minval, primop1); |
| max_lt = INT_CST_LT (maxval, primop1); |
| } |
| |
| val = 0; |
| /* This used to be a switch, but Genix compiler can't handle that. */ |
| if (code == NE_EXPR) |
| { |
| if (max_lt || min_gt) |
| val = truthvalue_true_node; |
| } |
| else if (code == EQ_EXPR) |
| { |
| if (max_lt || min_gt) |
| val = truthvalue_false_node; |
| } |
| else if (code == LT_EXPR) |
| { |
| if (max_lt) |
| val = truthvalue_true_node; |
| if (!min_lt) |
| val = truthvalue_false_node; |
| } |
| else if (code == GT_EXPR) |
| { |
| if (min_gt) |
| val = truthvalue_true_node; |
| if (!max_gt) |
| val = truthvalue_false_node; |
| } |
| else if (code == LE_EXPR) |
| { |
| if (!max_gt) |
| val = truthvalue_true_node; |
| if (min_gt) |
| val = truthvalue_false_node; |
| } |
| else if (code == GE_EXPR) |
| { |
| if (!min_lt) |
| val = truthvalue_true_node |