diff mbox series

[v2] MIPS16: Implement `code_readable` function attribute.

Message ID 20230519080631.309062-1-jie.mei@oss.cipunited.com
State New
Headers show
Series [v2] MIPS16: Implement `code_readable` function attribute. | expand

Commit Message

Jie Mei May 19, 2023, 8:07 a.m. UTC
From: Simon Dardis <simon.dardis@imgtec.com>

Support for __attribute__ ((code_readable)).  Takes up to one argument of
"yes", "no", "pcrel".  This will change the code readability setting for just
that function.  If no argument is supplied, then the setting is 'yes'.

gcc/ChangeLog:

	* config/mips/mips.cc (enum mips_code_readable_setting):New enmu.
	(mips_handle_code_readable_attr):New static function.
	(mips_get_code_readable_attr):New static enum function.
	(mips_set_current_function):Set the code_readable mode.
	(mips_option_override):Same as above.
	* doc/extend.texi:Document code_readable.

gcc/testsuite/ChangeLog:

	* gcc.target/mips/code-readable-attr-1.c: New test.
	* gcc.target/mips/code-readable-attr-2.c: New test.
	* gcc.target/mips/code-readable-attr-3.c: New test.
	* gcc.target/mips/code-readable-attr-4.c: New test.
	* gcc.target/mips/code-readable-attr-5.c: New test.
---
 gcc/config/mips/mips.cc                       | 97 ++++++++++++++++++-
 gcc/doc/extend.texi                           | 17 ++++
 .../gcc.target/mips/code-readable-attr-1.c    | 51 ++++++++++
 .../gcc.target/mips/code-readable-attr-2.c    | 49 ++++++++++
 .../gcc.target/mips/code-readable-attr-3.c    | 50 ++++++++++
 .../gcc.target/mips/code-readable-attr-4.c    | 51 ++++++++++
 .../gcc.target/mips/code-readable-attr-5.c    |  5 +
 7 files changed, 319 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/gcc.target/mips/code-readable-attr-1.c
 create mode 100644 gcc/testsuite/gcc.target/mips/code-readable-attr-2.c
 create mode 100644 gcc/testsuite/gcc.target/mips/code-readable-attr-3.c
 create mode 100644 gcc/testsuite/gcc.target/mips/code-readable-attr-4.c
 create mode 100644 gcc/testsuite/gcc.target/mips/code-readable-attr-5.c

Comments

