diff mbox

[alpha] Fix GPREL16 relocation error building glibc

Message ID 504E2F55.9040904@redhat.com
State New
Headers show

Commit Message

Richard Henderson Sept. 10, 2012, 6:20 p.m. UTC
There's an access within glibc wherein we've forced an important global variable into the .sdata section (so that accesses to its members can use 16-bit relocations), but an array access gets constant-folded such that we produce an offset well well outside of a 16-bit range.

A test case that must be visually inspected looks like the following.  I'm not certain how to turn this into a portable link-time test.  But considering that other ports also handle SYMBOL_REF_SMALL_DATA, it does seem like something portable would be nice.  Ideas?  Or should I just drop this in as an alpha specific test?

extern int x[10] __attribute__((visibility("hidden"), section(".sdata")));

int foo(int y)
{
  return x[y-100000];
}


Committed the patch itself to mainline and 4.7 branch.



r~
diff mbox

Patch

diff --git a/gcc/config/alpha/predicates.md b/gcc/config/alpha/predicates.md
index 598742f..0a1885b 100644
--- a/gcc/config/alpha/predicates.md
+++ b/gcc/config/alpha/predicates.md
@@ -328,26 +328,50 @@ 
 (define_predicate "small_symbolic_operand"
   (match_code "const,symbol_ref")
 {
+  HOST_WIDE_INT ofs = 0, max_ofs = 0;
+
   if (! TARGET_SMALL_DATA)
-    return 0;
+    return false;
 
   if (GET_CODE (op) == CONST
       && GET_CODE (XEXP (op, 0)) == PLUS
       && CONST_INT_P (XEXP (XEXP (op, 0), 1)))
-    op = XEXP (XEXP (op, 0), 0);
+    {
+      ofs = INTVAL (XEXP (XEXP (op, 0), 1));
+      op = XEXP (XEXP (op, 0), 0);
+    }
 
   if (GET_CODE (op) != SYMBOL_REF)
-    return 0;
+    return false;
 
   /* ??? There's no encode_section_info equivalent for the rtl
      constant pool, so SYMBOL_FLAG_SMALL never gets set.  */
   if (CONSTANT_POOL_ADDRESS_P (op))
-    return GET_MODE_SIZE (get_pool_mode (op)) <= g_switch_value;
+    {
+      max_ofs = GET_MODE_SIZE (get_pool_mode (op));
+      if (max_ofs > g_switch_value)
+	return false;
+    }
+  else if (SYMBOL_REF_LOCAL_P (op)
+	    && SYMBOL_REF_SMALL_P (op)
+	    && !SYMBOL_REF_WEAK (op)
+	    && !SYMBOL_REF_TLS_MODEL (op))
+    {
+      if (SYMBOL_REF_DECL (op))
+        max_ofs = tree_low_cst (DECL_SIZE_UNIT (SYMBOL_REF_DECL (op)), 1);
+    }
+  else
+    return false;
 
-  return (SYMBOL_REF_LOCAL_P (op)
-	  && SYMBOL_REF_SMALL_P (op)
-	  && !SYMBOL_REF_WEAK (op)
-	  && !SYMBOL_REF_TLS_MODEL (op));
+  /* Given that we know that the GP is always 8 byte aligned, we can
+     always adjust by 7 without overflowing.  */
+  if (max_ofs < 8)
+    max_ofs = 8;
+
+  /* Since we know this is an object in a small data section, we know the
+     entire section is addressable via GP.  We don't know where the section
+     boundaries are, but we know the entire object is within.  */
+  return IN_RANGE (ofs, 0, max_ofs - 1);
 })
 
 ;; Return true if OP is a SYMBOL_REF or CONST referencing a variable