Home | History | Annotate | Download | only in runtime
      1 // Inferno's libkern/vlop-arm.s
      2 // http://code.google.com/p/inferno-os/source/browse/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 /* replaced use of R10 by R11 because the former can be the data segment base register */
     32 
     33 TEXT _mulv(SB), NOSPLIT, $0
     34 	MOVW	l0+0(FP), R2	/* l0 */
     35 	MOVW	h0+4(FP), R11	/* h0 */
     36 	MOVW	l1+8(FP), R4	/* l1 */
     37 	MOVW	h1+12(FP), R5	/* h1 */
     38 	MULLU	R4, R2, (R7,R6)
     39 	MUL	R11, R4, R8
     40 	ADD	R8, R7
     41 	MUL	R2, R5, R8
     42 	ADD	R8, R7
     43 	MOVW	R6, ret_lo+16(FP)
     44 	MOVW	R7, ret_hi+20(FP)
     45 	RET
     46 
     47 // trampoline for _sfloat2. passes LR as arg0 and
     48 // saves registers R0-R13 and CPSR on the stack. R0-R12 and CPSR flags can
     49 // be changed by _sfloat2.
     50 TEXT _sfloat(SB), NOSPLIT, $68-0 // 4 arg + 14*4 saved regs + cpsr + return value
     51 	MOVW	R14, 4(R13)
     52 	MOVW	R0, 8(R13)
     53 	MOVW	$12(R13), R0
     54 	MOVM.IA.W	[R1-R12], (R0)
     55 	MOVW	$72(R13), R1 // correct for frame size
     56 	MOVW	R1, 60(R13)
     57 	WORD	$0xe10f1000 // mrs r1, cpsr
     58 	MOVW	R1, 64(R13)
     59 	// Disable preemption of this goroutine during _sfloat2 by
     60 	// m->locks++ and m->locks-- around the call.
     61 	// Rescheduling this goroutine may cause the loss of the
     62 	// contents of the software floating point registers in
     63 	// m->freghi, m->freglo, m->fflag, if the goroutine is moved
     64 	// to a different m or another goroutine runs on this m.
     65 	// Rescheduling at ordinary function calls is okay because
     66 	// all registers are caller save, but _sfloat2 and the things
     67 	// that it runs are simulating the execution of individual
     68 	// program instructions, and those instructions do not expect
     69 	// the floating point registers to be lost.
     70 	// An alternative would be to move the software floating point
     71 	// registers into G, but they do not need to be kept at the
     72 	// usual places a goroutine reschedules (at function calls),
     73 	// so it would be a waste of 132 bytes per G.
     74 	MOVW	g_m(g), R8
     75 	MOVW	m_locks(R8), R1
     76 	ADD	$1, R1
     77 	MOVW	R1, m_locks(R8)
     78 	MOVW	$1, R1
     79 	MOVW	R1, m_softfloat(R8)
     80 	BL	runtime_sfloat2(SB)
     81 	MOVW	68(R13), R0
     82 	MOVW	g_m(g), R8
     83 	MOVW	m_locks(R8), R1
     84 	SUB	$1, R1
     85 	MOVW	R1, m_locks(R8)
     86 	MOVW	$0, R1
     87 	MOVW	R1, m_softfloat(R8)
     88 	MOVW	R0, 0(R13)
     89 	MOVW	64(R13), R1
     90 	WORD	$0xe128f001	// msr cpsr_f, r1
     91 	MOVW	$12(R13), R0
     92 	// Restore R1-R12, R0.
     93 	MOVM.IA.W	(R0), [R1-R12]
     94 	MOVW	8(R13), R0
     95 	RET
     96 
     97 // trampoline for _sfloat2 panic.
     98 // _sfloat2 instructs _sfloat to return here.
     99 // We need to push a fake saved LR onto the stack,
    100 // load the signal fault address into LR, and jump
    101 // to the real sigpanic.
    102 // This simulates what sighandler does for a memory fault.
    103 TEXT runtime_sfloatpanic(SB),NOSPLIT,$-4
    104 	MOVW	$0, R0
    105 	MOVW.W	R0, -4(R13)
    106 	MOVW	g_sigpc(g), LR
    107 	B	runtimesigpanic(SB)
    108 
    109 // func udiv(n, d uint32) (q, r uint32)
    110 // Reference:
    111 // Sloss, Andrew et. al; ARM System Developer's Guide: Designing and Optimizing System Software
    112 // Morgan Kaufmann; 1 edition (April 8, 2004), ISBN 978-1558608740
    113 #define Rq	R0 // input d, output q
    114 #define Rr	R1 // input n, output r
    115 #define Rs	R2 // three temporary variables
    116 #define RM	R3
    117 #define Ra	R11
    118 
    119 // Be careful: Ra == R11 will be used by the linker for synthesized instructions.
    120 TEXT udiv<>(SB),NOSPLIT,$-4
    121 	CLZ 	Rq, Rs // find normalizing shift
    122 	MOVW.S	Rq<<Rs, Ra
    123 	MOVW	$fast_udiv_tab<>-64(SB), RM
    124 	ADD.NE	Ra>>25, RM, Ra // index by most significant 7 bits of divisor
    125 	MOVBU.NE	(Ra), Ra
    126 
    127 	SUB.S	$7, Rs
    128 	RSB 	$0, Rq, RM // M = -q
    129 	MOVW.PL	Ra<<Rs, Rq
    130 
    131 	// 1st Newton iteration
    132 	MUL.PL	RM, Rq, Ra // a = -q*d
    133 	BMI 	udiv_by_large_d
    134 	MULAWT	Ra, Rq, Rq, Rq // q approx q-(q*q*d>>32)
    135 	TEQ 	RM->1, RM // check for d=0 or d=1
    136 
    137 	// 2nd Newton iteration
    138 	MUL.NE	RM, Rq, Ra
    139 	MOVW.NE	$0, Rs
    140 	MULAL.NE Rq, Ra, (Rq,Rs)
    141 	BEQ 	udiv_by_0_or_1
    142 
    143 	// q now accurate enough for a remainder r, 0<=r<3*d
    144 	MULLU	Rq, Rr, (Rq,Rs) // q = (r * q) >> 32
    145 	ADD 	RM, Rr, Rr // r = n - d
    146 	MULA	RM, Rq, Rr, Rr // r = n - (q+1)*d
    147 
    148 	// since 0 <= n-q*d < 3*d; thus -d <= r < 2*d
    149 	CMN 	RM, Rr // t = r-d
    150 	SUB.CS	RM, Rr, Rr // if (t<-d || t>=0) r=r+d
    151 	ADD.CC	$1, Rq
    152 	ADD.PL	RM<<1, Rr
    153 	ADD.PL	$2, Rq
    154 	RET
    155 
    156 udiv_by_large_d:
    157 	// at this point we know d>=2^(31-6)=2^25
    158 	SUB 	$4, Ra, Ra
    159 	RSB 	$0, Rs, Rs
    160 	MOVW	Ra>>Rs, Rq
    161 	MULLU	Rq, Rr, (Rq,Rs)
    162 	MULA	RM, Rq, Rr, Rr
    163 
    164 	// q now accurate enough for a remainder r, 0<=r<4*d
    165 	CMN 	Rr>>1, RM // if(r/2 >= d)
    166 	ADD.CS	RM<<1, Rr
    167 	ADD.CS	$2, Rq
    168 	CMN 	Rr, RM
    169 	ADD.CS	RM, Rr
    170 	ADD.CS	$1, Rq
    171 	RET
    172 
    173 udiv_by_0_or_1:
    174 	// carry set if d==1, carry clear if d==0
    175 	BCC udiv_by_0
    176 	MOVW	Rr, Rq
    177 	MOVW	$0, Rr
    178 	RET
    179 
    180 udiv_by_0:
    181 	MOVW	$runtimepanicdivide(SB), R11
    182 	B	(R11)
    183 
    184 // var tab [64]byte
    185 // tab[0] = 255; for i := 1; i <= 63; i++ { tab[i] = (1<<14)/(64+i) }
    186 // laid out here as little-endian uint32s
    187 DATA fast_udiv_tab<>+0x00(SB)/4, $0xf4f8fcff
    188 DATA fast_udiv_tab<>+0x04(SB)/4, $0xe6eaedf0
    189 DATA fast_udiv_tab<>+0x08(SB)/4, $0xdadde0e3
    190 DATA fast_udiv_tab<>+0x0c(SB)/4, $0xcfd2d4d7
    191 DATA fast_udiv_tab<>+0x10(SB)/4, $0xc5c7cacc
    192 DATA fast_udiv_tab<>+0x14(SB)/4, $0xbcbec0c3
    193 DATA fast_udiv_tab<>+0x18(SB)/4, $0xb4b6b8ba
    194 DATA fast_udiv_tab<>+0x1c(SB)/4, $0xacaeb0b2
    195 DATA fast_udiv_tab<>+0x20(SB)/4, $0xa5a7a8aa
    196 DATA fast_udiv_tab<>+0x24(SB)/4, $0x9fa0a2a3
    197 DATA fast_udiv_tab<>+0x28(SB)/4, $0x999a9c9d
    198 DATA fast_udiv_tab<>+0x2c(SB)/4, $0x93949697
    199 DATA fast_udiv_tab<>+0x30(SB)/4, $0x8e8f9092
    200 DATA fast_udiv_tab<>+0x34(SB)/4, $0x898a8c8d
    201 DATA fast_udiv_tab<>+0x38(SB)/4, $0x85868788
    202 DATA fast_udiv_tab<>+0x3c(SB)/4, $0x81828384
    203 GLOBL fast_udiv_tab<>(SB), RODATA, $64
    204 
    205 // The linker will pass numerator in RTMP, and it also
    206 // expects the result in RTMP
    207 #define RTMP R11
    208 
    209 TEXT _divu(SB), NOSPLIT, $16-0
    210 	// It's not strictly true that there are no local pointers.
    211 	// It could be that the saved registers Rq, Rr, Rs, and Rm
    212 	// contain pointers. However, the only way this can matter
    213 	// is if the stack grows (which it can't, udiv is nosplit)
    214 	// or if a fault happens and more frames are added to
    215 	// the stack due to deferred functions.
    216 	// In the latter case, the stack can grow arbitrarily,
    217 	// and garbage collection can happen, and those
    218 	// operations care about pointers, but in that case
    219 	// the calling frame is dead, and so are the saved
    220 	// registers. So we can claim there are no pointers here.
    221 	NO_LOCAL_POINTERS
    222 	MOVW	Rq, 4(R13)
    223 	MOVW	Rr, 8(R13)
    224 	MOVW	Rs, 12(R13)
    225 	MOVW	RM, 16(R13)
    226 
    227 	MOVW	RTMP, Rr		/* numerator */
    228 	MOVW	g_m(g), Rq
    229 	MOVW	m_divmod(Rq), Rq	/* denominator */
    230 	BL  	udiv<>(SB)
    231 	MOVW	Rq, RTMP
    232 	MOVW	4(R13), Rq
    233 	MOVW	8(R13), Rr
    234 	MOVW	12(R13), Rs
    235 	MOVW	16(R13), RM
    236 	RET
    237 
    238 TEXT _modu(SB), NOSPLIT, $16-0
    239 	NO_LOCAL_POINTERS
    240 	MOVW	Rq, 4(R13)
    241 	MOVW	Rr, 8(R13)
    242 	MOVW	Rs, 12(R13)
    243 	MOVW	RM, 16(R13)
    244 
    245 	MOVW	RTMP, Rr		/* numerator */
    246 	MOVW	g_m(g), Rq
    247 	MOVW	m_divmod(Rq), Rq	/* denominator */
    248 	BL  	udiv<>(SB)
    249 	MOVW	Rr, RTMP
    250 	MOVW	4(R13), Rq
    251 	MOVW	8(R13), Rr
    252 	MOVW	12(R13), Rs
    253 	MOVW	16(R13), RM
    254 	RET
    255 
    256 TEXT _div(SB),NOSPLIT,$16-0
    257 	NO_LOCAL_POINTERS
    258 	MOVW	Rq, 4(R13)
    259 	MOVW	Rr, 8(R13)
    260 	MOVW	Rs, 12(R13)
    261 	MOVW	RM, 16(R13)
    262 	MOVW	RTMP, Rr		/* numerator */
    263 	MOVW	g_m(g), Rq
    264 	MOVW	m_divmod(Rq), Rq	/* denominator */
    265 	CMP 	$0, Rr
    266 	BGE 	d1
    267 	RSB 	$0, Rr, Rr
    268 	CMP 	$0, Rq
    269 	BGE 	d2
    270 	RSB 	$0, Rq, Rq
    271 d0:
    272 	BL  	udiv<>(SB)  		/* none/both neg */
    273 	MOVW	Rq, RTMP
    274 	B		out1
    275 d1:
    276 	CMP 	$0, Rq
    277 	BGE 	d0
    278 	RSB 	$0, Rq, Rq
    279 d2:
    280 	BL  	udiv<>(SB)  		/* one neg */
    281 	RSB		$0, Rq, RTMP
    282 out1:
    283 	MOVW	4(R13), Rq
    284 	MOVW	8(R13), Rr
    285 	MOVW	12(R13), Rs
    286 	MOVW	16(R13), RM
    287 	RET
    288 
    289 TEXT _mod(SB),NOSPLIT,$16-0
    290 	NO_LOCAL_POINTERS
    291 	MOVW	Rq, 4(R13)
    292 	MOVW	Rr, 8(R13)
    293 	MOVW	Rs, 12(R13)
    294 	MOVW	RM, 16(R13)
    295 	MOVW	RTMP, Rr		/* numerator */
    296 	MOVW	g_m(g), Rq
    297 	MOVW	m_divmod(Rq), Rq	/* denominator */
    298 	CMP 	$0, Rq
    299 	RSB.LT	$0, Rq, Rq
    300 	CMP 	$0, Rr
    301 	BGE 	m1
    302 	RSB 	$0, Rr, Rr
    303 	BL  	udiv<>(SB)  		/* neg numerator */
    304 	RSB 	$0, Rr, RTMP
    305 	B   	out
    306 m1:
    307 	BL  	udiv<>(SB)  		/* pos numerator */
    308 	MOVW	Rr, RTMP
    309 out:
    310 	MOVW	4(R13), Rq
    311 	MOVW	8(R13), Rr
    312 	MOVW	12(R13), Rs
    313 	MOVW	16(R13), RM
    314 	RET
    315 
    316 // _mul64by32 and _div64by32 not implemented on arm
    317 TEXT runtime_mul64by32(SB), NOSPLIT, $0
    318 	MOVW	$0, R0
    319 	MOVW	(R0), R1 // crash
    320 
    321 TEXT runtime_div64by32(SB), NOSPLIT, $0
    322 	MOVW	$0, R0
    323 	MOVW	(R0), R1 // crash
    324