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