YunQiang Su June 6, 2023, 5:59 a.m. UTC | #1
Jie Mei <jie.mei@oss.cipunited.com> 于2023年5月19日周五 16:07写道:
>
> From: Simon Dardis <simon.dardis@imgtec.com>
>
> Support for __attribute__ ((code_readable)).  Takes up to one argument of
> "yes", "no", "pcrel".  This will change the code readability setting for just
> that function.  If no argument is supplied, then the setting is 'yes'.
>
> gcc/ChangeLog:
>
>         * config/mips/mips.cc (enum mips_code_readable_setting):New enmu.
>         (mips_handle_code_readable_attr):New static function.
>         (mips_get_code_readable_attr):New static enum function.
>         (mips_set_current_function):Set the code_readable mode.
>         (mips_option_override):Same as above.
>         * doc/extend.texi:Document code_readable.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.target/mips/code-readable-attr-1.c: New test.
>         * gcc.target/mips/code-readable-attr-2.c: New test.
>         * gcc.target/mips/code-readable-attr-3.c: New test.
>         * gcc.target/mips/code-readable-attr-4.c: New test.
>         * gcc.target/mips/code-readable-attr-5.c: New test.
> ---
>  gcc/config/mips/mips.cc                       | 97 ++++++++++++++++++-
>  gcc/doc/extend.texi                           | 17 ++++
>  .../gcc.target/mips/code-readable-attr-1.c    | 51 ++++++++++
>  .../gcc.target/mips/code-readable-attr-2.c    | 49 ++++++++++
>  .../gcc.target/mips/code-readable-attr-3.c    | 50 ++++++++++
>  .../gcc.target/mips/code-readable-attr-4.c    | 51 ++++++++++
>  .../gcc.target/mips/code-readable-attr-5.c    |  5 +
>  7 files changed, 319 insertions(+), 1 deletion(-)
>  create mode 100644 gcc/testsuite/gcc.target/mips/code-readable-attr-1.c
>  create mode 100644 gcc/testsuite/gcc.target/mips/code-readable-attr-2.c
>  create mode 100644 gcc/testsuite/gcc.target/mips/code-readable-attr-3.c
>  create mode 100644 gcc/testsuite/gcc.target/mips/code-readable-attr-4.c
>  create mode 100644 gcc/testsuite/gcc.target/mips/code-readable-attr-5.c
>
> diff --git a/gcc/config/mips/mips.cc b/gcc/config/mips/mips.cc
> index ca822758b41..97f45e67529 100644
> --- a/gcc/config/mips/mips.cc
> +++ b/gcc/config/mips/mips.cc
> @@ -498,6 +498,9 @@ static int mips_base_target_flags;
>  /* The default compression mode.  */
>  unsigned int mips_base_compression_flags;
>
> +/* The default code readable setting.  */
> +enum mips_code_readable_setting mips_base_code_readable;
> +
>  /* The ambient values of other global variables.  */
>  static int mips_base_schedule_insns; /* flag_schedule_insns */
>  static int mips_base_reorder_blocks_and_partition; /* flag_reorder... */
> @@ -602,6 +605,7 @@ const enum reg_class mips_regno_to_class[FIRST_PSEUDO_REGISTER] = {
>    ALL_REGS,    ALL_REGS,       ALL_REGS,       ALL_REGS
>  };
>
> +static tree mips_handle_code_readable_attr (tree *, tree, tree, int, bool *);
>  static tree mips_handle_interrupt_attr (tree *, tree, tree, int, bool *);
>  static tree mips_handle_use_shadow_register_set_attr (tree *, tree, tree, int,
>                                                       bool *);
> @@ -623,6 +627,8 @@ static const struct attribute_spec mips_attribute_table[] = {
>    { "micromips",   0, 0, true,  false, false, false, NULL, NULL },
>    { "nomicromips", 0, 0, true,  false, false, false, NULL, NULL },
>    { "nocompression", 0, 0, true,  false, false, false, NULL, NULL },
> +  { "code_readable", 0, 1, true,  false, false, false,
> +    mips_handle_code_readable_attr, NULL },
>    /* Allow functions to be specified as interrupt handlers */
>    { "interrupt",   0, 1, false, true,  true, false, mips_handle_interrupt_attr,
>      NULL },
> @@ -1310,6 +1316,81 @@ mips_use_debug_exception_return_p (tree type)
>                            TYPE_ATTRIBUTES (type)) != NULL;
>  }
>
> +
> +/* Verify the arguments to a code_readable attribute.  */
> +
> +static tree
> +mips_handle_code_readable_attr (tree *node ATTRIBUTE_UNUSED, tree name,
> +                               tree args, int flags ATTRIBUTE_UNUSED,
> +                               bool *no_add_attrs)
> +{
> +  if (!is_attribute_p ("code_readable", name) || args == NULL)
> +    return NULL_TREE;
> +
> +  if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
> +    {
> +      warning (OPT_Wattributes,
> +              "%qE attribute requires a string argument", name);
> +      *no_add_attrs = true;
> +    }
> +  else if (strcmp (TREE_STRING_POINTER (TREE_VALUE (args)), "no") != 0
> +          && strcmp (TREE_STRING_POINTER (TREE_VALUE (args)), "pcrel") != 0
> +          && strcmp (TREE_STRING_POINTER (TREE_VALUE (args)), "yes") != 0)
> +    {
> +      warning (OPT_Wattributes,
> +              "argument to %qE attribute is neither no, pcrel nor yes", name);
> +      *no_add_attrs = true;
> +    }
> +
> +  return NULL_TREE;
> +}
> +
> +/* Determine the code_readable setting for a function if it has one.  Set
> +   *valid to true if we have a properly formed argument and
> +   return the result. If there's no argument, return GCC's default.

contrib/check_GNU_style.sh complains that one more space is needed after
       return the result.

> +   Otherwise, leave valid false and return mips_base_code_readable.  In
> +   that case the result should be unused anyway.  */
> +
> +static enum mips_code_readable_setting
> +mips_get_code_readable_attr (tree decl)
> +{
> +  tree attr;
> +
> +  if (decl == NULL)
> +    return mips_base_code_readable;
> +
> +  attr = lookup_attribute ("code_readable", DECL_ATTRIBUTES (decl));
> +
> +  if (attr != NULL)
> +    {
> +      if (TREE_VALUE (attr) != NULL_TREE)
> +       {
> +         const char * str;
> +
> +         str = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr)));
> +         if (strcmp (str, "no") == 0)
> +           return CODE_READABLE_NO;
> +         else if (strcmp (str, "pcrel") == 0)
> +           return CODE_READABLE_PCREL;
> +         else if (strcmp (str, "yes") == 0)
> +           return CODE_READABLE_YES;
> +
> +         /* mips_handle_code_readable_attr will have verified the
> +            arguments are correct before adding the attribute.  */
> +         gcc_unreachable ();
> +       }
> +
> +      /* Just like GCC's default -mcode-readable= setting, the
> +        presence of the code_readable attribute implies that a
> +        function can read data from the instruction stream by
> +        default.  */
> +      return CODE_READABLE_YES;
> +    }
> +
> +  return mips_base_code_readable;
> +}
> +
> +
>  /* Return the set of compression modes that are explicitly required
>     by the attributes in ATTRIBUTES.  */
>
> @@ -19905,12 +19986,25 @@ mips_set_compression_mode (unsigned int compression_mode)
>
>  /* Implement TARGET_SET_CURRENT_FUNCTION.  Decide whether the current
>     function should use the MIPS16 or microMIPS ISA and switch modes
> -   accordingly.  */
> +   accordingly.  Also set the current code_readable mode.  */
>
>  static void
>  mips_set_current_function (tree fndecl)
>  {
> +  enum mips_code_readable_setting old_code_readable = mips_code_readable;
> +
>    mips_set_compression_mode (mips_get_compress_mode (fndecl));
> +
> +  mips_code_readable = mips_get_code_readable_attr (fndecl);
> +
> +  /* Since the mips_code_readable setting has potientially changed, the

A typo: potientially -> potentially

> +     relocation tables must be reinitialized.  Otherwise GCC will not
> +     split symbols for functions that are code_readable ("no") when others
> +     are code_readable ("yes") and ICE later on in places such as
> +     mips_emit_move.  Ditto for similar paired cases.  It must be restored
> +     to its previous state as well.  */
> +  if (old_code_readable != mips_code_readable)
> +    mips_init_relocs ();
>  }
>
>  /* Allocate a chunk of memory for per-function machine-dependent data.  */
> @@ -20042,6 +20136,7 @@ mips_option_override (void)
>       were generating uncompressed code.  */
>    mips_base_compression_flags = TARGET_COMPRESSION;
>    target_flags &= ~TARGET_COMPRESSION;
> +  mips_base_code_readable = mips_code_readable;
>
>    /* -mno-float overrides -mhard-float and -msoft-float.  */
>    if (TARGET_NO_FLOAT)
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 69b21a75e62..b48f5f55513 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -5670,6 +5670,23 @@ command line (@pxref{MIPS Options}).
>  This function attribute instructs the compiler to generate a hazard barrier
>  return that clears all execution and instruction hazards while returning,
>  instead of generating a normal return instruction.
> +
> +@item code_readable
> +@cindex @code{code_readable} function attribute, MIPS
> +For MIPS targets that support PC-relative addressing modes, this attribute
> +can be used to control how an object is addressed.  The attribute takes
> +a single optional argument:
> +
> +@table @samp
> +@item no
> +The function should not read the instruction stream as data.
> +@item yes
> +The function can read the instruction stream as data.
> +@item pcrel
> +The function can read the instruction stream in a pc-relative mode.
> +@end table
> +
> +If there is no argument supplied, the default of @code{"yes"} applies.
>  @end table
>
>  @node MSP430 Function Attributes
> diff --git a/gcc/testsuite/gcc.target/mips/code-readable-attr-1.c b/gcc/testsuite/gcc.target/mips/code-readable-attr-1.c
> new file mode 100644
> index 00000000000..4ccb27c8bde
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/mips/code-readable-attr-1.c
> @@ -0,0 +1,51 @@
> +/* { dg-options "(-mips16) -mgp32 addressing=absolute" } */
> +/* { dg-skip-if ".half requires -O" { *-*-* } { "-O0" } { "" } } */
> +
> +volatile int x1;
> +volatile int x2;
> +volatile int x3;
> +volatile int x4;
> +volatile int x5;
> +volatile int x6;
> +volatile int x7;
> +volatile int x8;
> +volatile int x9;
> +volatile int x10;
> +volatile int x11;
> +
> +MIPS16 __attribute__ ((code_readable)) int
> +foo (int i, volatile int *x)
> +{
> +  switch (i)
> +    {
> +    case 1: return x1 + x[0];
> +    case 2: return x2 + x[1];
> +    case 3: return x3 + x[2];
> +    case 4: return x4 + x[3];
> +    case 5: return x5 + x[4];
> +    case 6: return x6 + x[5];
> +    case 7: return x7 + x[6];
> +    case 8: return x8 + x[7];
> +    case 9: return x9 + x[8];
> +    case 10: return x10 + x[9];
> +    case 11: return x11 + x[10];
> +    default: return 0;
> +    }
> +}
> +
> +extern int k[];
> +
> +MIPS16 __attribute__ ((code_readable)) int *
> +bar (void)
> +{
> +  return k;
> +}
> +
> +/* { dg-final { scan-assembler "\tla\t" } } */
> +/* { dg-final { scan-assembler "\t\\.half\t" } } */
> +/* { dg-final { scan-assembler-not "%hi\\(\[^)\]*L" } } */
> +/* { dg-final { scan-assembler-not "%lo\\(\[^)\]*L" } } */
> +
> +/* { dg-final { scan-assembler "\t\\.word\tk\n" } } */
> +/* { dg-final { scan-assembler-not "%hi\\(k\\)" } } */
> +/* { dg-final { scan-assembler-not "%lo\\(k\\)" } } */
> diff --git a/gcc/testsuite/gcc.target/mips/code-readable-attr-2.c b/gcc/testsuite/gcc.target/mips/code-readable-attr-2.c
> new file mode 100644
> index 00000000000..c7dd5113359
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/mips/code-readable-attr-2.c
> @@ -0,0 +1,49 @@
> +/* { dg-options "(-mips16) -mgp32 addressing=absolute" } */
> +
> +volatile int x1;
> +volatile int x2;
> +volatile int x3;
> +volatile int x4;
> +volatile int x5;
> +volatile int x6;
> +volatile int x7;
> +volatile int x8;
> +volatile int x9;
> +volatile int x10;
> +volatile int x11;
> +
> +MIPS16 __attribute__((code_readable ("pcrel"))) int
> +foo (int i, volatile int *x)
> +{
> +  switch (i)
> +    {
> +    case 1: return x1 + x[0];
> +    case 2: return x2 + x[1];
> +    case 3: return x3 + x[2];
> +    case 4: return x4 + x[3];
> +    case 5: return x5 + x[4];
> +    case 6: return x6 + x[5];
> +    case 7: return x7 + x[6];
> +    case 8: return x8 + x[7];
> +    case 9: return x9 + x[8];
> +    case 10: return x10 + x[9];
> +    case 11: return x11 + x[10];
> +    default: return 0;
> +    }
> +}
> +
> +extern int k[];
> +
> +MIPS16 __attribute__((code_readable ("pcrel"))) int *
> +bar (void)
> +{
> +  return k;
> +}
> +
> +/* { dg-final { scan-assembler-not "\tla\t" } } */
> +/* { dg-final { scan-assembler-not "\t\\.half\t" } } */
> +/* { dg-final { scan-assembler "\t\\.word\t\[^\n\]*L" } } */
> +
> +/* { dg-final { scan-assembler "\t\\.word\tk\n" } } */
> +/* { dg-final { scan-assembler-not "%hi\\(k\\)" } } */
> +/* { dg-final { scan-assembler-not "%lo\\(k\\)" } } */
> diff --git a/gcc/testsuite/gcc.target/mips/code-readable-attr-3.c b/gcc/testsuite/gcc.target/mips/code-readable-attr-3.c
> new file mode 100644
> index 00000000000..99c13a901bc
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/mips/code-readable-attr-3.c
> @@ -0,0 +1,50 @@
> +/* { dg-options "(-mips16) -mgp32 addressing=absolute" } */
> +
> +volatile int x1;
> +volatile int x2;
> +volatile int x3;
> +volatile int x4;
> +volatile int x5;
> +volatile int x6;
> +volatile int x7;
> +volatile int x8;
> +volatile int x9;
> +volatile int x10;
> +volatile int x11;
> +
> +MIPS16 __attribute__((code_readable ("no"))) int
> +foo (int i, volatile int *x)
> +{
> +  switch (i)
> +    {
> +    case 1: return x1 + x[0];
> +    case 2: return x2 + x[1];
> +    case 3: return x3 + x[2];
> +    case 4: return x4 + x[3];
> +    case 5: return x5 + x[4];
> +    case 6: return x6 + x[5];
> +    case 7: return x7 + x[6];
> +    case 8: return x8 + x[7];
> +    case 9: return x9 + x[8];
> +    case 10: return x10 + x[9];
> +    case 11: return x11 + x[10];
> +    default: return 0;
> +    }
> +}
> +
> +extern int k[];
> +
> +MIPS16 __attribute__((code_readable ("no"))) int *
> +bar (void)
> +{
> +  return k;
> +}
> +
> +/* { dg-final { scan-assembler-not "\tla\t" } } */
> +/* { dg-final { scan-assembler-not "\t\\.half\t" } } */
> +/* { dg-final { scan-assembler "%hi\\(\[^)\]*L" } } */
> +/* { dg-final { scan-assembler "%lo\\(\[^)\]*L" } } */
> +
> +/* { dg-final { scan-assembler-not "\t\\.word\tk\n" } } */
> +/* { dg-final { scan-assembler "%hi\\(k\\)" } } */
> +/* { dg-final { scan-assembler "%lo\\(k\\)" } } */
> diff --git a/gcc/testsuite/gcc.target/mips/code-readable-attr-4.c b/gcc/testsuite/gcc.target/mips/code-readable-attr-4.c
> new file mode 100644
> index 00000000000..4058ba13810
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/mips/code-readable-attr-4.c
> @@ -0,0 +1,51 @@
> +/* { dg-options "(-mips16) -mabi=eabi -mgp64" } */
> +/* { dg-skip-if ".half requires -O" { *-*-* } { "-O0" } { "" } } */
> +
> +volatile int x1;
> +volatile int x2;
> +volatile int x3;
> +volatile int x4;
> +volatile int x5;
> +volatile int x6;
> +volatile int x7;
> +volatile int x8;
> +volatile int x9;
> +volatile int x10;
> +volatile int x11;
> +
> +MIPS16 __attribute__((code_readable ("yes"))) int
> +foo (int i, volatile int *x)
> +{
> +  switch (i)
> +    {
> +    case 1: return x1 + x[0];
> +    case 2: return x2 + x[1];
> +    case 3: return x3 + x[2];
> +    case 4: return x4 + x[3];
> +    case 5: return x5 + x[4];
> +    case 6: return x6 + x[5];
> +    case 7: return x7 + x[6];
> +    case 8: return x8 + x[7];
> +    case 9: return x9 + x[8];
> +    case 10: return x10 + x[9];
> +    case 11: return x11 + x[10];
> +    default: return 0;
> +    }
> +}
> +
> +extern int k[];
> +
> +MIPS16 __attribute__((code_readable ("yes"))) int *
> +bar (void)
> +{
> +  return k;
> +}
> +
> +/* { dg-final { scan-assembler "\tdla\t" } } */
> +/* { dg-final { scan-assembler "\t\\.half\t" } } */
> +/* { dg-final { scan-assembler-not "%hi\\(\[^)\]*L" } } */
> +/* { dg-final { scan-assembler-not "%lo\\(\[^)\]*L" } } */
> +
> +/* { dg-final { scan-assembler "\t\\.dword\tk\n" } } */
> +/* { dg-final { scan-assembler-not "%hi\\(k\\)" } } */
> +/* { dg-final { scan-assembler-not "%lo\\(k\\)" } } */
> diff --git a/gcc/testsuite/gcc.target/mips/code-readable-attr-5.c b/gcc/testsuite/gcc.target/mips/code-readable-attr-5.c
> new file mode 100644
> index 00000000000..0a547a9acfc
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/mips/code-readable-attr-5.c
> @@ -0,0 +1,5 @@
> +/* { dg-options "(-mips16) isa_rev<=5" } */
> +
> + __attribute__((code_readable ("magic"))) int foo () {} /* { dg-warning "argument to 'code_readable' attribute is neither no, pcrel nor yes" } */
> +
> + __attribute__((code_readable (1))) int * bar () {} /* { dg-warning "'code_readable' attribute requires a string argument" } */
> --
> 2.40.1
diff mbox series

