1 /* THREAD_* accessors.  x86_64 version.
2    Copyright (C) 2002-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 /* Read member of the thread descriptor directly.  */
20 # define THREAD_GETMEM(descr, member) \
21   ({ __typeof (descr->member) __value;					      \
22      _Static_assert (sizeof (__value) == 1				      \
23 		     || sizeof (__value) == 4				      \
24 		     || sizeof (__value) == 8,				      \
25 		     "size of per-thread data");			      \
26      if (sizeof (__value) == 1)						      \
27        asm volatile ("movb %%fs:%P2,%b0"				      \
28 		     : "=q" (__value)					      \
29 		     : "0" (0), "i" (offsetof (struct pthread, member)));     \
30      else if (sizeof (__value) == 4)					      \
31        asm volatile ("movl %%fs:%P1,%0"					      \
32 		     : "=r" (__value)					      \
33 		     : "i" (offsetof (struct pthread, member)));	      \
34      else /* 8 */								      \
35        {								      \
36 	 asm volatile ("movq %%fs:%P1,%q0"				      \
37 		       : "=r" (__value)					      \
38 		       : "i" (offsetof (struct pthread, member)));	      \
39        }								      \
40      __value; })
41 
42 /* THREAD_GETMEM already forces a read.  */
43 #define THREAD_GETMEM_VOLATILE(descr, member) THREAD_GETMEM (descr, member)
44 
45 /* Same as THREAD_GETMEM, but the member offset can be non-constant.  */
46 # define THREAD_GETMEM_NC(descr, member, idx) \
47   ({ __typeof (descr->member[0]) __value;				      \
48      _Static_assert (sizeof (__value) == 1				      \
49 		     || sizeof (__value) == 4				      \
50 		     || sizeof (__value) == 8,				      \
51 		     "size of per-thread data");			      \
52      if (sizeof (__value) == 1)						      \
53        asm volatile ("movb %%fs:%P2(%q3),%b0"				      \
54 		     : "=q" (__value)					      \
55 		     : "0" (0), "i" (offsetof (struct pthread, member[0])),   \
56 		       "r" (idx));					      \
57      else if (sizeof (__value) == 4)					      \
58        asm volatile ("movl %%fs:%P1(,%q2,4),%0"				      \
59 		     : "=r" (__value)					      \
60 		     : "i" (offsetof (struct pthread, member[0])), "r" (idx));\
61      else /* 8 */							      \
62        {								      \
63 	 asm volatile ("movq %%fs:%P1(,%q2,8),%q0"			      \
64 		       : "=r" (__value)					      \
65 		       : "i" (offsetof (struct pthread, member[0])),	      \
66 			 "r" (idx));					      \
67        }								      \
68      __value; })
69 
70 
71 /* Loading addresses of objects on x86-64 needs to be treated special
72    when generating PIC code.  */
73 #ifdef __pic__
74 # define IMM_MODE "nr"
75 #else
76 # define IMM_MODE "ir"
77 #endif
78 
79 
80 /* Set member of the thread descriptor directly.  */
81 # define THREAD_SETMEM(descr, member, value) \
82   ({									      \
83      _Static_assert (sizeof (descr->member) == 1			      \
84 		     || sizeof (descr->member) == 4			      \
85 		     || sizeof (descr->member) == 8,			      \
86 		     "size of per-thread data");			      \
87      if (sizeof (descr->member) == 1)					      \
88        asm volatile ("movb %b0,%%fs:%P1" :				      \
89 		     : "iq" (value),					      \
90 		       "i" (offsetof (struct pthread, member)));	      \
91      else if (sizeof (descr->member) == 4)				      \
92        asm volatile ("movl %0,%%fs:%P1" :				      \
93 		     : IMM_MODE (value),				      \
94 		       "i" (offsetof (struct pthread, member)));	      \
95      else /* 8 */							      \
96        {								      \
97 	 /* Since movq takes a signed 32-bit immediate or a register source   \
98 	    operand, use "er" constraint for 32-bit signed integer constant   \
99 	    or register.  */						      \
100 	 asm volatile ("movq %q0,%%fs:%P1" :				      \
101 		       : "er" ((uint64_t) cast_to_integer (value)),	      \
102 			 "i" (offsetof (struct pthread, member)));	      \
103        }})
104 
105 
106 /* Same as THREAD_SETMEM, but the member offset can be non-constant.  */
107 # define THREAD_SETMEM_NC(descr, member, idx, value) \
108   ({									      \
109      _Static_assert (sizeof (descr->member[0]) == 1			      \
110 		     || sizeof (descr->member[0]) == 4			      \
111 		     || sizeof (descr->member[0]) == 8,			      \
112 		     "size of per-thread data");			      \
113      if (sizeof (descr->member[0]) == 1)				      \
114        asm volatile ("movb %b0,%%fs:%P1(%q2)" :				      \
115 		     : "iq" (value),					      \
116 		       "i" (offsetof (struct pthread, member[0])),	      \
117 		       "r" (idx));					      \
118      else if (sizeof (descr->member[0]) == 4)				      \
119        asm volatile ("movl %0,%%fs:%P1(,%q2,4)" :			      \
120 		     : IMM_MODE (value),				      \
121 		       "i" (offsetof (struct pthread, member[0])),	      \
122 		       "r" (idx));					      \
123      else /* 8 */							      \
124        {								      \
125 	 /* Since movq takes a signed 32-bit immediate or a register source   \
126 	    operand, use "er" constraint for 32-bit signed integer constant   \
127 	    or register.  */						      \
128 	 asm volatile ("movq %q0,%%fs:%P1(,%q2,8)" :			      \
129 		       : "er" ((uint64_t) cast_to_integer (value)),	      \
130 			 "i" (offsetof (struct pthread, member[0])),	      \
131 			 "r" (idx));					      \
132        }})
133