about summary refs log tree commit diff
path: root/sysdeps/loongarch/dl-tlsdesc-dynamic.h
blob: d10f4a8800ce22a2b52c4cb9ca62c2b7c6e48808 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/* Thread-local storage handling in the ELF dynamic linker.
   LoongArch version.
   Copyright (C) 2024 Free Software Foundation, Inc.

   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <https://www.gnu.org/licenses/>.  */

#define FRAME_SIZE	  (-((-14 * SZREG) & ALMASK))
#define FRAME_SIZE_LSX	  (-((-32 * SZVREG) & ALMASK))
#define FRAME_SIZE_LASX	  (-((-32 * SZXREG) & ALMASK))
#define FRAME_SIZE_FLOAT  (-((-24 * SZFREG) & ALMASK))

	/* Handler for dynamic TLS symbols.
	   Prototype:
	   _dl_tlsdesc_dynamic (tlsdesc *) ;

	   The second word of the descriptor points to a
	   tlsdesc_dynamic_arg structure.

	   Returns the offset between the thread pointer and the
	   object referenced by the argument.

	   ptrdiff_t
	   _dl_tlsdesc_dynamic (struct tlsdesc *tdp)
	   {
	     struct tlsdesc_dynamic_arg *td = tdp->arg;
	     dtv_t *dtv = *(dtv_t **)((char *)__thread_pointer - SIZE_OF_TCB);
	     if (__glibc_likely (td->gen_count <= dtv[0].counter
		&& (dtv[td->tlsinfo.ti_module].pointer.val
		    != TLS_DTV_UNALLOCATED),
		1))
	       return dtv[td->tlsinfo.ti_module].pointer.val
		+ td->tlsinfo.ti_offset
		- __thread_pointer;

	     return ___tls_get_addr (&td->tlsinfo) - __thread_pointer;
	   }  */
	.hidden _dl_tlsdesc_dynamic
	.global	_dl_tlsdesc_dynamic
	.type	_dl_tlsdesc_dynamic,%function
	cfi_startproc
	.align 2
_dl_tlsdesc_dynamic:
	/* Save just enough registers to support fast path, if we fall
	   into slow path we will save additional registers.  */
	ADDI	sp, sp, -32
	cfi_adjust_cfa_offset (32)
	REG_S	t0, sp, 0
	REG_S	t1, sp, 8
	REG_S	t2, sp, 16
	cfi_rel_offset (12, 0)
	cfi_rel_offset (13, 8)
	cfi_rel_offset (14, 16)

/* Runtime Storage Layout of Thread-Local Storage
   TP point to the start of TLS block.

				      dtv
Low address	TCB ----------------> dtv0(counter)
	 TP -->	static_block0  <----- dtv1
		static_block1  <----- dtv2
		static_block2  <----- dtv3
		dynamic_block0 <----- dtv4
Hign address	dynamic_block1 <----- dtv5  */

	REG_L	t0, tp, -SIZE_OF_TCB	  /* t0 = dtv */
	REG_L	a0, a0, TLSDESC_ARG	  /* a0(td) = tdp->arg */
	REG_L	t1, a0, TLSDESC_GEN_COUNT /* t1 = td->gen_count */
	REG_L	t2, t0, DTV_COUNTER	  /* t2 = dtv[0].counter */
	/* If dtv[0].counter < td->gen_count, goto slow path.  */
	bltu	t2, t1, .Lslow

	REG_L	t1, a0, TLSDESC_MODID /* t1 = td->tlsinfo.ti_module */
	/* t1 = t1 * sizeof(dtv_t) = t1 * (2 * sizeof(void*)) */
	slli.d	t1, t1, 4
	add.d	t1, t1, t0  /* t1 = dtv[td->tlsinfo.ti_module] */
	REG_L	t1, t1, 0   /* t1 = dtv[td->tlsinfo.ti_module].pointer.val */
	li.d	t2, TLS_DTV_UNALLOCATED
	/* If dtv[td->tlsinfo.ti_module].pointer.val is TLS_DTV_UNALLOCATED,
	   goto slow path.  */
	beq	t1, t2, .Lslow

	cfi_remember_state
	REG_L	t2, a0, TLSDESC_MODOFF	/* t2 = td->tlsinfo.ti_offset */
	/* dtv[td->tlsinfo.ti_module].pointer.val + td->tlsinfo.ti_offset */
	add.d	a0, t1, t2
.Lret:
	sub.d	a0, a0, tp
	REG_L	t0, sp, 0
	REG_L	t1, sp, 8
	REG_L	t2, sp, 16
	ADDI	sp, sp, 32
	cfi_adjust_cfa_offset (-32)
	RET