Patch

diff --git a/gcc/config/mips/mips.cc b/gcc/config/mips/mips.cc
index ca822758b41..97f45e67529 100644
--- a/gcc/config/mips/mips.cc
+++ b/gcc/config/mips/mips.cc
@@ -498,6 +498,9 @@  static int mips_base_target_flags;
 /* The default compression mode.  */
 unsigned int mips_base_compression_flags;
 
+/* The default code readable setting.  */
+enum mips_code_readable_setting mips_base_code_readable;
+
 /* The ambient values of other global variables.  */
 static int mips_base_schedule_insns; /* flag_schedule_insns */
 static int mips_base_reorder_blocks_and_partition; /* flag_reorder... */
@@ -602,6 +605,7 @@  const enum reg_class mips_regno_to_class[FIRST_PSEUDO_REGISTER] = {
   ALL_REGS,	ALL_REGS,	ALL_REGS,	ALL_REGS
 };
 
+static tree mips_handle_code_readable_attr (tree *, tree, tree, int, bool *);
 static tree mips_handle_interrupt_attr (tree *, tree, tree, int, bool *);
 static tree mips_handle_use_shadow_register_set_attr (tree *, tree, tree, int,
 						      bool *);
@@ -623,6 +627,8 @@  static const struct attribute_spec mips_attribute_table[] = {
   { "micromips",   0, 0, true,  false, false, false, NULL, NULL },
   { "nomicromips", 0, 0, true,  false, false, false, NULL, NULL },
   { "nocompression", 0, 0, true,  false, false, false, NULL, NULL },
+  { "code_readable", 0, 1, true,  false, false, false,
+    mips_handle_code_readable_attr, NULL },
   /* Allow functions to be specified as interrupt handlers */
   { "interrupt",   0, 1, false, true,  true, false, mips_handle_interrupt_attr,
     NULL },
@@ -1310,6 +1316,81 @@  mips_use_debug_exception_return_p (tree type)
 			   TYPE_ATTRIBUTES (type)) != NULL;
 }
 
