1// Inferno's libkern/vlop-arm.s
2// https://bitbucket.org/inferno-os/inferno-os/src/default/libkern/vlop-arm.s
3//
4//         Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
5//         Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).  All rights reserved.
6//         Portions Copyright 2009 The Go Authors. All rights reserved.
7//
8// Permission is hereby granted, free of charge, to any person obtaining a copy
9// of this software and associated documentation files (the "Software"), to deal
10// in the Software without restriction, including without limitation the rights
11// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12// copies of the Software, and to permit persons to whom the Software is
13// furnished to do so, subject to the following conditions:
14//
15// The above copyright notice and this permission notice shall be included in
16// all copies or substantial portions of the Software.
17//
18// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
21// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24// THE SOFTWARE.
25
26#include "go_asm.h"
27#include "go_tls.h"
28#include "funcdata.h"
29#include "textflag.h"
30
31// trampoline for _sfloat2. passes LR as arg0 and
32// saves registers R0-R13 and CPSR on the stack. R0-R12 and CPSR flags can
33// be changed by _sfloat2.
34TEXT runtime·_sfloat(SB), NOSPLIT, $68-0 // 4 arg + 14*4 saved regs + cpsr + return value
35	MOVW	R14, 4(R13)
36	MOVW	R0, 8(R13)
37	MOVW	$12(R13), R0
38	MOVM.IA.W	[R1-R12], (R0)
39	MOVW	$72(R13), R1 // correct for frame size
40	MOVW	R1, 60(R13)
41	WORD	$0xe10f1000 // mrs r1, cpsr
42	MOVW	R1, 64(R13)
43	// Disable preemption of this goroutine during _sfloat2 by
44	// m->locks++ and m->locks-- around the call.
45	// Rescheduling this goroutine may cause the loss of the
46	// contents of the software floating point registers in
47	// m->freghi, m->freglo, m->fflag, if the goroutine is moved
48	// to a different m or another goroutine runs on this m.
49	// Rescheduling at ordinary function calls is okay because
50	// all registers are caller save, but _sfloat2 and the things
51	// that it runs are simulating the execution of individual
52	// program instructions, and those instructions do not expect
53	// the floating point registers to be lost.
54	// An alternative would be to move the software floating point
55	// registers into G, but they do not need to be kept at the
56	// usual places a goroutine reschedules (at function calls),
57	// so it would be a waste of 132 bytes per G.
58	MOVW	g_m(g), R8
59	MOVW	m_locks(R8), R1
60	ADD	$1, R1
61	MOVW	R1, m_locks(R8)
62	MOVW	$1, R1
63	MOVW	R1, m_softfloat(R8)
64	BL	runtime·_sfloat2(SB)
65	MOVW	68(R13), R0
66	MOVW	g_m(g), R8
67	MOVW	m_locks(R8), R1
68	SUB	$1, R1
69	MOVW	R1, m_locks(R8)
70	MOVW	$0, R1
71	MOVW	R1, m_softfloat(R8)
72	MOVW	R0, 0(R13)
73	MOVW	64(R13), R1
74	WORD	$0xe128f001	// msr cpsr_f, r1
75	MOVW	$12(R13), R0
76	// Restore R1-R12, R0.
77	MOVM.IA.W	(R0), [R1-R12]
78	MOVW	8(R13), R0
79	RET
80
81// trampoline for _sfloat2 panic.
82// _sfloat2 instructs _sfloat to return here.
83// We need to push a fake saved LR onto the stack,
84// load the signal fault address into LR, and jump
85// to the real sigpanic.
86// This simulates what sighandler does for a memory fault.
87TEXT runtime·_sfloatpanic(SB),NOSPLIT,$-4
88	MOVW	$0, R0
89	MOVW.W	R0, -4(R13)
90	MOVW	g_sigpc(g), LR
91	B	runtime·sigpanic(SB)
92
93// func runtime·udiv(n, d uint32) (q, r uint32)
94// compiler knowns the register usage of this function
95// Reference:
96// Sloss, Andrew et. al; ARM System Developer's Guide: Designing and Optimizing System Software
97// Morgan Kaufmann; 1 edition (April 8, 2004), ISBN 978-1558608740
98#define Rq	R0 // input d, output q
99#define Rr	R1 // input n, output r
100#define Rs	R2 // three temporary variables
101#define RM	R3
102#define Ra	R11
103
104// Be careful: Ra == R11 will be used by the linker for synthesized instructions.
105TEXT runtime·udiv(SB),NOSPLIT,$-4
106	MOVBU	runtime·hardDiv(SB), Ra
107	CMP	$0, Ra
108	BNE	udiv_hardware
109
110	CLZ 	Rq, Rs // find normalizing shift
111	MOVW.S	Rq<<Rs, Ra
112	MOVW	$fast_udiv_tab<>-64(SB), RM
113	ADD.NE	Ra>>25, RM, Ra // index by most significant 7 bits of divisor
114	MOVBU.NE	(Ra), Ra
115
116	SUB.S	$7, Rs
117	RSB 	$0, Rq, RM // M = -q
118	MOVW.PL	Ra<<Rs, Rq
119
120	// 1st Newton iteration
121	MUL.PL	RM, Rq, Ra // a = -q*d
122	BMI 	udiv_by_large_d
123	MULAWT	Ra, Rq, Rq, Rq // q approx q-(q*q*d>>32)
124	TEQ 	RM->1, RM // check for d=0 or d=1
125
126	// 2nd Newton iteration
127	MUL.NE	RM, Rq, Ra
128	MOVW.NE	$0, Rs
129	MULAL.NE Rq, Ra, (Rq,Rs)
130	BEQ 	udiv_by_0_or_1
131
132	// q now accurate enough for a remainder r, 0<=r<3*d
133	MULLU	Rq, Rr, (Rq,Rs) // q = (r * q) >> 32
134	ADD 	RM, Rr, Rr // r = n - d
135	MULA	RM, Rq, Rr, Rr // r = n - (q+1)*d
136
137	// since 0 <= n-q*d < 3*d; thus -d <= r < 2*d
138	CMN 	RM, Rr // t = r-d
139	SUB.CS	RM, Rr, Rr // if (t<-d || t>=0) r=r+d
140	ADD.CC	$1, Rq
141	ADD.PL	RM<<1, Rr
142	ADD.PL	$2, Rq
143	RET
144
145// use hardware divider
146udiv_hardware:
147	DIVUHW	Rq, Rr, Rs
148	MUL	Rs, Rq, RM
149	RSB	Rr, RM, Rr
150	MOVW	Rs, Rq
151	RET
152
153udiv_by_large_d:
154	// at this point we know d>=2^(31-6)=2^25
155	SUB 	$4, Ra, Ra
156	RSB 	$0, Rs, Rs
157	MOVW	Ra>>Rs, Rq
158	MULLU	Rq, Rr, (Rq,Rs)
159	MULA	RM, Rq, Rr, Rr
160
161	// q now accurate enough for a remainder r, 0<=r<4*d
162	CMN 	Rr>>1, RM // if(r/2 >= d)
163	ADD.CS	RM<<1, Rr
164	ADD.CS	$2, Rq
165	CMN 	Rr, RM
166	ADD.CS	RM, Rr
167	ADD.CS	$1, Rq
168	RET
169
170udiv_by_0_or_1:
171	// carry set if d==1, carry clear if d==0
172	BCC udiv_by_0
173	MOVW	Rr, Rq
174	MOVW	$0, Rr
175	RET
176
177udiv_by_0:
178	MOVW	$runtime·panicdivide(SB), R11
179	B	(R11)
180
181// var tab [64]byte
182// tab[0] = 255; for i := 1; i <= 63; i++ { tab[i] = (1<<14)/(64+i) }
183// laid out here as little-endian uint32s
184DATA fast_udiv_tab<>+0x00(SB)/4, $0xf4f8fcff
185DATA fast_udiv_tab<>+0x04(SB)/4, $0xe6eaedf0
186DATA fast_udiv_tab<>+0x08(SB)/4, $0xdadde0e3
187DATA fast_udiv_tab<>+0x0c(SB)/4, $0xcfd2d4d7
188DATA fast_udiv_tab<>+0x10(SB)/4, $0xc5c7cacc
189DATA fast_udiv_tab<>+0x14(SB)/4, $0xbcbec0c3
190DATA fast_udiv_tab<>+0x18(SB)/4, $0xb4b6b8ba
191DATA fast_udiv_tab<>+0x1c(SB)/4, $0xacaeb0b2
192DATA fast_udiv_tab<>+0x20(SB)/4, $0xa5a7a8aa
193DATA fast_udiv_tab<>+0x24(SB)/4, $0x9fa0a2a3
194DATA fast_udiv_tab<>+0x28(SB)/4, $0x999a9c9d
195DATA fast_udiv_tab<>+0x2c(SB)/4, $0x93949697
196DATA fast_udiv_tab<>+0x30(SB)/4, $0x8e8f9092
197DATA fast_udiv_tab<>+0x34(SB)/4, $0x898a8c8d
198DATA fast_udiv_tab<>+0x38(SB)/4, $0x85868788
199DATA fast_udiv_tab<>+0x3c(SB)/4, $0x81828384
200GLOBL fast_udiv_tab<>(SB), RODATA, $64
201
202// The linker will pass numerator in R8
203#define Rn R8
204// The linker expects the result in RTMP
205#define RTMP R11
206
207TEXT runtime·_divu(SB), NOSPLIT, $16-0
208	// It's not strictly true that there are no local pointers.
209	// It could be that the saved registers Rq, Rr, Rs, and Rm
210	// contain pointers. However, the only way this can matter
211	// is if the stack grows (which it can't, udiv is nosplit)
212	// or if a fault happens and more frames are added to
213	// the stack due to deferred functions.
214	// In the latter case, the stack can grow arbitrarily,
215	// and garbage collection can happen, and those
216	// operations care about pointers, but in that case
217	// the calling frame is dead, and so are the saved
218	// registers. So we can claim there are no pointers here.
219	NO_LOCAL_POINTERS
220	MOVW	Rq, 4(R13)
221	MOVW	Rr, 8(R13)
222	MOVW	Rs, 12(R13)
223	MOVW	RM, 16(R13)
224
225	MOVW	Rn, Rr			/* numerator */
226	MOVW	g_m(g), Rq
227	MOVW	m_divmod(Rq), Rq	/* denominator */
228	BL  	runtime·udiv(SB)
229	MOVW	Rq, RTMP
230	MOVW	4(R13), Rq
231	MOVW	8(R13), Rr
232	MOVW	12(R13), Rs
233	MOVW	16(R13), RM
234	RET
235
236TEXT runtime·_modu(SB), NOSPLIT, $16-0
237	NO_LOCAL_POINTERS
238	MOVW	Rq, 4(R13)
239	MOVW	Rr, 8(R13)
240	MOVW	Rs, 12(R13)
241	MOVW	RM, 16(R13)
242
243	MOVW	Rn, Rr			/* numerator */
244	MOVW	g_m(g), Rq
245	MOVW	m_divmod(Rq), Rq	/* denominator */
246	BL  	runtime·udiv(SB)
247	MOVW	Rr, RTMP
248	MOVW	4(R13), Rq
249	MOVW	8(R13), Rr
250	MOVW	12(R13), Rs
251	MOVW	16(R13), RM
252	RET
253
254TEXT runtime·_div(SB),NOSPLIT,$16-0
255	NO_LOCAL_POINTERS
256	MOVW	Rq, 4(R13)
257	MOVW	Rr, 8(R13)
258	MOVW	Rs, 12(R13)
259	MOVW	RM, 16(R13)
260	MOVW	Rn, Rr			/* numerator */
261	MOVW	g_m(g), Rq
262	MOVW	m_divmod(Rq), Rq	/* denominator */
263	CMP 	$0, Rr
264	BGE 	d1
265	RSB 	$0, Rr, Rr
266	CMP 	$0, Rq
267	BGE 	d2
268	RSB 	$0, Rq, Rq
269d0:
270	BL  	runtime·udiv(SB)  	/* none/both neg */
271	MOVW	Rq, RTMP
272	B	out1
273d1:
274	CMP 	$0, Rq
275	BGE 	d0
276	RSB 	$0, Rq, Rq
277d2:
278	BL  	runtime·udiv(SB)  	/* one neg */
279	RSB	$0, Rq, RTMP
280out1:
281	MOVW	4(R13), Rq
282	MOVW	8(R13), Rr
283	MOVW	12(R13), Rs
284	MOVW	16(R13), RM
285	RET
286
287TEXT runtime·_mod(SB),NOSPLIT,$16-0
288	NO_LOCAL_POINTERS
289	MOVW	Rq, 4(R13)
290	MOVW	Rr, 8(R13)
291	MOVW	Rs, 12(R13)
292	MOVW	RM, 16(R13)
293	MOVW	Rn, Rr			/* numerator */
294	MOVW	g_m(g), Rq
295	MOVW	m_divmod(Rq), Rq	/* denominator */
296	CMP 	$0, Rq
297	RSB.LT	$0, Rq, Rq
298	CMP 	$0, Rr
299	BGE 	m1
300	RSB 	$0, Rr, Rr
301	BL  	runtime·udiv(SB)  	/* neg numerator */
302	RSB 	$0, Rr, RTMP
303	B   	out
304m1:
305	BL  	runtime·udiv(SB)  	/* pos numerator */
306	MOVW	Rr, RTMP
307out:
308	MOVW	4(R13), Rq
309	MOVW	8(R13), Rr
310	MOVW	12(R13), Rs
311	MOVW	16(R13), RM
312	RET
313
314// _mul64by32 and _div64by32 not implemented on arm
315TEXT runtime·_mul64by32(SB), NOSPLIT, $0
316	MOVW	$0, R0
317	MOVW	(R0), R1 // crash
318
319TEXT runtime·_div64by32(SB), NOSPLIT, $0
320	MOVW	$0, R0
321	MOVW	(R0), R1 // crash
322