| /* VMS specific, C compiler specific functions. |
| Copyright (C) 2011-2015 Free Software Foundation, Inc. |
| Contributed by Tristan Gingold (gingold@adacore.com). |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GCC is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "cpplib.h" |
| #include "hash-set.h" |
| #include "machmode.h" |
| #include "vec.h" |
| #include "double-int.h" |
| #include "input.h" |
| #include "alias.h" |
| #include "symtab.h" |
| #include "wide-int.h" |
| #include "inchash.h" |
| #include "tree.h" |
| #include "c-family/c-pragma.h" |
| #include "c-family/c-common.h" |
| #include "c/c-tree.h" |
| #include "toplev.h" |
| #include "ggc.h" |
| #include "tm_p.h" |
| #include "incpath.h" |
| #include "diagnostic.h" |
| |
| /* '#pragma __nostandard' is simply ignored. */ |
| |
| static void |
| vms_pragma_nostandard (cpp_reader *pfile ATTRIBUTE_UNUSED) |
| { |
| tree x; |
| |
| if (pragma_lex (&x) != CPP_EOF) |
| warning (OPT_Wpragmas, "junk at end of #pragma __nostandard"); |
| } |
| |
| /* '#pragma __standard' is simply ignored. */ |
| |
| static void |
| vms_pragma_standard (cpp_reader *pfile ATTRIBUTE_UNUSED) |
| { |
| tree x; |
| |
| if (pragma_lex (&x) != CPP_EOF) |
| warning (OPT_Wpragmas, "junk at end of #pragma __standard"); |
| } |
| |
| /* Saved member alignment. */ |
| static int saved_member_alignment; |
| |
| /* Handle '#pragma member_alignment'. */ |
| |
| static void |
| vms_pragma_member_alignment (cpp_reader *pfile ATTRIBUTE_UNUSED) |
| { |
| tree x; |
| int tok; |
| const char *arg; |
| |
| tok = pragma_lex (&x); |
| |
| if (tok == CPP_EOF) |
| { |
| /* Disable packing. */ |
| maximum_field_alignment = initial_max_fld_align; |
| return; |
| } |
| if (tok != CPP_NAME) |
| { |
| warning (OPT_Wpragmas, "malformed '#pragma member_alignment', ignoring"); |
| return; |
| } |
| |
| arg = IDENTIFIER_POINTER (x); |
| /* Accept '__' prefix. */ |
| if (arg[0] == '_' && arg[1] == '_') |
| arg += 2; |
| |
| if (strcmp (arg, "save") == 0) |
| saved_member_alignment = maximum_field_alignment; |
| else if (strcmp (arg, "restore") == 0) |
| maximum_field_alignment = saved_member_alignment; |
| else |
| { |
| error ("unknown '#pragma member_alignment' name %s", arg); |
| return; |
| } |
| if (pragma_lex (&x) != CPP_EOF) |
| { |
| error ("malformed '#pragma member_alignment'"); |
| return; |
| } |
| } |
| |
| /* Handle '#pragma nomember_alignment'. */ |
| |
| static void |
| vms_pragma_nomember_alignment (cpp_reader *pfile ATTRIBUTE_UNUSED) |
| { |
| tree x; |
| int tok; |
| |
| tok = pragma_lex (&x); |
| if (tok == CPP_NAME) |
| { |
| const char *arg = IDENTIFIER_POINTER (x); |
| |
| /* Accept '__' prefix. */ |
| if (arg[0] == '_' && arg[1] == '_') |
| arg += 2; |
| |
| if (strcmp (arg, "byte") == 0) |
| maximum_field_alignment = 1 * BITS_PER_UNIT; |
| else if (strcmp (arg, "word") == 0) |
| maximum_field_alignment = 2 * BITS_PER_UNIT; |
| else if (strcmp (arg, "longword") == 0) |
| maximum_field_alignment = 4 * BITS_PER_UNIT; |
| else if (strcmp (arg, "quadword") == 0) |
| maximum_field_alignment = 8 * BITS_PER_UNIT; |
| else if (strcmp (arg, "octaword") == 0) |
| maximum_field_alignment = 16 * BITS_PER_UNIT; |
| else |
| { |
| error ("unhandled alignment for '#pragma nomember_alignment'"); |
| } |
| |
| tok = pragma_lex (&x); |
| } |
| else |
| { |
| /* Enable packing. */ |
| maximum_field_alignment = BITS_PER_UNIT; |
| } |
| |
| if (tok != CPP_EOF) |
| { |
| error ("garbage at end of '#pragma nomember_alignment'"); |
| return; |
| } |
| } |
| |
| /* The 'extern model' for public data. This drives how the following |
| declarations are handled: |
| 1) extern int name; |
| 2) int name; |
| 3) int name = 5; |
| See below for the behaviour as implemented by the native compiler. |
| */ |
| |
| enum extern_model_kind |
| { |
| /* Create one overlaid section per variable. All the above declarations (1, |
| 2 and 3) are handled the same way: they create an overlaid section named |
| NAME (and initialized only for 3). No global symbol is created. |
| This is the VAX C behavior. */ |
| extern_model_common_block, |
| |
| /* Like unix: multiple not-initialized declarations are allowed. |
| Only one initialized definition (case 3) is allows, but multiple |
| uninitialize definition (case 2) are allowed. |
| For case 2, this creates both a section named NAME and a global symbol. |
| For case 3, this creates a conditional global symbol defenition and a |
| conditional section definition. |
| This is the traditional UNIX C behavior. */ |
| extern_model_relaxed_refdef, |
| |
| /* Like -fno-common. Only one definition (cases 2 and 3) are allowed. |
| This is the ANSI-C model. */ |
| extern_model_strict_refdef, |
| |
| /* Declarations creates symbols without storage. */ |
| extern_model_globalvalue |
| }; |
| |
| /* Current and saved extern model. */ |
| static enum extern_model_kind current_extern_model; |
| static enum extern_model_kind saved_extern_model; |
| |
| /* Partial handling of '#pragma extern_model'. */ |
| |
| static void |
| vms_pragma_extern_model (cpp_reader *pfile ATTRIBUTE_UNUSED) |
| { |
| tree x; |
| int tok; |
| const char *arg; |
| |
| tok = pragma_lex (&x); |
| |
| if (tok != CPP_NAME) |
| { |
| warning (OPT_Wpragmas, "malformed '#pragma extern_model', ignoring"); |
| return; |
| } |
| |
| arg = IDENTIFIER_POINTER (x); |
| /* Accept "__" prefix. */ |
| if (arg[0] == '_' && arg[1] == '_') |
| arg += 2; |
| |
| if (strcmp (arg, "save") == 0) |
| saved_extern_model = current_extern_model; |
| else if (strcmp (arg, "restore") == 0) |
| current_extern_model = saved_extern_model; |
| else if (strcmp (arg, "relaxed_refdef") == 0) |
| current_extern_model = extern_model_relaxed_refdef; |
| else if (strcmp (arg, "strict_refdef") == 0) |
| current_extern_model = extern_model_strict_refdef; |
| else if (strcmp (arg, "common_block") == 0) |
| current_extern_model = extern_model_common_block; |
| else if (strcmp (arg, "globalvalue") == 0) |
| { |
| sorry ("extern model globalvalue"); |
| return; |
| } |
| else |
| { |
| error ("unknown '#pragma extern_model' model '%s'", arg); |
| return; |
| } |
| #if 0 |
| if (pragma_lex (&x) != CPP_EOF) |
| { |
| permerror (input_location, "junk at end of '#pragma extern_model'"); |
| return; |
| } |
| #endif |
| } |
| |
| /* Ignore '#pragma message'. */ |
| |
| static void |
| vms_pragma_message (cpp_reader *pfile ATTRIBUTE_UNUSED) |
| { |
| /* Completly ignored. */ |
| #if 0 |
| pedwarn (input_location, OPT_Wpragmas, |
| "vms '#pragma __message' is ignored"); |
| #endif |
| } |
| |
| /* Handle '#pragma __extern_prefix' */ |
| |
| static GTY(()) tree saved_extern_prefix; |
| |
| static void |
| vms_pragma_extern_prefix (cpp_reader * ARG_UNUSED (dummy)) |
| { |
| enum cpp_ttype tok; |
| tree x; |
| |
| tok = pragma_lex (&x); |
| if (tok == CPP_NAME) |
| { |
| const char *op = IDENTIFIER_POINTER (x); |
| |
| if (!strcmp (op, "__save")) |
| saved_extern_prefix = pragma_extern_prefix; |
| else if (!strcmp (op, "__restore")) |
| pragma_extern_prefix = saved_extern_prefix; |
| else |
| warning (OPT_Wpragmas, |
| "malformed '#pragma __extern_prefix', ignoring"); |
| return; |
| } |
| else if (tok != CPP_STRING) |
| { |
| warning (OPT_Wpragmas, |
| "malformed '#pragma __extern_prefix', ignoring"); |
| } |
| else |
| { |
| /* Note that the length includes the null terminator. */ |
| pragma_extern_prefix = (TREE_STRING_LENGTH (x) > 1 ? x : NULL); |
| } |
| } |
| |
| /* #pragma __pointer_size */ |
| |
| static machine_mode saved_pointer_mode; |
| |
| static void |
| handle_pragma_pointer_size (const char *pragma_name) |
| { |
| enum cpp_ttype tok; |
| tree x; |
| |
| tok = pragma_lex (&x); |
| if (tok == CPP_NAME) |
| { |
| const char *op = IDENTIFIER_POINTER (x); |
| |
| if (!strcmp (op, "__save")) |
| saved_pointer_mode = c_default_pointer_mode; |
| else if (!strcmp (op, "__restore")) |
| c_default_pointer_mode = saved_pointer_mode; |
| else if (!strcmp (op, "__short")) |
| c_default_pointer_mode = SImode; |
| else if (!strcmp (op, "__long")) |
| c_default_pointer_mode = DImode; |
| else |
| error ("malformed %<#pragma %s%>, ignoring", pragma_name); |
| } |
| else if (tok == CPP_NUMBER) |
| { |
| int val; |
| |
| if (TREE_CODE (x) == INTEGER_CST) |
| val = TREE_INT_CST_LOW (x); |
| else |
| val = -1; |
| |
| if (val == 32) |
| c_default_pointer_mode = SImode; |
| else if (val == 64) |
| c_default_pointer_mode = DImode; |
| else |
| error ("invalid constant in %<#pragma %s%>", pragma_name); |
| } |
| else |
| { |
| error ("malformed %<#pragma %s%>, ignoring", pragma_name); |
| } |
| } |
| |
| static void |
| vms_pragma_pointer_size (cpp_reader * ARG_UNUSED (dummy)) |
| { |
| /* Ignore if no -mpointer-size option. */ |
| if (flag_vms_pointer_size == VMS_POINTER_SIZE_NONE) |
| return; |
| |
| handle_pragma_pointer_size ("pointer_size"); |
| } |
| |
| static void |
| vms_pragma_required_pointer_size (cpp_reader * ARG_UNUSED (dummy)) |
| { |
| handle_pragma_pointer_size ("required_pointer_size"); |
| } |
| |
| /* Add vms-specific pragma. */ |
| |
| void |
| vms_c_register_pragma (void) |
| { |
| c_register_pragma (NULL, "__nostandard", vms_pragma_nostandard); |
| c_register_pragma (NULL, "nostandard", vms_pragma_nostandard); |
| c_register_pragma (NULL, "__standard", vms_pragma_standard); |
| c_register_pragma (NULL, "standard", vms_pragma_standard); |
| c_register_pragma (NULL, "__member_alignment", vms_pragma_member_alignment); |
| c_register_pragma (NULL, "member_alignment", vms_pragma_member_alignment); |
| c_register_pragma_with_expansion (NULL, "__nomember_alignment", |
| vms_pragma_nomember_alignment); |
| c_register_pragma_with_expansion (NULL, "nomember_alignment", |
| vms_pragma_nomember_alignment); |
| c_register_pragma (NULL, "__pointer_size", |
| vms_pragma_pointer_size); |
| c_register_pragma (NULL, "__required_pointer_size", |
| vms_pragma_required_pointer_size); |
| c_register_pragma (NULL, "__extern_model", vms_pragma_extern_model); |
| c_register_pragma (NULL, "extern_model", vms_pragma_extern_model); |
| c_register_pragma (NULL, "__message", vms_pragma_message); |
| c_register_pragma (NULL, "__extern_prefix", vms_pragma_extern_prefix); |
| } |
| |
| /* Canonicalize the filename (remove directory prefix, force the .h extension), |
| and append it to the directory to create the path, but don't |
| turn / into // or // into ///; // may be a namespace escape. */ |
| |
| static char * |
| vms_construct_include_filename (const char *fname, cpp_dir *dir) |
| { |
| size_t dlen, flen; |
| char *path; |
| const char *fbasename = lbasename (fname); |
| size_t i; |
| |
| dlen = dir->len; |
| flen = strlen (fbasename) + 2; |
| path = XNEWVEC (char, dlen + 1 + flen + 1); |
| memcpy (path, dir->name, dlen); |
| if (dlen && !IS_DIR_SEPARATOR (path[dlen - 1])) |
| path[dlen++] = '/'; |
| for (i = 0; i < flen; i++) |
| if (fbasename[i] == '.') |
| break; |
| else |
| path[dlen + i] = TOLOWER (fbasename[i]); |
| path[dlen + i + 0] = '.'; |
| path[dlen + i + 1] = 'h'; |
| path[dlen + i + 2] = 0; |
| |
| return path; |
| } |
| |
| /* Standard modules list. */ |
| static const char * const vms_std_modules[] = { "rtldef", "starlet_c", NULL }; |
| |
| /* Find include modules in the include path. */ |
| |
| void |
| vms_c_register_includes (const char *sysroot, |
| const char *iprefix ATTRIBUTE_UNUSED, int stdinc) |
| { |
| static const char dir_separator_str[] = { DIR_SEPARATOR, 0 }; |
| struct cpp_dir *dir; |
| |
| /* Add on standard include pathes. */ |
| if (!stdinc) |
| return; |
| |
| for (dir = get_added_cpp_dirs (SYSTEM); dir != NULL; dir = dir->next) |
| { |
| const char * const *lib; |
| for (lib = vms_std_modules; *lib != NULL; lib++) |
| { |
| char *path; |
| struct stat st; |
| |
| if (sysroot != NULL) |
| path = concat (sysroot, dir->name, dir_separator_str, *lib, NULL); |
| else |
| path = concat (dir->name, dir_separator_str, *lib, NULL); |
| |
| if (stat (path, &st) == 0 && S_ISDIR (st.st_mode)) |
| { |
| cpp_dir *p; |
| |
| p = XNEW (cpp_dir); |
| p->next = NULL; |
| p->name = path; |
| p->sysp = 1; |
| p->construct = vms_construct_include_filename; |
| p->user_supplied_p = 0; |
| add_cpp_dir_path (p, SYSTEM); |
| } |
| else |
| free (path); |
| } |
| } |
| } |
| |
| void |
| vms_c_common_override_options (void) |
| { |
| /* Allow variadic functions without parameters (as declared in starlet). */ |
| flag_allow_parameterless_variadic_functions = TRUE; |
| |
| /* Initialize c_default_pointer_mode. */ |
| switch (flag_vms_pointer_size) |
| { |
| case VMS_POINTER_SIZE_NONE: |
| break; |
| case VMS_POINTER_SIZE_32: |
| c_default_pointer_mode = SImode; |
| break; |
| case VMS_POINTER_SIZE_64: |
| c_default_pointer_mode = DImode; |
| break; |
| } |
| } |
| |
| /* The default value for _CRTL_VER macro. */ |
| |
| int |
| vms_c_get_crtl_ver (void) |
| { |
| return VMS_DEFAULT_CRTL_VER; |
| } |
| |
| /* The default value for _VMS_VER macro. */ |
| |
| int |
| vms_c_get_vms_ver (void) |
| { |
| return VMS_DEFAULT_VMS_VER; |
| } |