.Lslow:
	/* This is the slow path.  We need to call __tls_get_addr() which
	   means we need to save and restore all the register that the
	   callee will trash.  */

	/* Save the remaining registers that we must treat as caller save.  */
	cfi_restore_state
	ADDI	sp, sp, -FRAME_SIZE
	cfi_adjust_cfa_offset (FRAME_SIZE)
	REG_S	ra, sp, 0 * SZREG
	REG_S	a1, sp, 1 * SZREG
	REG_S	a2, sp, 2 * SZREG
	REG_S	a3, sp, 3 * SZREG
	REG_S	a4, sp, 4 * SZREG
	REG_S	a5, sp, 5 * SZREG
	REG_S	a6, sp, 6 * SZREG
	REG_S	a7, sp, 7 * SZREG
	REG_S	t3, sp, 8 * SZREG
	REG_S	t4, sp, 9 * SZREG
	REG_S	t5, sp, 10 * SZREG
	REG_S	t6, sp, 11 * SZREG
	REG_S	t7, sp, 12 * SZREG
	REG_S	t8, sp, 13 * SZREG
	cfi_rel_offset (1, 0 * SZREG)
	cfi_rel_offset (5, 1 * SZREG)
	cfi_rel_offset (6, 2 * SZREG)
	cfi_rel_offset (7, 3 * SZREG)
	cfi_rel_offset (8, 4 * SZREG)
	cfi_rel_offset (9, 5 * SZREG)
	cfi_rel_offset (10, 6 * SZREG)
	cfi_rel_offset (11, 7 * SZREG)
	cfi_rel_offset (15, 8 * SZREG)
	cfi_rel_offset (16, 9 * SZREG)
	cfi_rel_offset (17, 10 * SZREG)
	cfi_rel_offset (18, 11 * SZREG)
	cfi_rel_offset (19, 12 * SZREG)
	cfi_rel_offset (20, 13 * SZREG)

#ifndef __loongarch_soft_float

	/* Save fcsr0 register.
	   Only one physical fcsr0 register, fcsr1-fcsr3 are aliases
	   of some fields in fcsr0.  */
	movfcsr2gr  t0, fcsr0
	st.w	t0, sp, FRAME_SIZE + 24 /* Use the spare slot above t2.  */

#ifdef USE_LASX
  #define V_REG_S xvst
  #define V_REG_L xvld
  #define V_SPACE FRAME_SIZE_LASX
  #define V_REG(n) $xr##n
  #define V_REGS 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,  \
		 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
  #define V_REGSZ SZXREG
#elif defined USE_LSX
  #define V_REG_S vst
  #define V_REG_L vld
  #define V_SPACE FRAME_SIZE_LSX
  #define V_REG(n) $vr##n
  #define V_REGS 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,  \
		 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
  #define V_REGSZ SZVREG
#else
  #define V_REG_S fst.d
  #define V_REG_L fld.d
  #define V_SPACE FRAME_SIZE_FLOAT
  #define V_REG(n) $f##n
  #define V_REGS 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23
  #define V_REGSZ SZFREG
#endif

	ADDI	sp, sp, -V_SPACE
	cfi_adjust_cfa_offset (V_SPACE)
	.irp	i,V_REGS
        V_REG_S	V_REG(\i), sp, \i * V_REGSZ
	.endr

#endif /* #ifndef __loongarch_soft_float */

	bl	HIDDEN_JUMPTARGET(__tls_get_addr)
	ADDI	a0, a0, -TLS_DTV_OFFSET

#ifndef __loongarch_soft_float

	.irp	i,V_REGS
	V_REG_L	V_REG(\i), sp, \i * V_REGSZ
	.endr
	ADDI	sp, sp, V_SPACE
	cfi_adjust_cfa_offset (-V_SPACE)

	/* Restore fcsr0 register.  */
	ld.w	t0, sp, FRAME_SIZE + 24
	movgr2fcsr  fcsr0, t0

#endif /* #ifndef __loongarch_soft_float */

	REG_L	ra, sp, 0 * SZREG
	REG_L	a1, sp, 1 * SZREG
	REG_L	a2, sp, 2 * SZREG
	REG_L	a3, sp, 3 * SZREG
	REG_L	a4, sp, 4 * SZREG
	REG_L	a5, sp, 5 * SZREG
	REG_L	a6, sp, 6 * SZREG
	REG_L	a7, sp, 7 * SZREG
	REG_L	t3, sp, 8 * SZREG
	REG_L	t4, sp, 9 * SZREG
	REG_L	t5, sp, 10 * SZREG
	REG_L	t6, sp, 11 * SZREG
	REG_L	t7, sp, 12 * SZREG
	REG_L	t8, sp, 13 * SZREG
	ADDI	sp, sp, FRAME_SIZE
	cfi_adjust_cfa_offset (-FRAME_SIZE)

	b	.Lret
	cfi_endproc
	.size	_dl_tlsdesc_dynamic, .-_dl_tlsdesc_dynamic
	.hidden HIDDEN_JUMPTARGET(__tls_get_addr)