+
+/* Verify the arguments to a code_readable attribute.  */
+
+static tree
+mips_handle_code_readable_attr (tree *node ATTRIBUTE_UNUSED, tree name,
+				tree args, int flags ATTRIBUTE_UNUSED,
+				bool *no_add_attrs)
+{
+  if (!is_attribute_p ("code_readable", name) || args == NULL)
+    return NULL_TREE;
+
+  if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+    {
+      warning (OPT_Wattributes,
+	       "%qE attribute requires a string argument", name);
+      *no_add_attrs = true;
+    }
+  else if (strcmp (TREE_STRING_POINTER (TREE_VALUE (args)), "no") != 0
+	   && strcmp (TREE_STRING_POINTER (TREE_VALUE (args)), "pcrel") != 0
+	   && strcmp (TREE_STRING_POINTER (TREE_VALUE (args)), "yes") != 0)
+    {
+      warning (OPT_Wattributes,
+	       "argument to %qE attribute is neither no, pcrel nor yes", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+/* Determine the code_readable setting for a function if it has one.  Set
+   *valid to true if we have a properly formed argument and
+   return the result. If there's no argument, return GCC's default.
+   Otherwise, leave valid false and return mips_base_code_readable.  In
+   that case the result should be unused anyway.  */
+
+static enum mips_code_readable_setting
+mips_get_code_readable_attr (tree decl)
+{
+  tree attr;
+
+  if (decl == NULL)
+    return mips_base_code_readable;
+
+  attr = lookup_attribute ("code_readable", DECL_ATTRIBUTES (decl));
+
+  if (attr != NULL)
+    {
+      if (TREE_VALUE (attr) != NULL_TREE)
+	{
+	  const char * str;
+
+	  str = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr)));
+	  if (strcmp (str, "no") == 0)
+	    return CODE_READABLE_NO;
+	  else if (strcmp (str, "pcrel") == 0)
+	    return CODE_READABLE_PCREL;
+	  else if (strcmp (str, "yes") == 0)
+	    return CODE_READABLE_YES;
+
+	  /* mips_handle_code_readable_attr will have verified the
+	     arguments are correct before adding the attribute.  */
+	  gcc_unreachable ();
+	}
+
+      /* Just like GCC's default -mcode-readable= setting, the
+	 presence of the code_readable attribute implies that a
+	 function can read data from the instruction stream by
+	 default.  */
+      return CODE_READABLE_YES;
+    }
+
+  return mips_base_code_readable;
+}
+
+
 /* Return the set of compression modes that are explicitly required
    by the attributes in ATTRIBUTES.  */
 
