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" } */
