| /* Definitions of Toshiba Media Processor |
| Copyright (C) 2001-2014 Free Software Foundation, Inc. |
| Contributed by Red Hat, Inc. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "tree.h" |
| #include "diagnostic-core.h" |
| #include "c-family/c-pragma.h" |
| #include "cpplib.h" |
| #include "hard-reg-set.h" |
| #include "output.h" /* for decode_reg_name */ |
| #include "mep-protos.h" |
| #include "function.h" |
| #define MAX_RECOG_OPERANDS 10 |
| #include "reload.h" |
| #include "target.h" |
| |
| enum cw_which { CW_AVAILABLE, CW_CALL_SAVED }; |
| |
| /* This is normally provided by rtl.h but we can't include that file |
| here. It's safe to copy the definition here because we're only |
| using it internally; the value isn't passed to functions outside |
| this file. */ |
| #ifndef INVALID_REGNUM |
| #define INVALID_REGNUM (~(unsigned int) 0) |
| #endif |
| |
| static enum cpp_ttype |
| mep_pragma_lex (tree *valp) |
| { |
| enum cpp_ttype t = pragma_lex (valp); |
| if (t == CPP_EOF) |
| t = CPP_PRAGMA_EOL; |
| return t; |
| } |
| |
| static void |
| mep_pragma_io_volatile (cpp_reader *reader ATTRIBUTE_UNUSED) |
| { |
| /* On off. */ |
| tree val; |
| enum cpp_ttype type; |
| const char * str; |
| |
| type = mep_pragma_lex (&val); |
| if (type == CPP_NAME) |
| { |
| str = IDENTIFIER_POINTER (val); |
| |
| type = mep_pragma_lex (&val); |
| if (type != CPP_PRAGMA_EOL) |
| warning (0, "junk at end of #pragma io_volatile"); |
| |
| if (strcmp (str, "on") == 0) |
| { |
| target_flags |= MASK_IO_VOLATILE; |
| return; |
| } |
| if (strcmp (str, "off") == 0) |
| { |
| target_flags &= ~ MASK_IO_VOLATILE; |
| return; |
| } |
| } |
| |
| error ("#pragma io_volatile takes only on or off"); |
| } |
| |
| static unsigned int |
| parse_cr_reg (const char * str) |
| { |
| unsigned int regno; |
| |
| regno = decode_reg_name (str); |
| if (regno >= FIRST_PSEUDO_REGISTER) |
| return INVALID_REGNUM; |
| |
| /* Verify that the regno is in CR_REGS. */ |
| if (! TEST_HARD_REG_BIT (reg_class_contents[CR_REGS], regno)) |
| return INVALID_REGNUM; |
| return regno; |
| } |
| |
| static bool |
| parse_cr_set (HARD_REG_SET * set) |
| { |
| tree val; |
| enum cpp_ttype type; |
| unsigned int last_regno = INVALID_REGNUM; |
| bool do_range = false; |
| |
| CLEAR_HARD_REG_SET (*set); |
| |
| while ((type = mep_pragma_lex (&val)) != CPP_PRAGMA_EOL) |
| { |
| if (type == CPP_COMMA) |
| { |
| last_regno = INVALID_REGNUM; |
| do_range = false; |
| } |
| else if (type == CPP_ELLIPSIS) |
| { |
| if (last_regno == INVALID_REGNUM) |
| { |
| error ("invalid coprocessor register range"); |
| return false; |
| } |
| do_range = true; |
| } |
| else if (type == CPP_NAME || type == CPP_STRING) |
| { |
| const char *str; |
| unsigned int regno, i; |
| |
| if (TREE_CODE (val) == IDENTIFIER_NODE) |
| str = IDENTIFIER_POINTER (val); |
| else if (TREE_CODE (val) == STRING_CST) |
| str = TREE_STRING_POINTER (val); |
| else |
| gcc_unreachable (); |
| |
| regno = parse_cr_reg (str); |
| if (regno == INVALID_REGNUM) |
| { |
| error ("invalid coprocessor register %qE", val); |
| return false; |
| } |
| |
| if (do_range) |
| { |
| if (last_regno > regno) |
| i = regno, regno = last_regno; |
| else |
| i = last_regno; |
| do_range = false; |
| } |
| else |
| last_regno = i = regno; |
| |
| while (i <= regno) |
| { |
| SET_HARD_REG_BIT (*set, i); |
| i++; |
| } |
| } |
| else |
| { |
| error ("malformed coprocessor register"); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| static void |
| mep_pragma_coprocessor_which (enum cw_which cw_which) |
| { |
| HARD_REG_SET set; |
| |
| /* Process the balance of the pragma and turn it into a hard reg set. */ |
| if (! parse_cr_set (&set)) |
| return; |
| |
| /* Process the collected hard reg set. */ |
| switch (cw_which) |
| { |
| case CW_AVAILABLE: |
| { |
| int i; |
| for (i = 0; i < FIRST_PSEUDO_REGISTER; ++i) |
| if (TEST_HARD_REG_BIT (set, i)) |
| fixed_regs[i] = 0; |
| } |
| break; |
| |
| case CW_CALL_SAVED: |
| { |
| int i; |
| for (i = 0; i < FIRST_PSEUDO_REGISTER; ++i) |
| if (TEST_HARD_REG_BIT (set, i)) |
| fixed_regs[i] = call_used_regs[i] = 0; |
| } |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| /* Fix up register class hierarchy. */ |
| mep_save_register_info (); |
| mep_reinit_regs (); |
| |
| if (cfun == 0) |
| { |
| init_dummy_function_start (); |
| init_caller_save (); |
| expand_dummy_function_end (); |
| } |
| else |
| { |
| init_caller_save (); |
| } |
| } |
| |
| static void |
| mep_pragma_coprocessor_width (void) |
| { |
| tree val; |
| enum cpp_ttype type; |
| HOST_WIDE_INT i; |
| |
| type = mep_pragma_lex (&val); |
| switch (type) |
| { |
| case CPP_NUMBER: |
| if (! tree_fits_uhwi_p (val)) |
| break; |
| i = tree_to_uhwi (val); |
| /* This pragma no longer has any effect. */ |
| #if 0 |
| if (i == 32) |
| target_flags &= ~MASK_64BIT_CR_REGS; |
| else if (i == 64) |
| target_flags |= MASK_64BIT_CR_REGS; |
| else |
| break; |
| targetm.init_builtins (); |
| #else |
| if (i != 32 && i != 64) |
| break; |
| #endif |
| |
| type = mep_pragma_lex (&val); |
| if (type != CPP_PRAGMA_EOL) |
| warning (0, "junk at end of #pragma GCC coprocessor width"); |
| return; |
| |
| default: |
| break; |
| } |
| |
| error ("#pragma GCC coprocessor width takes only 32 or 64"); |
| } |
| |
| static void |
| mep_pragma_coprocessor_subclass (void) |
| { |
| tree val; |
| enum cpp_ttype type; |
| HARD_REG_SET set; |
| int class_letter; |
| enum reg_class rclass; |
| |
| type = mep_pragma_lex (&val); |
| if (type != CPP_CHAR) |
| goto syntax_error; |
| class_letter = tree_to_uhwi (val); |
| if (class_letter >= 'A' && class_letter <= 'D') |
| switch (class_letter) |
| { |
| case 'A': |
| rclass = USER0_REGS; |
| break; |
| case 'B': |
| rclass = USER1_REGS; |
| break; |
| case 'C': |
| rclass = USER2_REGS; |
| break; |
| case 'D': |
| rclass = USER3_REGS; |
| break; |
| } |
| else |
| { |
| error ("#pragma GCC coprocessor subclass letter must be in [ABCD]"); |
| return; |
| } |
| if (reg_class_size[rclass] > 0) |
| { |
| error ("#pragma GCC coprocessor subclass '%c' already defined", |
| class_letter); |
| return; |
| } |
| |
| type = mep_pragma_lex (&val); |
| if (type != CPP_EQ) |
| goto syntax_error; |
| |
| if (! parse_cr_set (&set)) |
| return; |
| |
| /* Fix up register class hierarchy. */ |
| COPY_HARD_REG_SET (reg_class_contents[rclass], set); |
| mep_init_regs (); |
| return; |
| |
| syntax_error: |
| error ("malformed #pragma GCC coprocessor subclass"); |
| } |
| |
| static void |
| mep_pragma_disinterrupt (cpp_reader *reader ATTRIBUTE_UNUSED) |
| { |
| tree val; |
| enum cpp_ttype type; |
| int saw_one = 0; |
| |
| for (;;) |
| { |
| type = mep_pragma_lex (&val); |
| if (type == CPP_COMMA) |
| continue; |
| if (type != CPP_NAME) |
| break; |
| mep_note_pragma_disinterrupt (IDENTIFIER_POINTER (val)); |
| saw_one = 1; |
| } |
| if (!saw_one || type != CPP_PRAGMA_EOL) |
| { |
| error ("malformed #pragma disinterrupt"); |
| return; |
| } |
| } |
| |
| static void |
| mep_pragma_coprocessor (cpp_reader *reader ATTRIBUTE_UNUSED) |
| { |
| tree val; |
| enum cpp_ttype type; |
| |
| type = mep_pragma_lex (&val); |
| if (type != CPP_NAME) |
| { |
| error ("malformed #pragma GCC coprocessor"); |
| return; |
| } |
| |
| if (!TARGET_COP) |
| error ("coprocessor not enabled"); |
| |
| if (strcmp (IDENTIFIER_POINTER (val), "available") == 0) |
| mep_pragma_coprocessor_which (CW_AVAILABLE); |
| else if (strcmp (IDENTIFIER_POINTER (val), "call_saved") == 0) |
| mep_pragma_coprocessor_which (CW_CALL_SAVED); |
| else if (strcmp (IDENTIFIER_POINTER (val), "width") == 0) |
| mep_pragma_coprocessor_width (); |
| else if (strcmp (IDENTIFIER_POINTER (val), "subclass") == 0) |
| mep_pragma_coprocessor_subclass (); |
| else |
| error ("unknown #pragma GCC coprocessor %E", val); |
| } |
| |
| static void |
| mep_pragma_call (cpp_reader *reader ATTRIBUTE_UNUSED) |
| { |
| tree val; |
| enum cpp_ttype type; |
| int saw_one = 0; |
| |
| for (;;) |
| { |
| type = mep_pragma_lex (&val); |
| if (type == CPP_COMMA) |
| continue; |
| if (type != CPP_NAME) |
| break; |
| mep_note_pragma_call (IDENTIFIER_POINTER (val)); |
| saw_one = 1; |
| } |
| if (!saw_one || type != CPP_PRAGMA_EOL) |
| { |
| error ("malformed #pragma call"); |
| return; |
| } |
| } |
| |
| void |
| mep_register_pragmas (void) |
| { |
| c_register_pragma ("custom", "io_volatile", mep_pragma_io_volatile); |
| c_register_pragma ("GCC", "coprocessor", mep_pragma_coprocessor); |
| c_register_pragma (0, "disinterrupt", mep_pragma_disinterrupt); |
| c_register_pragma (0, "call", mep_pragma_call); |
| } |