@@ -19905,12 +19986,25 @@  mips_set_compression_mode (unsigned int compression_mode)
 
 /* Implement TARGET_SET_CURRENT_FUNCTION.  Decide whether the current
    function should use the MIPS16 or microMIPS ISA and switch modes
-   accordingly.  */
+   accordingly.  Also set the current code_readable mode.  */
 
 static void
 mips_set_current_function (tree fndecl)
 {
+  enum mips_code_readable_setting old_code_readable = mips_code_readable;
+
   mips_set_compression_mode (mips_get_compress_mode (fndecl));
+
+  mips_code_readable = mips_get_code_readable_attr (fndecl);
+
+  /* Since the mips_code_readable setting has potientially changed, the
+     relocation tables must be reinitialized.  Otherwise GCC will not
+     split symbols for functions that are code_readable ("no") when others
+     are code_readable ("yes") and ICE later on in places such as
+     mips_emit_move.  Ditto for similar paired cases.  It must be restored
+     to its previous state as well.  */
+  if (old_code_readable != mips_code_readable)
+    mips_init_relocs ();
 }
 
 /* Allocate a chunk of memory for per-function machine-dependent data.  */
@@ -20042,6 +20136,7 @@  mips_option_override (void)
      were generating uncompressed code.  */
   mips_base_compression_flags = TARGET_COMPRESSION;
   target_flags &= ~TARGET_COMPRESSION;
