| /* Subroutines shared by all languages that are variants of C. |
| Copyright (C) 1992-2022 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/>. */ |
| |
| #define GCC_C_COMMON_C |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "target.h" |
| #include "function.h" |
| #include "tree.h" |
| #include "memmodel.h" |
| #include "c-common.h" |
| #include "gimple-expr.h" |
| #include "tm_p.h" |
| #include "stringpool.h" |
| #include "cgraph.h" |
| #include "diagnostic.h" |
| #include "intl.h" |
| #include "stor-layout.h" |
| #include "calls.h" |
| #include "attribs.h" |
| #include "varasm.h" |
| #include "trans-mem.h" |
| #include "c-objc.h" |
| #include "common/common-target.h" |
| #include "langhooks.h" |
| #include "tree-inline.h" |
| #include "toplev.h" |
| #include "tree-iterator.h" |
| #include "opts.h" |
| #include "gimplify.h" |
| #include "substring-locations.h" |
| #include "spellcheck.h" |
| #include "c-spellcheck.h" |
| #include "selftest.h" |
| #include "debug.h" |
| #include "tree-vector-builder.h" |
| #include "vec-perm-indices.h" |
| |
| cpp_reader *parse_in; /* Declared in c-pragma.h. */ |
| |
| /* Mode used to build pointers (VOIDmode means ptr_mode). */ |
| |
| machine_mode c_default_pointer_mode = VOIDmode; |
| |
| /* 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 char8_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 `wchar_t[SOMENUMBER]' or something like it. |
| Used when a wide string literal is created. |
| |
| tree wchar_array_type_node; |
| |
| Type `char8_t[SOMENUMBER]' or something like it. |
| Used when a UTF-8 string literal is created. |
| |
| tree char8_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 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; |
| |
| /* 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 (or C11) dialect of C. */ |
| |
| int flag_isoc99; |
| |
| /* Nonzero means use the ISO C11 dialect of C. */ |
| |
| int flag_isoc11; |
| |
| /* Nonzero means use the ISO C2X dialect of C. */ |
| |
| int flag_isoc2x; |
| |
| /* Nonzero means that we have builtin functions, and main is an int. */ |
| |
| int flag_hosted = 1; |
| |
| |
| /* ObjC language option variables. */ |
| |
| |
| /* 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. */ |
| |
| /* The reference version of the ABI for -Wabi. */ |
| |
| int warn_abi_version = -1; |
| |
| /* The C++ dialect being used. Default set in c_common_post_options. */ |
| |
| enum cxx_dialect cxx_dialect = cxx_unset; |
| |
| /* Maximum template instantiation depth. This limit exists to limit the |
| time it takes to notice excessively recursive template instantiations. |
| |
| The default is lower than the 1024 recommended by the C++0x standard |
| because G++ runs out of stack before 1024 with highly recursive template |
| argument deduction substitution (g++.dg/cpp0x/enum11.C). */ |
| |
| int max_tinst_depth = 900; |
| |
| /* 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) (location_t, tree, int); |
| |
| /* Nonzero means don't warn about problems that occur when the code is |
| executed. */ |
| int c_inhibit_evaluation_warnings; |
| |
| /* Whether we are building a boolean conversion inside |
| convert_for_assignment, or some other late binary operation. If |
| build_binary_op is called for C (from code shared by C and C++) in |
| this case, then the operands have already been folded and the |
| result will not be folded again, so C_MAYBE_CONST_EXPR should not |
| be generated. */ |
| bool in_late_binary_op; |
| |
| /* Whether lexing has been completed, so subsequent preprocessor |
| errors should use the compiler's input_location. */ |
| bool done_lexing = false; |
| |
| /* 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}, |
| }; |
| |
| /* Global visibility options. */ |
| struct visibility_flags visibility_options; |
| |
| static tree check_case_value (location_t, tree); |
| |
| |
| static void check_nonnull_arg (void *, tree, unsigned HOST_WIDE_INT); |
| static bool nonnull_check_p (tree, unsigned HOST_WIDE_INT); |
| |
| /* 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=c++98: D_CONLY | D_CXX11 | D_CXX20 | D_OBJC |
| C++ --std=c++11: D_CONLY | D_CXX20 | D_OBJC |
| C++ --std=c++20: 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. |
| |
| Note the complication of the D_CXX_OBJC keywords. These are |
| reserved words such as 'class'. In C++, 'class' is a reserved |
| word. In Objective-C++ it is too. In Objective-C, it is a |
| reserved word too, but only if it follows an '@' sign. |
| */ |
| const struct c_common_resword c_common_reswords[] = |
| { |
| { "_Alignas", RID_ALIGNAS, D_CONLY }, |
| { "_Alignof", RID_ALIGNOF, D_CONLY }, |
| { "_Atomic", RID_ATOMIC, D_CONLY }, |
| { "_Bool", RID_BOOL, D_CONLY }, |
| { "_Complex", RID_COMPLEX, 0 }, |
| { "_Imaginary", RID_IMAGINARY, D_CONLY }, |
| { "_Float16", RID_FLOAT16, D_CONLY }, |
| { "_Float32", RID_FLOAT32, D_CONLY }, |
| { "_Float64", RID_FLOAT64, D_CONLY }, |
| { "_Float128", RID_FLOAT128, D_CONLY }, |
| { "_Float32x", RID_FLOAT32X, D_CONLY }, |
| { "_Float64x", RID_FLOAT64X, D_CONLY }, |
| { "_Float128x", RID_FLOAT128X, D_CONLY }, |
| { "_Decimal32", RID_DFLOAT32, D_CONLY }, |
| { "_Decimal64", RID_DFLOAT64, D_CONLY }, |
| { "_Decimal128", RID_DFLOAT128, D_CONLY }, |
| { "_Fract", RID_FRACT, D_CONLY | D_EXT }, |
| { "_Accum", RID_ACCUM, D_CONLY | D_EXT }, |
| { "_Sat", RID_SAT, D_CONLY | D_EXT }, |
| { "_Static_assert", RID_STATIC_ASSERT, D_CONLY }, |
| { "_Noreturn", RID_NORETURN, D_CONLY }, |
| { "_Generic", RID_GENERIC, D_CONLY }, |
| { "_Thread_local", RID_THREAD, D_CONLY }, |
| { "__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 }, |
| { "__auto_type", RID_AUTO_TYPE, D_CONLY }, |
| { "__bases", RID_BASES, D_CXXONLY }, |
| { "__builtin_addressof", RID_ADDRESSOF, D_CXXONLY }, |
| { "__builtin_bit_cast", RID_BUILTIN_BIT_CAST, D_CXXONLY }, |
| { "__builtin_call_with_static_chain", |
| RID_BUILTIN_CALL_WITH_STATIC_CHAIN, D_CONLY }, |
| { "__builtin_choose_expr", RID_CHOOSE_EXPR, D_CONLY }, |
| { "__builtin_complex", RID_BUILTIN_COMPLEX, D_CONLY }, |
| { "__builtin_convertvector", RID_BUILTIN_CONVERTVECTOR, 0 }, |
| { "__builtin_has_attribute", RID_BUILTIN_HAS_ATTRIBUTE, 0 }, |
| { "__builtin_launder", RID_BUILTIN_LAUNDER, D_CXXONLY }, |
| { "__builtin_assoc_barrier", RID_BUILTIN_ASSOC_BARRIER, 0 }, |
| { "__builtin_shuffle", RID_BUILTIN_SHUFFLE, 0 }, |
| { "__builtin_shufflevector", RID_BUILTIN_SHUFFLEVECTOR, 0 }, |
| { "__builtin_tgmath", RID_BUILTIN_TGMATH, 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 }, |
| { "__constinit", RID_CONSTINIT, D_CXXONLY }, |
| { "__decltype", RID_DECLTYPE, D_CXXONLY }, |
| { "__direct_bases", RID_DIRECT_BASES, 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_unique_object_representations", RID_HAS_UNIQUE_OBJ_REPRESENTATIONS, |
| D_CXXONLY }, |
| { "__has_virtual_destructor", RID_HAS_VIRTUAL_DESTRUCTOR, D_CXXONLY }, |
| { "__imag", RID_IMAGPART, 0 }, |
| { "__imag__", RID_IMAGPART, 0 }, |
| { "__inline", RID_INLINE, 0 }, |
| { "__inline__", RID_INLINE, 0 }, |
| { "__is_abstract", RID_IS_ABSTRACT, D_CXXONLY }, |
| { "__is_aggregate", RID_IS_AGGREGATE, D_CXXONLY }, |
| { "__is_base_of", RID_IS_BASE_OF, D_CXXONLY }, |
| { "__is_class", RID_IS_CLASS, D_CXXONLY }, |
| { "__is_empty", RID_IS_EMPTY, D_CXXONLY }, |
| { "__is_enum", RID_IS_ENUM, D_CXXONLY }, |
| { "__is_final", RID_IS_FINAL, D_CXXONLY }, |
| { "__is_layout_compatible", RID_IS_LAYOUT_COMPATIBLE, D_CXXONLY }, |
| { "__is_literal_type", RID_IS_LITERAL_TYPE, D_CXXONLY }, |
| { "__is_pointer_interconvertible_base_of", |
| RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF, D_CXXONLY }, |
| { "__is_pod", RID_IS_POD, D_CXXONLY }, |
| { "__is_polymorphic", RID_IS_POLYMORPHIC, D_CXXONLY }, |
| { "__is_same", RID_IS_SAME_AS, D_CXXONLY }, |
| { "__is_same_as", RID_IS_SAME_AS, D_CXXONLY }, |
| { "__is_standard_layout", RID_IS_STD_LAYOUT, D_CXXONLY }, |
| { "__is_trivial", RID_IS_TRIVIAL, D_CXXONLY }, |
| { "__is_trivially_assignable", RID_IS_TRIVIALLY_ASSIGNABLE, D_CXXONLY }, |
| { "__is_trivially_constructible", RID_IS_TRIVIALLY_CONSTRUCTIBLE, D_CXXONLY }, |
| { "__is_trivially_copyable", RID_IS_TRIVIALLY_COPYABLE, D_CXXONLY }, |
| { "__is_union", RID_IS_UNION, D_CXXONLY }, |
| { "__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 }, |
| { "__transaction_atomic", RID_TRANSACTION_ATOMIC, 0 }, |
| { "__transaction_relaxed", RID_TRANSACTION_RELAXED, 0 }, |
| { "__transaction_cancel", RID_TRANSACTION_CANCEL, 0 }, |
| { "__typeof", RID_TYPEOF, 0 }, |
| { "__typeof__", RID_TYPEOF, 0 }, |
| { "__underlying_type", RID_UNDERLYING_TYPE, D_CXXONLY }, |
| { "__volatile", RID_VOLATILE, 0 }, |
| { "__volatile__", RID_VOLATILE, 0 }, |
| { "__GIMPLE", RID_GIMPLE, D_CONLY }, |
| { "__PHI", RID_PHI, D_CONLY }, |
| { "__RTL", RID_RTL, D_CONLY }, |
| { "alignas", RID_ALIGNAS, D_CXXONLY | D_CXX11 | D_CXXWARN }, |
| { "alignof", RID_ALIGNOF, D_CXXONLY | D_CXX11 | D_CXXWARN }, |
| { "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 }, |
| { "char8_t", RID_CHAR8, D_CXX_CHAR8_T_FLAGS | D_CXXWARN }, |
| { "char16_t", RID_CHAR16, D_CXXONLY | D_CXX11 | D_CXXWARN }, |
| { "char32_t", RID_CHAR32, D_CXXONLY | D_CXX11 | D_CXXWARN }, |
| { "class", RID_CLASS, D_CXX_OBJC | D_CXXWARN }, |
| { "const", RID_CONST, 0 }, |
| { "consteval", RID_CONSTEVAL, D_CXXONLY | D_CXX20 | D_CXXWARN }, |
| { "constexpr", RID_CONSTEXPR, D_CXXONLY | D_CXX11 | D_CXXWARN }, |
| { "constinit", RID_CONSTINIT, D_CXXONLY | D_CXX20 | D_CXXWARN }, |
| { "const_cast", RID_CONSTCAST, D_CXXONLY | D_CXXWARN }, |
| { "continue", RID_CONTINUE, 0 }, |
| { "decltype", RID_DECLTYPE, D_CXXONLY | D_CXX11 | 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 }, |
| { "noexcept", RID_NOEXCEPT, D_CXXONLY | D_CXX11 | D_CXXWARN }, |
| { "nullptr", RID_NULLPTR, D_CXXONLY | D_CXX11 | 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_CXX11 | 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 }, |
| { "thread_local", RID_THREAD, D_CXXONLY | D_CXX11 | 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 }, |
| { "__is_assignable", RID_IS_ASSIGNABLE, D_CXXONLY }, |
| { "__is_constructible", RID_IS_CONSTRUCTIBLE, D_CXXONLY }, |
| { "__is_nothrow_assignable", RID_IS_NOTHROW_ASSIGNABLE, D_CXXONLY }, |
| { "__is_nothrow_constructible", RID_IS_NOTHROW_CONSTRUCTIBLE, D_CXXONLY }, |
| |
| /* C++ transactional memory. */ |
| { "synchronized", RID_SYNCHRONIZED, D_CXX_OBJC | D_TRANSMEM }, |
| { "atomic_noexcept", RID_ATOMIC_NOEXCEPT, D_CXXONLY | D_TRANSMEM }, |
| { "atomic_cancel", RID_ATOMIC_CANCEL, D_CXXONLY | D_TRANSMEM }, |
| { "atomic_commit", RID_TRANSACTION_ATOMIC, D_CXXONLY | D_TRANSMEM }, |
| |
| /* Concepts-related keywords */ |
| { "concept", RID_CONCEPT, D_CXX_CONCEPTS_FLAGS | D_CXXWARN }, |
| { "requires", RID_REQUIRES, D_CXX_CONCEPTS_FLAGS | D_CXXWARN }, |
| |
| /* Modules-related keywords, these are internal unspellable tokens, |
| created by the preprocessor. */ |
| { "module ", RID__MODULE, D_CXX_MODULES_FLAGS | D_CXXWARN }, |
| { "import ", RID__IMPORT, D_CXX_MODULES_FLAGS | D_CXXWARN }, |
| { "export ", RID__EXPORT, D_CXX_MODULES_FLAGS | D_CXXWARN }, |
| |
| /* Coroutines-related keywords */ |
| { "co_await", RID_CO_AWAIT, D_CXX_COROUTINES_FLAGS | D_CXXWARN }, |
| { "co_yield", RID_CO_YIELD, D_CXX_COROUTINES_FLAGS | D_CXXWARN }, |
| { "co_return", RID_CO_RETURN, D_CXX_COROUTINES_FLAGS | D_CXXWARN }, |
| |
| /* 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 }, |
| { "optional", RID_AT_OPTIONAL, D_OBJC }, |
| { "required", RID_AT_REQUIRED, D_OBJC }, |
| { "property", RID_AT_PROPERTY, D_OBJC }, |
| { "package", RID_AT_PACKAGE, D_OBJC }, |
| { "synthesize", RID_AT_SYNTHESIZE, D_OBJC }, |
| { "dynamic", RID_AT_DYNAMIC, 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 }, |
| /* These are recognized inside a property attribute list */ |
| { "assign", RID_ASSIGN, D_OBJC }, |
| { "atomic", RID_PROPATOMIC, D_OBJC }, |
| { "copy", RID_COPY, D_OBJC }, |
| { "getter", RID_GETTER, D_OBJC }, |
| { "nonatomic", RID_NONATOMIC, D_OBJC }, |
| { "readonly", RID_READONLY, D_OBJC }, |
| { "readwrite", RID_READWRITE, D_OBJC }, |
| { "retain", RID_RETAIN, D_OBJC }, |
| { "setter", RID_SETTER, D_OBJC }, |
| /* These are Objective C implementation of nullability, accepted only in |
| specific contexts. */ |
| { "null_unspecified", RID_NULL_UNSPECIFIED, D_OBJC }, |
| { "nullable", RID_NULLABLE, D_OBJC }, |
| { "nonnull", RID_NONNULL, D_OBJC }, |
| { "null_resettable", RID_NULL_RESETTABLE, D_OBJC }, |
| }; |
| |
| const unsigned int num_c_common_reswords = |
| sizeof c_common_reswords / sizeof (struct c_common_resword); |
| |
| /* Return identifier for address space AS. */ |
| |
| const char * |
| c_addr_space_name (addr_space_t as) |
| { |
| int rid = RID_FIRST_ADDR_SPACE + as; |
| gcc_assert (ridpointers [rid]); |
| return IDENTIFIER_POINTER (ridpointers [rid]); |
| } |
| |
| /* 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 (integer_type_node, 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) (loc, 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, charsz; |
| 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)) |
| { |
| charsz = 1; |
| e_type = char_type_node; |
| } |
| else if (flag_char8_t && TREE_TYPE (value) == char8_array_type_node) |
| { |
| charsz = TYPE_PRECISION (char8_type_node) / BITS_PER_UNIT; |
| e_type = char8_type_node; |
| } |
| else if (TREE_TYPE (value) == char16_array_type_node) |
| { |
| charsz = TYPE_PRECISION (char16_type_node) / BITS_PER_UNIT; |
| e_type = char16_type_node; |
| } |
| else if (TREE_TYPE (value) == char32_array_type_node) |
| { |
| charsz = TYPE_PRECISION (char32_type_node) / BITS_PER_UNIT; |
| e_type = char32_type_node; |
| } |
| else |
| { |
| charsz = TYPE_PRECISION (wchar_type_node) / BITS_PER_UNIT; |
| e_type = wchar_type_node; |
| } |
| |
| /* This matters only for targets where ssizetype has smaller precision |
| than 32 bits. */ |
| if (wi::lts_p (wi::to_wide (TYPE_MAX_VALUE (ssizetype)), length)) |
| { |
| error ("size of string literal is too large"); |
| length = tree_to_shwi (TYPE_MAX_VALUE (ssizetype)) / charsz * charsz; |
| char *str = CONST_CAST (char *, TREE_STRING_POINTER (value)); |
| memset (str + length, '\0', |
| MIN (TREE_STRING_LENGTH (value) - length, charsz)); |
| TREE_STRING_LENGTH (value) = length; |
| } |
| nchars = length / charsz; |
| |
| /* 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 (size_int (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; |
| } |
| |
| /* Given a string of type STRING_TYPE, determine what kind of string |
| token would give an equivalent execution encoding: CPP_STRING, |
| CPP_STRING16, or CPP_STRING32. Return CPP_OTHER in case of error. |
| This may not be exactly the string token type that initially created |
| the string, since CPP_WSTRING is indistinguishable from the 16/32 bit |
| string type, and CPP_UTF8STRING is indistinguishable from CPP_STRING |
| at this point. |
| |
| This effectively reverses part of the logic in lex_string and |
| fix_string_type. */ |
| |
| static enum cpp_ttype |
| get_cpp_ttype_from_string_type (tree string_type) |
| { |
| gcc_assert (string_type); |
| if (TREE_CODE (string_type) == POINTER_TYPE) |
| string_type = TREE_TYPE (string_type); |
| |
| if (TREE_CODE (string_type) != ARRAY_TYPE) |
| return CPP_OTHER; |
| |
| tree element_type = TREE_TYPE (string_type); |
| if (TREE_CODE (element_type) != INTEGER_TYPE) |
| return CPP_OTHER; |
| |
| int bits_per_character = TYPE_PRECISION (element_type); |
| switch (bits_per_character) |
| { |
| case 8: |
| return CPP_STRING; /* It could have also been CPP_UTF8STRING. */ |
| case 16: |
| return CPP_STRING16; |
| case 32: |
| return CPP_STRING32; |
| } |
| |
| return CPP_OTHER; |
| } |
| |
| /* The global record of string concatentations, for use in |
| extracting locations within string literals. */ |
| |
| GTY(()) string_concat_db *g_string_concat_db; |
| |
| /* Implementation of LANG_HOOKS_GET_SUBSTRING_LOCATION. */ |
| |
| const char * |
| c_get_substring_location (const substring_loc &substr_loc, |
| location_t *out_loc) |
| { |
| enum cpp_ttype tok_type |
| = get_cpp_ttype_from_string_type (substr_loc.get_string_type ()); |
| if (tok_type == CPP_OTHER) |
| return "unrecognized string type"; |
| |
| return get_location_within_string (parse_in, g_string_concat_db, |
| substr_loc.get_fmt_string_loc (), |
| tok_type, |
| substr_loc.get_caret_idx (), |
| substr_loc.get_start_idx (), |
| substr_loc.get_end_idx (), |
| out_loc); |
| } |
| |
| |
| /* Return true iff T is a boolean promoted to int. */ |
| |
| bool |
| bool_promoted_to_int_p (tree t) |
| { |
| return (CONVERT_EXPR_P (t) |
| && TREE_TYPE (t) == integer_type_node |
| && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == BOOLEAN_TYPE); |
| } |
| |
| /* vector_targets_convertible_p is used for vector pointer types. The |
| callers perform various checks that the qualifiers are satisfactory, |
| while OTOH vector_targets_convertible_p ignores the number of elements |
| in the vectors. That's fine with vector pointers as we can consider, |
| say, a vector of 8 elements as two consecutive vectors of 4 elements, |
| and that does not require and conversion of the pointer values. |
| In contrast, vector_types_convertible_p and |
| vector_types_compatible_elements_p are used for vector value types. */ |
| /* 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 (VECTOR_TYPE_P (t1) && VECTOR_TYPE_P (t2) |
| && (TYPE_VECTOR_OPAQUE (t1) || TYPE_VECTOR_OPAQUE (t2)) |
| && tree_int_cst_equal (TYPE_SIZE (t1), TYPE_SIZE (t2))) |
| return true; |
| |
| return false; |
| } |
| |
| /* vector_types_convertible_p is used for vector value types. |
| It could in principle call vector_targets_convertible_p as a subroutine, |
| but then the check for vector type would be duplicated with its callers, |
| and also the purpose of vector_targets_convertible_p would become |
| muddled. |
| Where vector_types_convertible_p returns true, a conversion might still be |
| needed to make the types match. |
| In contrast, vector_targets_convertible_p is used for vector pointer |
| values, and vector_types_compatible_elements_p is used specifically |
| in the context for binary operators, as a check if use is possible without |
| conversion. */ |
| /* 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 ((TYPE_VECTOR_OPAQUE (t1) || TYPE_VECTOR_OPAQUE (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 |
| || known_eq (TYPE_VECTOR_SUBPARTS (t1), |
| TYPE_VECTOR_SUBPARTS (t2))) |
| && (INTEGRAL_TYPE_P (TREE_TYPE (t1)) |
| == INTEGRAL_TYPE_P (TREE_TYPE (t2)))); |
| |
| if (!convertible_lax || flag_lax_vector_conversions) |
| return convertible_lax; |
| |
| if (known_eq (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; |
| } |
| |
| /* Build a VEC_PERM_EXPR if V0, V1 and MASK are not error_mark_nodes |
| and have vector types, V0 has the same type as V1, and the number of |
| elements of V0, V1, MASK is the same. |
| |
| In case V1 is a NULL_TREE it is assumed that __builtin_shuffle was |
| called with two arguments. In this case implementation passes the |
| first argument twice in order to share the same tree code. This fact |
| could enable the mask-values being twice the vector length. This is |
| an implementation accident and this semantics is not guaranteed to |
| the user. */ |
| tree |
| c_build_vec_perm_expr (location_t loc, tree v0, tree v1, tree mask, |
| bool complain) |
| { |
| tree ret; |
| bool wrap = true; |
| bool maybe_const = false; |
| bool two_arguments = false; |
| |
| if (v1 == NULL_TREE) |
| { |
| two_arguments = true; |
| v1 = v0; |
| } |
| |
| if (v0 == error_mark_node || v1 == error_mark_node |
| || mask == error_mark_node) |
| return error_mark_node; |
| |
| if (!gnu_vector_type_p (TREE_TYPE (mask)) |
| || !VECTOR_INTEGER_TYPE_P (TREE_TYPE (mask))) |
| { |
| if (complain) |
| error_at (loc, "%<__builtin_shuffle%> last argument must " |
| "be an integer vector"); |
| return error_mark_node; |
| } |
| |
| if (!gnu_vector_type_p (TREE_TYPE (v0)) |
| || !gnu_vector_type_p (TREE_TYPE (v1))) |
| { |
| if (complain) |
| error_at (loc, "%<__builtin_shuffle%> arguments must be vectors"); |
| return error_mark_node; |
| } |
| |
| if (TYPE_MAIN_VARIANT (TREE_TYPE (v0)) != TYPE_MAIN_VARIANT (TREE_TYPE (v1))) |
| { |
| if (complain) |
| error_at (loc, "%<__builtin_shuffle%> argument vectors must be of " |
| "the same type"); |
| return error_mark_node; |
| } |
| |
| if (maybe_ne (TYPE_VECTOR_SUBPARTS (TREE_TYPE (v0)), |
| TYPE_VECTOR_SUBPARTS (TREE_TYPE (mask))) |
| && maybe_ne (TYPE_VECTOR_SUBPARTS (TREE_TYPE (v1)), |
| TYPE_VECTOR_SUBPARTS (TREE_TYPE (mask)))) |
| { |
| if (complain) |
| error_at (loc, "%<__builtin_shuffle%> number of elements of the " |
| "argument vector(s) and the mask vector should " |
| "be the same"); |
| return error_mark_node; |
| } |
| |
| if (GET_MODE_BITSIZE (SCALAR_TYPE_MODE (TREE_TYPE (TREE_TYPE (v0)))) |
| != GET_MODE_BITSIZE (SCALAR_TYPE_MODE (TREE_TYPE (TREE_TYPE (mask))))) |
| { |
| if (complain) |
| error_at (loc, "%<__builtin_shuffle%> argument vector(s) inner type " |
| "must have the same size as inner type of the mask"); |
| return error_mark_node; |
| } |
| |
| if (!c_dialect_cxx ()) |
| { |
| /* Avoid C_MAYBE_CONST_EXPRs inside VEC_PERM_EXPR. */ |
| v0 = c_fully_fold (v0, false, &maybe_const); |
| wrap &= maybe_const; |
| |
| if (two_arguments) |
| v1 = v0 = save_expr (v0); |
| else |
| { |
| v1 = c_fully_fold (v1, false, &maybe_const); |
| wrap &= maybe_const; |
| } |
| |
| mask = c_fully_fold (mask, false, &maybe_const); |
| wrap &= maybe_const; |
| } |
| else if (two_arguments) |
| v1 = v0 = save_expr (v0); |
| |
| ret = build3_loc (loc, VEC_PERM_EXPR, TREE_TYPE (v0), v0, v1, mask); |
| |
| if (!c_dialect_cxx () && !wrap) |
| ret = c_wrap_maybe_const (ret, true); |
| |
| return ret; |
| } |
| |
| /* Build a VEC_PERM_EXPR if V0, V1 are not error_mark_nodes |
| and have vector types, V0 has the same element type as V1, and the |
| number of elements the result is that of MASK. */ |
| tree |
| c_build_shufflevector (location_t loc, tree v0, tree v1, |
| const vec<tree> &mask, bool complain) |
| { |
| tree ret; |
| bool wrap = true; |
| bool maybe_const = false; |
| |
| if (v0 == error_mark_node || v1 == error_mark_node) |
| return error_mark_node; |
| |
| if (!gnu_vector_type_p (TREE_TYPE (v0)) |
| || !gnu_vector_type_p (TREE_TYPE (v1))) |
| { |
| if (complain) |
| error_at (loc, "%<__builtin_shufflevector%> arguments must be vectors"); |
| return error_mark_node; |
| } |
| |
| /* ??? In principle one could select a constant part of a variable size |
| vector but things get a bit awkward with trying to support this here. */ |
| unsigned HOST_WIDE_INT v0n, v1n; |
| if (!TYPE_VECTOR_SUBPARTS (TREE_TYPE (v0)).is_constant (&v0n) |
| || !TYPE_VECTOR_SUBPARTS (TREE_TYPE (v1)).is_constant (&v1n)) |
| { |
| if (complain) |
| error_at (loc, "%<__builtin_shufflevector%> arguments must be constant" |
| " size vectors"); |
| return error_mark_node; |
| } |
| |
| if (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (v0))) |
| != TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (v1)))) |
| { |
| if (complain) |
| error_at (loc, "%<__builtin_shufflevector%> argument vectors must " |
| "have the same element type"); |
| return error_mark_node; |
| } |
| |
| if (!pow2p_hwi (mask.length ())) |
| { |
| if (complain) |
| error_at (loc, "%<__builtin_shufflevector%> must specify a result " |
| "with a power of two number of elements"); |
| return error_mark_node; |
| } |
| |
| if (!c_dialect_cxx ()) |
| { |
| /* Avoid C_MAYBE_CONST_EXPRs inside VEC_PERM_EXPR. */ |
| v0 = c_fully_fold (v0, false, &maybe_const); |
| wrap &= maybe_const; |
| |
| v1 = c_fully_fold (v1, false, &maybe_const); |
| wrap &= maybe_const; |
| } |
| |
| unsigned HOST_WIDE_INT maskl = MAX (mask.length (), MAX (v0n, v1n)); |
| unsigned HOST_WIDE_INT pad = (v0n < maskl ? maskl - v0n : 0); |
| vec_perm_builder sel (maskl, maskl, 1); |
| unsigned i; |
| for (i = 0; i < mask.length (); ++i) |
| { |
| tree idx = mask[i]; |
| if (!tree_fits_shwi_p (idx)) |
| { |
| if (complain) |
| error_at (loc, "invalid element index %qE to " |
| "%<__builtin_shufflevector%>", idx); |
| return error_mark_node; |
| } |
| HOST_WIDE_INT iidx = tree_to_shwi (idx); |
| if (iidx < -1 |
| || (iidx != -1 |
| && (unsigned HOST_WIDE_INT) iidx >= v0n + v1n)) |
| { |
| if (complain) |
| error_at (loc, "invalid element index %qE to " |
| "%<__builtin_shufflevector%>", idx); |
| return error_mark_node; |
| } |
| /* ??? Our VEC_PERM_EXPR does not allow for -1 yet. */ |
| if (iidx == -1) |
| iidx = i; |
| /* ??? Our VEC_PERM_EXPR does not allow different sized inputs, |
| so pad out a smaller v0. */ |
| else if ((unsigned HOST_WIDE_INT) iidx >= v0n) |
| iidx += pad; |
| sel.quick_push (iidx); |
| } |
| /* ??? VEC_PERM_EXPR does not support a result that is smaller than |
| the inputs, so we have to pad id out. */ |
| for (; i < maskl; ++i) |
| sel.quick_push (i); |
| |
| vec_perm_indices indices (sel, 2, maskl); |
| |
| tree ret_type = build_vector_type (TREE_TYPE (TREE_TYPE (v0)), maskl); |
| tree mask_type = build_vector_type (build_nonstandard_integer_type |
| (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (ret_type))), 1), |
| maskl); |
| /* Pad out arguments to the common vector size. */ |
| if (v0n < maskl) |
| { |
| constructor_elt elt = { NULL_TREE, build_zero_cst (TREE_TYPE (v0)) }; |
| v0 = build_constructor_single (ret_type, NULL_TREE, v0); |
| for (i = 1; i < maskl / v0n; ++i) |
| vec_safe_push (CONSTRUCTOR_ELTS (v0), elt); |
| } |
| if (v1n < maskl) |
| { |
| constructor_elt elt = { NULL_TREE, build_zero_cst (TREE_TYPE (v1)) }; |
| v1 = build_constructor_single (ret_type, NULL_TREE, v1); |
| for (i = 1; i < maskl / v1n; ++i) |
| vec_safe_push (CONSTRUCTOR_ELTS (v1), elt); |
| } |
| ret = build3_loc (loc, VEC_PERM_EXPR, ret_type, v0, v1, |
| vec_perm_indices_to_tree (mask_type, indices)); |
| /* Get the lowpart we are interested in. */ |
| if (mask.length () < maskl) |
| { |
| tree lpartt = build_vector_type (TREE_TYPE (ret_type), mask.length ()); |
| ret = build3_loc (loc, BIT_FIELD_REF, |
| lpartt, ret, TYPE_SIZE (lpartt), bitsize_zero_node); |
| /* Wrap the lowpart operation in a TARGET_EXPR so it gets a separate |
| temporary during gimplification. See PR101530 for cases where |
| we'd otherwise end up with non-toplevel BIT_FIELD_REFs. */ |
| tree tem = create_tmp_var_raw (lpartt); |
| DECL_CONTEXT (tem) = current_function_decl; |
| ret = build4 (TARGET_EXPR, lpartt, tem, ret, NULL_TREE, NULL_TREE); |
| TREE_SIDE_EFFECTS (ret) = 1; |
| } |
| |
| if (!c_dialect_cxx () && !wrap) |
| ret = c_wrap_maybe_const (ret, true); |
| |
| return ret; |
| } |
| |
| /* Build a VEC_CONVERT ifn for __builtin_convertvector builtin. */ |
| |
| tree |
| c_build_vec_convert (location_t loc1, tree expr, location_t loc2, tree type, |
| bool complain) |
| { |
| if (error_operand_p (type)) |
| return error_mark_node; |
| if (error_operand_p (expr)) |
| return error_mark_node; |
| |
| if (!gnu_vector_type_p (TREE_TYPE (expr)) |
| || (!VECTOR_INTEGER_TYPE_P (TREE_TYPE (expr)) |
| && !VECTOR_FLOAT_TYPE_P (TREE_TYPE (expr)))) |
| { |
| if (complain) |
| error_at (loc1, "%<__builtin_convertvector%> first argument must " |
| "be an integer or floating vector"); |
| return error_mark_node; |
| } |
| |
| if (!gnu_vector_type_p (type) |
| || (!VECTOR_INTEGER_TYPE_P (type) && !VECTOR_FLOAT_TYPE_P (type))) |
| { |
| if (complain) |
| error_at (loc2, "%<__builtin_convertvector%> second argument must " |
| "be an integer or floating vector type"); |
| return error_mark_node; |
| } |
| |
| if (maybe_ne (TYPE_VECTOR_SUBPARTS (TREE_TYPE (expr)), |
| TYPE_VECTOR_SUBPARTS (type))) |
| { |
| if (complain) |
| error_at (loc1, "%<__builtin_convertvector%> number of elements " |
| "of the first argument vector and the second argument " |
| "vector type should be the same"); |
| return error_mark_node; |
| } |
| |
| if ((TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (expr))) |
| == TYPE_MAIN_VARIANT (TREE_TYPE (type))) |
| || (VECTOR_INTEGER_TYPE_P (TREE_TYPE (expr)) |
| && VECTOR_INTEGER_TYPE_P (type) |
| && (TYPE_PRECISION (TREE_TYPE (TREE_TYPE (expr))) |
| == TYPE_PRECISION (TREE_TYPE (type))))) |
| return build1_loc (loc1, VIEW_CONVERT_EXPR, type, expr); |
| |
| bool wrap = true; |
| bool maybe_const = false; |
| tree ret; |
| if (!c_dialect_cxx ()) |
| { |
| /* Avoid C_MAYBE_CONST_EXPRs inside of VEC_CONVERT argument. */ |
| expr = c_fully_fold (expr, false, &maybe_const); |
| wrap &= maybe_const; |
| } |
| |
| ret = build_call_expr_internal_loc (loc1, IFN_VEC_CONVERT, type, 1, expr); |
| |
| if (!wrap) |
| ret = c_wrap_maybe_const (ret, true); |
| |
| return ret; |
| } |
| |
| /* Like tree.cc:get_narrower, but retain conversion from C++0x scoped enum |
| to integral type. */ |
| |
| tree |
| c_common_get_narrower (tree op, int *unsignedp_ptr) |
| { |
| op = get_narrower (op, unsignedp_ptr); |
| |
| if (TREE_CODE (TREE_TYPE (op)) == ENUMERAL_TYPE |
| && ENUM_IS_SCOPED (TREE_TYPE (op))) |
| { |
| /* C++0x scoped enumerations don't implicitly convert to integral |
| type; if we stripped an explicit conversion to a larger type we |
| need to replace it so common_type will still work. */ |
| tree type = c_common_type_for_size (TYPE_PRECISION (TREE_TYPE (op)), |
| TYPE_UNSIGNED (TREE_TYPE (op))); |
| op = fold_convert (type, op); |
| } |
| return op; |
| } |
| |
| /* 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 = c_common_get_narrower (op0, &unsigned0); |
| arg1 = c_common_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; |
| } |
| |
| /* Returns true iff any integer value of type FROM_TYPE can be represented as |
| real of type TO_TYPE. This is a helper function for unsafe_conversion_p. */ |
| |
| static bool |
| int_safely_convertible_to_real_p (const_tree from_type, const_tree to_type) |
| { |
| tree type_low_bound = TYPE_MIN_VALUE (from_type); |
| tree type_high_bound = TYPE_MAX_VALUE (from_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); |
| |
| return exact_real_truncate (TYPE_MODE (to_type), &real_low_bound) |
| && exact_real_truncate (TYPE_MODE (to_type), &real_high_bound); |
| } |
| |
| /* Checks if expression EXPR of complex/real/integer type cannot be converted |
| to the complex/real/integer type TYPE. Function returns non-zero when: |
| * EXPR is a constant which cannot be exactly converted to TYPE. |
| * EXPR is not a constant and size of EXPR's type > than size of TYPE, |
| for EXPR type and TYPE being both integers or both real, or both |
| complex. |
| * EXPR is not a constant of complex type and TYPE is a real or |
| an integer. |
| * EXPR is not a constant of real type and TYPE is an integer. |
| * EXPR is not a constant of integer type which cannot be |
| exactly converted to real type. |
| |
| Function allows conversions between types of different signedness if |
| CHECK_SIGN is false and can return SAFE_CONVERSION (zero) in that |
| case. Function can return UNSAFE_SIGN if CHECK_SIGN is true. |
| |
| RESULT, when non-null is the result of the conversion. When constant |
| it is included in the text of diagnostics. |
| |
| Function allows conversions from complex constants to non-complex types, |
| provided that imaginary part is zero and real part can be safely converted |
| to TYPE. */ |
| |
| enum conversion_safety |
| unsafe_conversion_p (tree type, tree expr, tree result, bool check_sign) |
| { |
| enum conversion_safety give_warning = SAFE_CONVERSION; /* is 0 or false */ |
| tree expr_type = TREE_TYPE (expr); |
| |
| expr = fold_for_warn (expr); |
| |
| if (TREE_CODE (expr) == REAL_CST || TREE_CODE (expr) == INTEGER_CST) |
| { |
| /* If type is complex, we are interested in compatibility with |
| underlying type. */ |
| if (TREE_CODE (type) == COMPLEX_TYPE) |
| type = TREE_TYPE (type); |
| |
| /* 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 = UNSAFE_REAL; |
| } |
| /* 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) |
| { |
| if (check_sign) |
| give_warning = UNSAFE_SIGN; |
| } |
| else if (!TYPE_UNSIGNED (type) && TYPE_UNSIGNED (expr_type)) |
| { |
| if (check_sign) |
| give_warning = UNSAFE_SIGN; |
| } |
| else |
| give_warning = UNSAFE_OTHER; |
| } |
| 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 = UNSAFE_REAL; |
| } |
| /* 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 = UNSAFE_REAL; |
| } |
| } |
| } |
| |
| else if (TREE_CODE (expr) == COMPLEX_CST) |
| { |
| tree imag_part = TREE_IMAGPART (expr); |
| /* Conversion from complex constant with zero imaginary part, |
| perform check for conversion of real part. */ |
| if ((TREE_CODE (imag_part) == REAL_CST |
| && real_zerop (imag_part)) |
| || (TREE_CODE (imag_part) == INTEGER_CST |
| && integer_zerop (imag_part))) |
| /* Note: in this branch we use recursive call to unsafe_conversion_p |
| with different type of EXPR, but it is still safe, because when EXPR |
| is a constant, it's type is not used in text of generated warnings |
| (otherwise they could sound misleading). */ |
| return unsafe_conversion_p (type, TREE_REALPART (expr), result, |
| check_sign); |
| /* Conversion from complex constant with non-zero imaginary part. */ |
| else |
| { |
| /* Conversion to complex type. |
| Perform checks for both real and imaginary parts. */ |
| if (TREE_CODE (type) == COMPLEX_TYPE) |
| { |
| enum conversion_safety re_safety = |
| unsafe_conversion_p (type, TREE_REALPART (expr), |
| result, check_sign); |
| enum conversion_safety im_safety = |
| unsafe_conversion_p (type, imag_part, result, check_sign); |
| |
| /* Merge the results into appropriate single warning. */ |
| |
| /* Note: this case includes SAFE_CONVERSION, i.e. success. */ |
| if (re_safety == im_safety) |
| give_warning = re_safety; |
| else if (!re_safety && im_safety) |
| give_warning = im_safety; |
| else if (re_safety && !im_safety) |
| give_warning = re_safety; |
| else |
| give_warning = UNSAFE_OTHER; |
| } |
| /* Warn about conversion from complex to real or integer type. */ |
| else |
| give_warning = UNSAFE_IMAGINARY; |
| } |
| } |
| |
| /* Checks for remaining case: EXPR is not constant. */ |
| else |
| { |
| /* Warn for real types converted to integer types. */ |
| if (TREE_CODE (expr_type) == REAL_TYPE |
| && TREE_CODE (type) == INTEGER_TYPE) |
| give_warning = UNSAFE_REAL; |
| |
| 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 SAFE_CONVERSION; |
| /* 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 SAFE_CONVERSION; |
| } |
| } |
| /* Warn for integer types converted to smaller integer types. */ |
| if (TYPE_PRECISION (type) < TYPE_PRECISION (expr_type)) |
| give_warning = UNSAFE_OTHER; |
| |
| /* 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))) |
| && check_sign) |
| give_warning = UNSAFE_SIGN; |
| } |
| |
| /* 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) |
| { |
| /* Don't warn about char y = 0xff; float x = (int) y; */ |
| expr = get_unwidened (expr, 0); |
| expr_type = TREE_TYPE (expr); |
| |
| if (!int_safely_convertible_to_real_p (expr_type, type)) |
| give_warning = UNSAFE_OTHER; |
| } |
| |
| /* 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 = UNSAFE_REAL; |
| |
| /* Check conversion between two complex types. */ |
| else if (TREE_CODE (expr_type) == COMPLEX_TYPE |
| && TREE_CODE (type) == COMPLEX_TYPE) |
| { |
| /* Extract underlying types (i.e., type of real and imaginary |
| parts) of expr_type and type. */ |
| tree from_type = TREE_TYPE (expr_type); |
| tree to_type = TREE_TYPE (type); |
| |
| /* Warn for real types converted to integer types. */ |
| if (TREE_CODE (from_type) == REAL_TYPE |
| && TREE_CODE (to_type) == INTEGER_TYPE) |
| give_warning = UNSAFE_REAL; |
| |
| /* Warn for real types converted to smaller real types. */ |
| else if (TREE_CODE (from_type) == REAL_TYPE |
| && TREE_CODE (to_type) == REAL_TYPE |
| && TYPE_PRECISION (to_type) < TYPE_PRECISION (from_type)) |
| give_warning = UNSAFE_REAL; |
| |
| /* Check conversion for complex integer types. Here implementation |
| is simpler than for real-domain integers because it does not |
| involve sophisticated cases, such as bitmasks, casts, etc. */ |
| else if (TREE_CODE (from_type) == INTEGER_TYPE |
| && TREE_CODE (to_type) == INTEGER_TYPE) |
| { |
| /* Warn for integer types converted to smaller integer types. */ |
| if (TYPE_PRECISION (to_type) < TYPE_PRECISION (from_type)) |
| give_warning = UNSAFE_OTHER; |
| |
| /* Check for different signedness, see case for real-domain |
| integers (above) for a more detailed comment. */ |
| else if (((TYPE_PRECISION (to_type) == TYPE_PRECISION (from_type) |
| && TYPE_UNSIGNED (to_type) != TYPE_UNSIGNED (from_type)) |
| || (TYPE_UNSIGNED (to_type) && !TYPE_UNSIGNED (from_type))) |
| && check_sign) |
| give_warning = UNSAFE_SIGN; |
| } |
| else if (TREE_CODE (from_type) == INTEGER_TYPE |
| && TREE_CODE (to_type) == REAL_TYPE |
| && !int_safely_convertible_to_real_p (from_type, to_type)) |
| give_warning = UNSAFE_OTHER; |
| } |
| |
| /* Warn for complex types converted to real or integer types. */ |
| else if (TREE_CODE (expr_type) == COMPLEX_TYPE |
| && TREE_CODE (type) != COMPLEX_TYPE) |
| give_warning = UNSAFE_IMAGINARY; |
| } |
| |
| return give_warning; |
| } |
| |
| |
| /* 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. |
| INIT_CONST is true if the conversion is for arithmetic types for a static |
| initializer and folding must apply accordingly (discarding floating-point |
| exceptions and assuming the default rounding mode is in effect). */ |
| |
| tree |
| convert_and_check (location_t loc, tree type, tree expr, bool init_const) |
| { |
| tree result; |
| tree expr_for_warning; |
| |
| /* Convert from a value with possible excess precision rather than |
| via the semantic type, but do not warn about values not fitting |
| exactly in the semantic type. */ |
| if (TREE_CODE (expr) == EXCESS_PRECISION_EXPR) |
| { |
| tree orig_type = TREE_TYPE (expr); |
| expr = TREE_OPERAND (expr, 0); |
| expr_for_warning = (init_const |
| ? convert_init (orig_type, expr) |
| : convert (orig_type, expr)); |
| if (orig_type == type) |
| return expr_for_warning; |
| } |
| else |
| expr_for_warning = expr; |
| |
| if (TREE_TYPE (expr) == type) |
| return expr; |
| |
| result = init_const ? convert_init (type, expr) : convert (type, expr); |
| |
| if (c_inhibit_evaluation_warnings == 0 |
| && !TREE_OVERFLOW_P (expr) |
| && result != error_mark_node) |
| warnings_for_convert_and_check (loc, type, expr_for_warning, 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 bool warning_candidate_p (tree); |
| static bool candidate_equal_p (const_tree, const_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 || !candidate_equal_p (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 (candidate_equal_p (tmp2->expr, add->expr)) |
| { |
| found = 1; |
| if (!tmp2->writer) |
| tmp2->writer = add->writer; |
| } |
| if (!found) |
| { |
| *end = copy ? new_tlist (NULL, add->expr, add->writer) : add; |
| 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 (candidate_equal_p (tmp->expr, written)) |
| return; |
| |
| while (list) |
| { |
| if (candidate_equal_p (list->expr, written) |
| && !candidate_equal_p (list->writer, writer) |
| && (!only_writes || list->writer)) |
| { |
| warned_ids = new_tlist (warned_ids, written, NULL_TREE); |
| warning_at (EXPR_LOC_OR_LOC (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 bool |
| warning_candidate_p (tree x) |
| { |
| if (DECL_P (x) && DECL_ARTIFICIAL (x)) |
| return false; |
| |
| if (TREE_CODE (x) == BLOCK) |
| return false; |
| |
| /* VOID_TYPE_P (TREE_TYPE (x)) is workaround for cp/tree.cc |
| (lvalue_p) crash on TRY/CATCH. */ |
| if (TREE_TYPE (x) == NULL_TREE || VOID_TYPE_P (TREE_TYPE (x))) |
| return false; |
| |
| if (!lvalue_p (x)) |
| return false; |
| |
| /* No point to track non-const calls, they will never satisfy |
| operand_equal_p. */ |
| if (TREE_CODE (x) == CALL_EXPR && (call_expr_flags (x) & ECF_CONST) == 0) |
| return false; |
| |
| if (TREE_CODE (x) == STRING_CST) |
| return false; |
| |
| return true; |
| } |
| |
| /* Return nonzero if X and Y appear to be the same candidate (or NULL) */ |
| static bool |
| candidate_equal_p (const_tree x, const_tree y) |
| { |
| return (x == y) || (x && y && operand_equal_p (x, y, 0)); |
| } |
| |
| /* 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; |
| |
| restart: |
| /* X may be NULL if it is the operand of an empty statement expression |
| ({ }). */ |
| if (x == NULL) |
| return; |
| |
| code = TREE_CODE (x); |
| cl = TREE_CODE_CLASS (code); |
| |
| if (warning_candidate_p (x)) |
| *pno_sp = new_tlist (*pno_sp, x, writer); |
| |
| switch (code) |
| { |
| case CONSTRUCTOR: |
| case SIZEOF_EXPR: |
| case PAREN_SIZEOF_EXPR: |
| return; |
| |
| case COMPOUND_EXPR: |
| case TRUTH_ANDIF_EXPR: |
| case TRUTH_ORIF_EXPR: |
| sequenced_binary: |
| tmp_before = tmp_nosp = tmp_list2 = 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, &tmp_list2, NULL_TREE); |
| warn_for_collisions (tmp_list2); |
| merge_tlist (pbefore_sp, tmp_list3, 0); |
| merge_tlist (pno_sp, tmp_list2, 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, 0); |
| |
| 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 (candidate_equal_p (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; |
| merge_tlist (&tmp_list3, tmp_nosp, 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; |
| |
| case VIEW_CONVERT_EXPR: |
| if (location_wrapper_p (x)) |
| { |
| x = TREE_OPERAND (x, 0); |
| goto restart; |
| } |
| goto do_default; |
| |
| case LSHIFT_EXPR: |
| case RSHIFT_EXPR: |
| case COMPONENT_REF: |
| case ARRAY_REF: |
| if (cxx_dialect >= cxx17) |
| goto sequenced_binary; |
| goto do_default; |
| |
| default: |
| do_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; |
| } |
| } |
| |
| static constexpr size_t verify_sequence_points_limit = 1024; |
| |
| /* Called from verify_sequence_points via walk_tree. */ |
| |
| static tree |
| verify_tree_lim_r (tree *tp, int *walk_subtrees, void *data) |
| { |
| if (++*((size_t *) data) > verify_sequence_points_limit) |
| return integer_zero_node; |
| |
| if (TYPE_P (*tp)) |
| *walk_subtrees = 0; |
| |
| return NULL_TREE; |
| } |
| |
| /* Try to warn for undefined behavior in EXPR due to missing sequence |
| points. */ |
| |
| void |
| verify_sequence_points (tree expr) |
| { |
| tlist *before_sp = nullptr, *after_sp = nullptr; |
| |
| /* verify_tree is highly recursive, and merge_tlist is O(n^2), |
| so we return early if the expression is too big. */ |
| size_t n = 0; |
| if (walk_tree (&expr, verify_tree_lim_r, &n, nullptr)) |
| return; |
| |
| warned_ids = nullptr; |
| save_expr_cache = nullptr; |
| if (!tlist_firstobj) |
| { |
| gcc_obstack_init (&tlist_obstack); |
| tlist_firstobj = (char *) obstack_alloc (&tlist_obstack, 0); |
| } |
| |
| verify_tree (expr, &before_sp, &after_sp, NULL_TREE); |
| 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 (location_t loc, tree value) |
| { |
| if (value == NULL_TREE) |
| return 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_at (loc, "case label does not reduce to an integer constant"); |
| value = error_mark_node; |
| } |
| |
| constant_expression_warning (value); |
| |
| return value; |
| } |
| |
| /* 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) |
| { |
| int i; |
| |
| 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); |
| |
| for (i = 0; i < NUM_INT_N_ENTS; i ++) |
| if (int_n_enabled_p[i] |
| && bits == int_n_data[i].bitsize) |
| return (unsignedp ? int_n_trees[i].unsigned_type |
| : int_n_trees[i].signed_type); |
| |
| 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 NULL_TREE; |
| } |
| |
| /* 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 mode_class mclass; |
| if (ibit == 0) |
| mclass = unsignedp ? MODE_UFRACT : MODE_FRACT; |
| else |
| mclass = unsignedp ? MODE_UACCUM : MODE_ACCUM; |
| |
| opt_scalar_mode opt_mode; |
| scalar_mode mode; |
| FOR_EACH_MODE_IN_CLASS (opt_mode, mclass) |
| { |
| mode = opt_mode.require (); |
| if (GET_MODE_IBIT (mode) >= ibit && GET_MODE_FBIT (mode) >= fbit) |
| break; |
| } |
| |
| if (!opt_mode.exists (&mode) || !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 NULL_TREE; |
| } |
| |
| return c_common_type_for_mode (mode, satp); |
| } |
| |
| /* Used for communication between c_common_type_for_mode and |
| c_register_builtin_type. */ |
| 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 (machine_mode mode, int unsignedp) |
| { |
| tree t; |
| int i; |
| |
| 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; |
| |
| for (i = 0; i < NUM_INT_N_ENTS; i ++) |
| if (int_n_enabled_p[i] |
| && mode == int_n_data[i].m) |
| return (unsignedp ? int_n_trees[i].unsigned_type |
| : int_n_trees[i].signed_type); |
| |
| 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; |
| |
| for (i = 0; i < NUM_FLOATN_NX_TYPES; i++) |
| if (FLOATN_NX_TYPE_NODE (i) != NULL_TREE |
| && mode == TYPE_MODE (FLOATN_NX_TYPE_NODE (i))) |
| return FLOATN_NX_TYPE_NODE (i); |
| |
| if (mode == TYPE_MODE (void_type_node)) |
| return void_type_node; |
| |
| if (mode == TYPE_MODE (build_pointer_type (char_type_node)) |
| || mode == TYPE_MODE (build_pointer_type (integer_type_node))) |
| { |
| unsigned int precision |
| = GET_MODE_PRECISION (as_a <scalar_int_mode> (mode)); |
| return (unsignedp |
| ? make_unsigned_type (precision) |
| : make_signed_type (precision)); |
| } |
| |
| if (COMPLEX_MODE_P (mode)) |
| { |
| machine_mode inner_mode; |
| tree inner_type; |
| |
| if (mode == TYPE_MODE (complex_float_type_node)) |
| return complex_float_type_node; |
| if (mode == TYPE_MODE (complex_double_type_node)) |
| return complex_double_type_node; |
| if (mode == TYPE_MODE (complex_long_double_type_node)) |
| return complex_long_double_type_node; |
| |
| for (i = 0; i < NUM_FLOATN_NX_TYPES; i++) |
| if (COMPLEX_FLOATN_NX_TYPE_NODE (i) != NULL_TREE |
| && mode == TYPE_MODE (COMPLEX_FLOATN_NX_TYPE_NODE (i))) |
| return COMPLEX_FLOATN_NX_TYPE_NODE (i); |
| |
| 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 (GET_MODE_CLASS (mode) == MODE_VECTOR_BOOL |
| && valid_vector_subparts_p (GET_MODE_NUNITS (mode))) |
| { |
| unsigned int elem_bits = vector_element_size (GET_MODE_BITSIZE (mode), |
| GET_MODE_NUNITS (mode)); |
| tree bool_type = build_nonstandard_boolean_type (elem_bits); |
| return build_vector_type_for_mode (bool_type, mode); |
| } |
| else if (VECTOR_MODE_P (mode) |
| && valid_vector_subparts_p (GET_MODE_NUNITS (mode))) |
| { |
| 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 (dfloat32_type_node != NULL_TREE |
| && mode == TYPE_MODE (dfloat32_type_node)) |
| return dfloat32_type_node; |
| if (dfloat64_type_node != NULL_TREE |
| && mode == TYPE_MODE (dfloat64_type_node)) |
| return dfloat64_type_node; |
| if (dfloat128_type_node != NULL_TREE |
| && 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)) |
| { |
| tree type = TREE_VALUE (t); |
| if (TYPE_MODE (type) == mode |
| && VECTOR_TYPE_P (type) == VECTOR_MODE_P (mode) |
| && !!unsignedp == !!TYPE_UNSIGNED (type)) |
| return type; |
| } |
| return NULL_TREE; |
| } |
| |
| 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; |
| int i; |
| |
| /* 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; |
| |
| for (i = 0; i < NUM_INT_N_ENTS; i ++) |
| if (int_n_enabled_p[i] |
| && (type1 == int_n_trees[i].unsigned_type |
| || type1 == int_n_trees[i].signed_type)) |
| return (unsignedp ? int_n_trees[i].unsigned_type |
| : int_n_trees[i].signed_type); |
| |
| #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); |
| |
| for (i = 0; i < NUM_INT_N_ENTS; i ++) |
| if (int_n_enabled_p[i] |
| && TYPE_MODE (type) == int_n_data[i].m |
| && TYPE_PRECISION (type) == int_n_data[i].bitsize) |
| return (unsignedp ? int_n_trees[i].unsigned_type |
| : int_n_trees[i].signed_type); |
| |
| #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) |
| { |
| int i; |
| |
| /* 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); |
| for (i = 0; i < NUM_INT_N_ENTS; i ++) |
| if (int_n_enabled_p[i] |
| && width == int_n_data[i].bitsize) |
| return (unsignedp ? int_n_trees[i].unsigned_type |
| : int_n_trees[i].signed_type); |
| 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 (UNKNOWN_LOCATION, |
| TYPE_DECL, get_identifier (name), type); |
| DECL_ARTIFICIAL (decl) = 1; |
| if (!TYPE_NAME (type)) |
| TYPE_NAME (type) = decl; |
| lang_hooks.decls.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. |
| RICHLOC is a rich location for the message, containing either |
| three separate locations for each of the operator and operands |
| |
| lhs op rhs |
| ~~~ ^~ ~~~ |
| |
| (C FE), or one location ranging over all over them |
| |
| lhs op rhs |
| ~~~~^~~~~~ |
| |
| (C++ FE). */ |
| |
| void |
| binary_op_error (rich_location *richloc, 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 (richloc, |
| "invalid operands to binary %s (have %qT and %qT)", |
| opname, type0, type1); |
| } |
| |
| /* Given an expression as a tree, return its original type. Do this |
| by stripping any conversion that preserves the sign and precision. */ |
| static tree |
| expr_original_type (tree expr) |
| { |
| STRIP_SIGN_NOPS (expr); |
| return TREE_TYPE (expr); |
| } |
| |
| /* 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. |
| |
| LOC is the location of the comparison. |
| |
| If this function returns non-NULL_TREE, it means that the comparison has |
| a constant value. What this function returns is an expression for |
| that value. */ |
| |
| tree |
| shorten_compare (location_t loc, 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 = c_common_get_narrower (op0, &unsignedp0); |
| primop1 = c_common_get_narrower (op1, &unsignedp1); |
| |
| /* If primopN is first sign-extended from primopN's precision to opN's |
| precision, then zero-extended from opN's precision to |
| *restype_ptr precision, shortenings might be invalid. */ |
| if (TYPE_PRECISION (TREE_TYPE (primop0)) < TYPE_PRECISION (TREE_TYPE (op0)) |
| && TYPE_PRECISION (TREE_TYPE (op0)) < TYPE_PRECISION (*restype_ptr) |
| && !unsignedp0 |
| && TYPE_UNSIGNED (TREE_TYPE (op0))) |
| primop0 = op0; |
| if (TYPE_PRECISION (TREE_TYPE (primop1)) < TYPE_PRECISION (TREE_TYPE (op1)) |
| && TYPE_PRECISION (TREE_TYPE (op1)) < TYPE_PRECISION (*restype_ptr) |
| && !unsignedp1 |
| && TYPE_UNSIGNED (TREE_TYPE (op1))) |
| primop1 = op1; |
| |
| /* 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)) |
| { |
| std::swap (primop0, primop1); |
| std::swap (op0, op1); |
| *op0_ptr = op0; |
| *op1_ptr = op1; |
| std::swap (unsignedp0, unsignedp1); |
| std::swap (real1, real2); |
| |
| 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); |
|