summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2015-12-01 15:18:30 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2015-12-01 15:18:30 +0000
commitc4ed28f037019ca260a3072f1055dec3e602b153 (patch)
tree950a6c79ed05d7f141dda7ca571aeed3b37ac887
parent30000b8919882a928a596832f53b03b74c6c3ec5 (diff)
Fix __sync_val_compare_and_swap_8() on i386 for code compiled with -fPIC.
In some cases GCC would generate a cmpxchg8b instruction with a memory reference that used %ebx. This is wrong (and will almost certainly result in SIGSEGV). This fix uses a new memory constraint "W" to prevent the use of %ebx in this case. This differs from the approach taken by upstream so there are no GPLv3 issues here. Fixes the Mesa i965 dri module on i386. ok jsg@
-rw-r--r--gnu/gcc/gcc/config/i386/constraints.md4
-rw-r--r--gnu/gcc/gcc/config/i386/i386.c28
-rw-r--r--gnu/gcc/gcc/config/i386/sync.md4
3 files changed, 34 insertions, 2 deletions
diff --git a/gnu/gcc/gcc/config/i386/constraints.md b/gnu/gcc/gcc/config/i386/constraints.md
index 0ab4995f3b8..149a69878b4 100644
--- a/gnu/gcc/gcc/config/i386/constraints.md
+++ b/gnu/gcc/gcc/config/i386/constraints.md
@@ -150,3 +150,7 @@
to fit that range (for immediate operands in zero-extending x86-64
instructions)."
(match_operand 0 "x86_64_zext_immediate_operand"))
+
+(define_memory_constraint "W"
+ "CMPXCHG8B memory reference."
+ (match_test "cmpxchg8b_mem_constraint (op)"))
diff --git a/gnu/gcc/gcc/config/i386/i386.c b/gnu/gcc/gcc/config/i386/i386.c
index 6fab6d81312..6f77b9ba5a7 100644
--- a/gnu/gcc/gcc/config/i386/i386.c
+++ b/gnu/gcc/gcc/config/i386/i386.c
@@ -4719,6 +4719,34 @@ standard_sse_constant_opcode (rtx insn, rtx x)
gcc_unreachable ();
}
+int
+cmpxchg8b_mem_constraint (rtx op)
+{
+ struct ix86_address parts;
+
+ if (TARGET_64BIT || !flag_pic)
+ return 1;
+
+ if (GET_CODE (op) != MEM)
+ return 0;
+ if (!ix86_decompose_address (XEXP (op, 0), &parts))
+ return 0;
+
+ if (parts.base && GET_CODE (parts.base) == SUBREG)
+ parts.base = SUBREG_REG (parts.base);
+ if (parts.index && GET_CODE (parts.index) == SUBREG)
+ parts.index = SUBREG_REG (parts.index);
+
+ if (parts.base && REG_P (parts.base)
+ && REGNO_REG_CLASS (REGNO (parts.base)) == BREG)
+ return 0;
+ if (parts.index && REG_P (parts.index)
+ && REGNO_REG_CLASS (REGNO (parts.index)) == BREG)
+ return 0;
+
+ return 1;
+}
+
/* Returns 1 if OP contains a symbol reference */
int
diff --git a/gnu/gcc/gcc/config/i386/sync.md b/gnu/gcc/gcc/config/i386/sync.md
index 8c2fdb230b0..30566450fa4 100644
--- a/gnu/gcc/gcc/config/i386/sync.md
+++ b/gnu/gcc/gcc/config/i386/sync.md
@@ -109,7 +109,7 @@
;; are just esi and edi.
(define_insn "*sync_double_compare_and_swapdi_pic"
[(set (match_operand:DI 0 "register_operand" "=A")
- (match_operand:DI 1 "memory_operand" "+m"))
+ (match_operand:DI 1 "memory_operand" "+W"))
(set (match_dup 1)
(unspec_volatile:DI
[(match_dup 1)
@@ -202,7 +202,7 @@
;; operand 3.
(define_insn "*sync_double_compare_and_swap_ccdi_pic"
[(set (match_operand:DI 0 "register_operand" "=A")
- (match_operand:DI 1 "memory_operand" "+m"))
+ (match_operand:DI 1 "memory_operand" "+W"))
(set (match_dup 1)
(unspec_volatile:DI
[(match_dup 1)