+  mips_base_code_readable = mips_code_readable;
 
   /* -mno-float overrides -mhard-float and -msoft-float.  */
   if (TARGET_NO_FLOAT)
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 69b21a75e62..b48f5f55513 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5670,6 +5670,23 @@  command line (@pxref{MIPS Options}).
 This function attribute instructs the compiler to generate a hazard barrier
 return that clears all execution and instruction hazards while returning,
 instead of generating a normal return instruction.
+
+@item code_readable
+@cindex @code{code_readable} function attribute, MIPS
+For MIPS targets that support PC-relative addressing modes, this attribute
+can be used to control how an object is addressed.  The attribute takes
+a single optional argument:
+
+@table @samp
+@item no
+The function should not read the instruction stream as data.
+@item yes
+The function can read the instruction stream as data.
+@item pcrel
+The function can read the instruction stream in a pc-relative mode.
+@end table
+
+If there is no argument supplied, the default of @code{"yes"} applies.
 @end table
 
 @node MSP430 Function Attributes
diff --git a/gcc/testsuite/gcc.target/mips/code-readable-attr-1.c b/gcc/testsuite/gcc.target/mips/code-readable-attr-1.c
new file mode 100644
index 00000000000..4ccb27c8bde
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/code-readable-attr-1.c
@@ -0,0 +1,51 @@ 
+/* { dg-options "(-mips16) -mgp32 addressing=absolute" } */
+/* { dg-skip-if ".half requires -O" { *-*-* } { "-O0" } { "" } } */
+
+volatile int x1;
+volatile int x2;
+volatile int x3;
+volatile int x4;
+volatile int x5;
+volatile int x6;
+volatile int x7;
+volatile int x8;
+volatile int x9;
+volatile int x10;
+volatile int x11;
+
+MIPS16 __attribute__ ((code_readable)) int
+foo (int i, volatile int *x)
+{
+  switch (i)
+    {
+    case 1: return x1 + x[0];
+    case 2: return x2 + x[1];
+    case 3: return x3 + x[2];
+    case 4: return x4 + x[3];
+    case 5: return x5 + x[4];
+    case 6: return x6 + x[5];
+    case 7: return x7 + x[6];
+    case 8: return x8 + x[7];
+    case 9: return x9 + x[8];
+    case 10: return x10 + x[9];
+    case 11: return x11 + x[10];
+    default: return 0;
+    }
+}
+
+extern int k[];
+
+MIPS16 __attribute__ ((code_readable)) int *
+bar (void)
+{
+  return k;
+}
+
+/* { dg-final { scan-assembler "\tla\t" } } */
+/* { dg-final { scan-assembler "\t\\.half\t" } } */
+/* { dg-final { scan-assembler-not "%hi\\(\[^)\]*L" } } */
+/* { dg-final { scan-assembler-not "%lo\\(\[^)\]*L" } } */
+
+/* { dg-final { scan-assembler "\t\\.word\tk\n" } } */
+/* { dg-final { scan-assembler-not "%hi\\(k\\)" } } */
+/* { dg-final { scan-assembler-not "%lo\\(k\\)" } } */
diff --git a/gcc/testsuite/gcc.target/mips/code-readable-attr-2.c b/gcc/testsuite/gcc.target/mips/code-readable-attr-2.c
new file mode 100644
index 00000000000..c7dd5113359
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/code-readable-attr-2.c
@@ -0,0 +1,49 @@ 
+/* { dg-options "(-mips16) -mgp32 addressing=absolute" } */
+
+volatile int x1;
+volatile int x2;
+volatile int x3;
+volatile int x4;
+volatile int x5;
+volatile int x6;
+volatile int x7;
+volatile int x8;
+volatile int x9;
+volatile int x10;
+volatile int x11;
+
+MIPS16 __attribute__((code_readable ("pcrel"))) int
+foo (int i, volatile int *x)
+{
+  switch (i)
+    {
+    case 1: return x1 + x[0];
+    case 2: return x2 + x[1];
+    case 3: return x3 + x[2];
+    case 4: return x4 + x[3];
+    case 5: return x5 + x[4];
+    case 6: return x6 + x[5];
+    case 7: return x7 + x[6];
+    case 8: return x8 + x[7];
+    case 9: return x9 + x[8];
+    case 10: return x10 + x[9];
+    case 11: return x11 + x[10];
+    default: return 0;
+    }
+}
+
+extern int k[];
+
+MIPS16 __attribute__((code_readable ("pcrel"))) int *
+bar (void)
+{
+  return k;
+}
+
+/* { dg-final { scan-assembler-not "\tla\t" } } */
+/* { dg-final { scan-assembler-not "\t\\.half\t" } } */
+/* { dg-final { scan-assembler "\t\\.word\t\[^\n\]*L" } } */
+
+/* { dg-final { scan-assembler "\t\\.word\tk\n" } } */
+/* { dg-final { scan-assembler-not "%hi\\(k\\)" } } */
+/* { dg-final { scan-assembler-not "%lo\\(k\\)" } } */
diff --git a/gcc/testsuite/gcc.target/mips/code-readable-attr-3.c b/gcc/testsuite/gcc.target/mips/code-readable-attr-3.c
new file mode 100644
index 00000000000..99c13a901bc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/code-readable-attr-3.c
@@ -0,0 +1,50 @@ 
+/* { dg-options "(-mips16) -mgp32 addressing=absolute" } */
+
+volatile int x1;
+volatile int x2;
+volatile int x3;
+volatile int x4;
+volatile int x5;
+volatile int x6;
+volatile int x7;
+volatile int x8;
+volatile int x9;
+volatile int x10;
+volatile int x11;
+
+MIPS16 __attribute__((code_readable ("no"))) int
+foo (int i, volatile int *x)
+{
+  switch (i)
+    {
+    case 1: return x1 + x[0];
+    case 2: return x2 + x[1];
+    case 3: return x3 + x[2];
+    case 4: return x4 + x[3];
+    case 5: return x5 + x[4];
+    case 6: return x6 + x[5];
+    case 7: return x7 + x[6];
+    case 8: return x8 + x[7];
+    case 9: return x9 + x[8];
+    case 10: return x10 + x[9];
+    case 11: return x11 + x[10];
+    default: return 0;
+    }
+}
+
+extern int k[];
+
+MIPS16 __attribute__((code_readable ("no"))) int *
+bar (void)
+{
+  return k;
+}
+
+/* { dg-final { scan-assembler-not "\tla\t" } } */
+/* { dg-final { scan-assembler-not "\t\\.half\t" } } */
+/* { dg-final { scan-assembler "%hi\\(\[^)\]*L" } } */
+/* { dg-final { scan-assembler "%lo\\(\[^)\]*L" } } */
+
+/* { dg-final { scan-assembler-not "\t\\.word\tk\n" } } */
+/* { dg-final { scan-assembler "%hi\\(k\\)" } } */
+/* { dg-final { scan-assembler "%lo\\(k\\)" } } */
diff --git a/gcc/testsuite/gcc.target/mips/code-readable-attr-4.c b/gcc/testsuite/gcc.target/mips/code-readable-attr-4.c
new file mode 100644
index 00000000000..4058ba13810
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/code-readable-attr-4.c
@@ -0,0 +1,51 @@ 
+/* { dg-options "(-mips16) -mabi=eabi -mgp64" } */
+/* { dg-skip-if ".half requires -O" { *-*-* } { "-O0" } { "" } } */
+
+volatile int x1;
+volatile int x2;
+volatile int x3;
+volatile int x4;
+volatile int x5;
+volatile int x6;
+volatile int x7;
+volatile int x8;
+volatile int x9;
+volatile int x10;
+volatile int x11;
+
+MIPS16 __attribute__((code_readable ("yes"))) int
+foo (int i, volatile int *x)
+{
+  switch (i)
+    {
+    case 1: return x1 + x[0];
+    case 2: return x2 + x[1];
+    case 3: return x3 + x[2];
+    case 4: return x4 + x[3];
+    case 5: return x5 + x[4];
+    case 6: return x6 + x[5];
+    case 7: return x7 + x[6];
+    case 8: return x8 + x[7];
+    case 9: return x9 + x[8];
+    case 10: return x10 + x[9];
+    case 11: return x11 + x[10];
+    default: return 0;
+    }
+}
+
+extern int k[];
+
+MIPS16 __attribute__((code_readable ("yes"))) int *
+bar (void)
+{
+  return k;
+}
+
+/* { dg-final { scan-assembler "\tdla\t" } } */
+/* { dg-final { scan-assembler "\t\\.half\t" } } */
+/* { dg-final { scan-assembler-not "%hi\\(\[^)\]*L" } } */
+/* { dg-final { scan-assembler-not "%lo\\(\[^)\]*L" } } */
+
+/* { dg-final { scan-assembler "\t\\.dword\tk\n" } } */
+/* { dg-final { scan-assembler-not "%hi\\(k\\)" } } */
+/* { dg-final { scan-assembler-not "%lo\\(k\\)" } } */
diff --git a/gcc/testsuite/gcc.target/mips/code-readable-attr-5.c b/gcc/testsuite/gcc.target/mips/code-readable-attr-5.c
new file mode 100644
index 00000000000..0a547a9acfc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/code-readable-attr-5.c
@@ -0,0 +1,5 @@ 
+/* { dg-options "(-mips16) isa_rev<=5" } */
+
+ __attribute__((code_readable ("magic"))) int foo () {} /* { dg-warning "argument to 'code_readable' attribute is neither no, pcrel nor yes" } */
+
+ __attribute__((code_readable (1))) int * bar () {} /* { dg-warning "'code_readable' attribute requires a string argument" } */